mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2026-04-08 21:04:26 +00:00
Compare commits
10 Commits
b02f5aa37f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3098622cb0 | ||
|
|
3646495bdf | ||
|
|
476c4ec8c2 | ||
|
|
0e33641927 | ||
|
|
8c061f20a5 | ||
|
|
72ac815294 | ||
|
|
d16b7e271a | ||
|
|
58ed869b3d | ||
|
|
3fede054da | ||
|
|
21bd88787e |
@@ -37,7 +37,8 @@ const getStepTitle = (
|
||||
if (step.type === 'reasoning') {
|
||||
return isStreaming && !step.reasoning ? 'Thinking...' : 'Thinking';
|
||||
} else if (step.type === 'searching') {
|
||||
return `Searching ${step.searching.length} ${step.searching.length === 1 ? 'query' : 'queries'}`;
|
||||
const queries = Array.isArray(step.searching) ? step.searching : [];
|
||||
return `Searching ${queries.length} ${queries.length === 1 ? 'query' : 'queries'}`;
|
||||
} else if (step.type === 'search_results') {
|
||||
return `Found ${step.reading.length} ${step.reading.length === 1 ? 'result' : 'results'}`;
|
||||
} else if (step.type === 'reading') {
|
||||
@@ -160,6 +161,7 @@ const AssistantSteps = ({
|
||||
)}
|
||||
|
||||
{step.type === 'searching' &&
|
||||
Array.isArray(step.searching) &&
|
||||
step.searching.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5 mt-1.5">
|
||||
{step.searching.map((query, idx) => (
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Fragment, useRef, useState } from 'react';
|
||||
import { useChat } from '@/lib/hooks/useChat';
|
||||
import { AnimatePresence } from 'motion/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const Attach = () => {
|
||||
const { files, setFiles, setFileIds, fileIds } = useChat();
|
||||
@@ -26,31 +27,59 @@ const Attach = () => {
|
||||
const fileInputRef = useRef<any>();
|
||||
|
||||
const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setLoading(true);
|
||||
const data = new FormData();
|
||||
const selectedFiles = e.target.files;
|
||||
|
||||
for (let i = 0; i < e.target.files!.length; i++) {
|
||||
data.append('files', e.target.files![i]);
|
||||
if (!selectedFiles?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const embeddingModelProvider = localStorage.getItem(
|
||||
'embeddingModelProviderId',
|
||||
);
|
||||
const embeddingModel = localStorage.getItem('embeddingModelKey');
|
||||
setLoading(true);
|
||||
|
||||
data.append('embedding_model_provider_id', embeddingModelProvider!);
|
||||
data.append('embedding_model_key', embeddingModel!);
|
||||
try {
|
||||
const data = new FormData();
|
||||
|
||||
const res = await fetch(`/api/uploads`, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
for (let i = 0; i < selectedFiles.length; i++) {
|
||||
data.append('files', selectedFiles[i]);
|
||||
}
|
||||
|
||||
const resData = await res.json();
|
||||
const embeddingModelProvider = localStorage.getItem(
|
||||
'embeddingModelProviderId',
|
||||
);
|
||||
const embeddingModel = localStorage.getItem('embeddingModelKey');
|
||||
|
||||
setFiles([...files, ...resData.files]);
|
||||
setFileIds([...fileIds, ...resData.files.map((file: any) => file.fileId)]);
|
||||
setLoading(false);
|
||||
if (!embeddingModelProvider || !embeddingModel) {
|
||||
throw new Error('Please select an embedding model before uploading.');
|
||||
}
|
||||
|
||||
data.append('embedding_model_provider_id', embeddingModelProvider);
|
||||
data.append('embedding_model_key', embeddingModel);
|
||||
|
||||
const res = await fetch(`/api/uploads`, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
|
||||
const resData = await res.json().catch(() => ({}));
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(resData.message || 'Failed to upload file(s).');
|
||||
}
|
||||
|
||||
if (!Array.isArray(resData.files)) {
|
||||
throw new Error('Invalid upload response from server.');
|
||||
}
|
||||
|
||||
setFiles([...files, ...resData.files]);
|
||||
setFileIds([
|
||||
...fileIds,
|
||||
...resData.files.map((file: any) => file.fileId),
|
||||
]);
|
||||
} catch (err: any) {
|
||||
toast(err?.message || 'Failed to upload file(s).');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
e.target.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
return loading ? (
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Fragment, useRef, useState } from 'react';
|
||||
import { useChat } from '@/lib/hooks/useChat';
|
||||
import { AnimatePresence } from 'motion/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const AttachSmall = () => {
|
||||
const { files, setFiles, setFileIds, fileIds } = useChat();
|
||||
@@ -17,31 +18,59 @@ const AttachSmall = () => {
|
||||
const fileInputRef = useRef<any>();
|
||||
|
||||
const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setLoading(true);
|
||||
const data = new FormData();
|
||||
const selectedFiles = e.target.files;
|
||||
|
||||
for (let i = 0; i < e.target.files!.length; i++) {
|
||||
data.append('files', e.target.files![i]);
|
||||
if (!selectedFiles?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const embeddingModelProvider = localStorage.getItem(
|
||||
'embeddingModelProviderId',
|
||||
);
|
||||
const embeddingModel = localStorage.getItem('embeddingModelKey');
|
||||
setLoading(true);
|
||||
|
||||
data.append('embedding_model_provider_id', embeddingModelProvider!);
|
||||
data.append('embedding_model_key', embeddingModel!);
|
||||
try {
|
||||
const data = new FormData();
|
||||
|
||||
const res = await fetch(`/api/uploads`, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
for (let i = 0; i < selectedFiles.length; i++) {
|
||||
data.append('files', selectedFiles[i]);
|
||||
}
|
||||
|
||||
const resData = await res.json();
|
||||
const embeddingModelProvider = localStorage.getItem(
|
||||
'embeddingModelProviderId',
|
||||
);
|
||||
const embeddingModel = localStorage.getItem('embeddingModelKey');
|
||||
|
||||
setFiles([...files, ...resData.files]);
|
||||
setFileIds([...fileIds, ...resData.files.map((file: any) => file.fileId)]);
|
||||
setLoading(false);
|
||||
if (!embeddingModelProvider || !embeddingModel) {
|
||||
throw new Error('Please select an embedding model before uploading.');
|
||||
}
|
||||
|
||||
data.append('embedding_model_provider_id', embeddingModelProvider);
|
||||
data.append('embedding_model_key', embeddingModel);
|
||||
|
||||
const res = await fetch(`/api/uploads`, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
|
||||
const resData = await res.json().catch(() => ({}));
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(resData.message || 'Failed to upload file(s).');
|
||||
}
|
||||
|
||||
if (!Array.isArray(resData.files)) {
|
||||
throw new Error('Invalid upload response from server.');
|
||||
}
|
||||
|
||||
setFiles([...files, ...resData.files]);
|
||||
setFileIds([
|
||||
...fileIds,
|
||||
...resData.files.map((file: any) => file.fileId),
|
||||
]);
|
||||
} catch (err: any) {
|
||||
toast(err?.message || 'Failed to upload file(s).');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
e.target.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
return loading ? (
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Cloud, Sun, CloudRain, CloudSnow, Wind } from 'lucide-react';
|
||||
'use client';
|
||||
|
||||
import { Wind } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getApproxLocation } from '@/lib/actions';
|
||||
|
||||
const WeatherWidget = () => {
|
||||
const [data, setData] = useState({
|
||||
@@ -15,17 +18,6 @@ const WeatherWidget = () => {
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const getApproxLocation = async () => {
|
||||
const res = await fetch('https://ipwhois.app/json/');
|
||||
const data = await res.json();
|
||||
|
||||
return {
|
||||
latitude: data.latitude,
|
||||
longitude: data.longitude,
|
||||
city: data.city,
|
||||
};
|
||||
};
|
||||
|
||||
const getLocation = async (
|
||||
callback: (location: {
|
||||
latitude: number;
|
||||
|
||||
@@ -20,3 +20,17 @@ export const getSuggestions = async (chatHistory: [string, string][]) => {
|
||||
|
||||
return data.suggestions;
|
||||
};
|
||||
|
||||
export const getApproxLocation = async () => {
|
||||
const res = await fetch('https://free.freeipapi.com/api/json', {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
return {
|
||||
latitude: data.latitude,
|
||||
longitude: data.longitude,
|
||||
city: data.cityName,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -30,7 +30,9 @@ const academicSearchAction: ResearchAction<typeof schema> = {
|
||||
config.classification.classification.skipSearch === false &&
|
||||
config.classification.classification.academicSearch === true,
|
||||
execute: async (input, additionalConfig) => {
|
||||
input.queries = input.queries.slice(0, 3);
|
||||
input.queries = (
|
||||
Array.isArray(input.queries) ? input.queries : [input.queries]
|
||||
).slice(0, 3);
|
||||
|
||||
const researchBlock = additionalConfig.session.getBlock(
|
||||
additionalConfig.researchBlockId,
|
||||
|
||||
@@ -30,7 +30,9 @@ const socialSearchAction: ResearchAction<typeof schema> = {
|
||||
config.classification.classification.skipSearch === false &&
|
||||
config.classification.classification.discussionSearch === true,
|
||||
execute: async (input, additionalConfig) => {
|
||||
input.queries = input.queries.slice(0, 3);
|
||||
input.queries = (
|
||||
Array.isArray(input.queries) ? input.queries : [input.queries]
|
||||
).slice(0, 3);
|
||||
|
||||
const researchBlock = additionalConfig.session.getBlock(
|
||||
additionalConfig.researchBlockId,
|
||||
|
||||
@@ -85,7 +85,9 @@ const webSearchAction: ResearchAction<typeof actionSchema> = {
|
||||
config.sources.includes('web') &&
|
||||
config.classification.classification.skipSearch === false,
|
||||
execute: async (input, additionalConfig) => {
|
||||
input.queries = input.queries.slice(0, 3);
|
||||
input.queries = (
|
||||
Array.isArray(input.queries) ? input.queries : [input.queries]
|
||||
).slice(0, 3);
|
||||
|
||||
const researchBlock = additionalConfig.session.getBlock(
|
||||
additionalConfig.researchBlockId,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'fs';
|
||||
import { Config, ConfigModelProvider, UIConfigSections } from './types';
|
||||
import { hashObj } from '../serverUtils';
|
||||
import { hashObj } from '../utils/hash';
|
||||
import { getModelProvidersUIConfigSection } from '../models/providers';
|
||||
|
||||
class ConfigManager {
|
||||
|
||||
@@ -25,7 +25,9 @@ const reasoningModels = [
|
||||
'qwen3',
|
||||
'deepseek-v3.1',
|
||||
'magistral',
|
||||
'nemotron-3-nano',
|
||||
'nemotron-3',
|
||||
'nemotron-cascade-2',
|
||||
'glm-4.7-flash',
|
||||
];
|
||||
|
||||
class OllamaLLM extends BaseLLM<OllamaConfig> {
|
||||
|
||||
1
src/lib/serverActions.ts
Normal file
1
src/lib/serverActions.ts
Normal file
@@ -0,0 +1 @@
|
||||
'use server';
|
||||
@@ -2,8 +2,7 @@ import BaseEmbedding from "../models/base/embedding";
|
||||
import UploadManager from "./manager";
|
||||
import computeSimilarity from "../utils/computeSimilarity";
|
||||
import { Chunk } from "../types";
|
||||
import { hashObj } from "../serverUtils";
|
||||
import fs from 'fs';
|
||||
import { hashObj } from '../utils/hash';
|
||||
|
||||
type UploadStoreParams = {
|
||||
embeddingModel: BaseEmbedding<any>;
|
||||
|
||||
Reference in New Issue
Block a user