mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-09-15 14:01:31 +00:00
Compare commits
11 Commits
fdaf3af3af
...
81a91da743
Author | SHA1 | Date | |
---|---|---|---|
|
81a91da743 | ||
|
70a61ee1eb | ||
|
9d89a4413b | ||
|
6ea17d54c6 | ||
|
11a828b073 | ||
|
37022fb11e | ||
|
dd50d4927b | ||
|
829b4e7134 | ||
|
77870b39cc | ||
|
8e0ae9b867 | ||
|
543f1df5ce |
13
package.json
13
package.json
@@ -15,11 +15,12 @@
|
|||||||
"@headlessui/react": "^2.2.0",
|
"@headlessui/react": "^2.2.0",
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"@icons-pack/react-simple-icons": "^12.3.0",
|
"@icons-pack/react-simple-icons": "^12.3.0",
|
||||||
"@langchain/anthropic": "^0.3.15",
|
"@langchain/anthropic": "^0.3.24",
|
||||||
"@langchain/community": "^0.3.36",
|
"@langchain/community": "^0.3.49",
|
||||||
"@langchain/core": "^0.3.42",
|
"@langchain/core": "^0.3.66",
|
||||||
"@langchain/google-genai": "^0.1.12",
|
"@langchain/google-genai": "^0.2.15",
|
||||||
"@langchain/openai": "^0.0.25",
|
"@langchain/ollama": "^0.2.3",
|
||||||
|
"@langchain/openai": "^0.6.2",
|
||||||
"@langchain/textsplitters": "^0.1.0",
|
"@langchain/textsplitters": "^0.1.0",
|
||||||
"@tailwindcss/typography": "^0.5.12",
|
"@tailwindcss/typography": "^0.5.12",
|
||||||
"@xenova/transformers": "^2.17.2",
|
"@xenova/transformers": "^2.17.2",
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
"drizzle-orm": "^0.40.1",
|
"drizzle-orm": "^0.40.1",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"jspdf": "^3.0.1",
|
"jspdf": "^3.0.1",
|
||||||
"langchain": "^0.1.30",
|
"langchain": "^0.3.30",
|
||||||
"lucide-react": "^0.363.0",
|
"lucide-react": "^0.363.0",
|
||||||
"mammoth": "^1.9.1",
|
"mammoth": "^1.9.1",
|
||||||
"markdown-to-jsx": "^7.7.2",
|
"markdown-to-jsx": "^7.7.2",
|
||||||
|
@@ -223,7 +223,7 @@ export const POST = async (req: Request) => {
|
|||||||
|
|
||||||
if (body.chatModel?.provider === 'custom_openai') {
|
if (body.chatModel?.provider === 'custom_openai') {
|
||||||
llm = new ChatOpenAI({
|
llm = new ChatOpenAI({
|
||||||
openAIApiKey: getCustomOpenaiApiKey(),
|
apiKey: getCustomOpenaiApiKey(),
|
||||||
modelName: getCustomOpenaiModelName(),
|
modelName: getCustomOpenaiModelName(),
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
|
@@ -49,7 +49,7 @@ export const POST = async (req: Request) => {
|
|||||||
|
|
||||||
if (body.chatModel?.provider === 'custom_openai') {
|
if (body.chatModel?.provider === 'custom_openai') {
|
||||||
llm = new ChatOpenAI({
|
llm = new ChatOpenAI({
|
||||||
openAIApiKey: getCustomOpenaiApiKey(),
|
apiKey: getCustomOpenaiApiKey(),
|
||||||
modelName: getCustomOpenaiModelName(),
|
modelName: getCustomOpenaiModelName(),
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
|
@@ -81,8 +81,7 @@ export const POST = async (req: Request) => {
|
|||||||
if (body.chatModel?.provider === 'custom_openai') {
|
if (body.chatModel?.provider === 'custom_openai') {
|
||||||
llm = new ChatOpenAI({
|
llm = new ChatOpenAI({
|
||||||
modelName: body.chatModel?.name || getCustomOpenaiModelName(),
|
modelName: body.chatModel?.name || getCustomOpenaiModelName(),
|
||||||
openAIApiKey:
|
apiKey: body.chatModel?.customOpenAIKey || getCustomOpenaiApiKey(),
|
||||||
body.chatModel?.customOpenAIKey || getCustomOpenaiApiKey(),
|
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
baseURL:
|
baseURL:
|
||||||
|
@@ -48,7 +48,7 @@ export const POST = async (req: Request) => {
|
|||||||
|
|
||||||
if (body.chatModel?.provider === 'custom_openai') {
|
if (body.chatModel?.provider === 'custom_openai') {
|
||||||
llm = new ChatOpenAI({
|
llm = new ChatOpenAI({
|
||||||
openAIApiKey: getCustomOpenaiApiKey(),
|
apiKey: getCustomOpenaiApiKey(),
|
||||||
modelName: getCustomOpenaiModelName(),
|
modelName: getCustomOpenaiModelName(),
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
|
@@ -49,7 +49,7 @@ export const POST = async (req: Request) => {
|
|||||||
|
|
||||||
if (body.chatModel?.provider === 'custom_openai') {
|
if (body.chatModel?.provider === 'custom_openai') {
|
||||||
llm = new ChatOpenAI({
|
llm = new ChatOpenAI({
|
||||||
openAIApiKey: getCustomOpenaiApiKey(),
|
apiKey: getCustomOpenaiApiKey(),
|
||||||
modelName: getCustomOpenaiModelName(),
|
modelName: getCustomOpenaiModelName(),
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
|
@@ -354,7 +354,11 @@ const ChatWindow = ({ id }: { id?: string }) => {
|
|||||||
}
|
}
|
||||||
}, [isMessagesLoaded, isConfigReady]);
|
}, [isMessagesLoaded, isConfigReady]);
|
||||||
|
|
||||||
const sendMessage = async (message: string, messageId?: string) => {
|
const sendMessage = async (
|
||||||
|
message: string,
|
||||||
|
messageId?: string,
|
||||||
|
rewrite = false,
|
||||||
|
) => {
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
if (!isConfigReady) {
|
if (!isConfigReady) {
|
||||||
toast.error('Cannot send message before the configuration is ready');
|
toast.error('Cannot send message before the configuration is ready');
|
||||||
@@ -482,6 +486,8 @@ const ChatWindow = ({ id }: { id?: string }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const messageIndex = messages.findIndex((m) => m.messageId === messageId);
|
||||||
|
|
||||||
const res = await fetch('/api/chat', {
|
const res = await fetch('/api/chat', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -498,7 +504,9 @@ const ChatWindow = ({ id }: { id?: string }) => {
|
|||||||
files: fileIds,
|
files: fileIds,
|
||||||
focusMode: focusMode,
|
focusMode: focusMode,
|
||||||
optimizationMode: optimizationMode,
|
optimizationMode: optimizationMode,
|
||||||
history: chatHistory,
|
history: rewrite
|
||||||
|
? chatHistory.slice(0, messageIndex === -1 ? undefined : messageIndex)
|
||||||
|
: chatHistory,
|
||||||
chatModel: {
|
chatModel: {
|
||||||
name: chatModelProvider.name,
|
name: chatModelProvider.name,
|
||||||
provider: chatModelProvider.provider,
|
provider: chatModelProvider.provider,
|
||||||
@@ -552,7 +560,7 @@ const ChatWindow = ({ id }: { id?: string }) => {
|
|||||||
return [...prev.slice(0, messages.length > 2 ? index - 1 : 0)];
|
return [...prev.slice(0, messages.length > 2 ? index - 1 : 0)];
|
||||||
});
|
});
|
||||||
|
|
||||||
sendMessage(message.content, message.messageId);
|
sendMessage(message.content, message.messageId, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@@ -21,8 +21,16 @@ import SearchVideos from './SearchVideos';
|
|||||||
import { useSpeech } from 'react-text-to-speech';
|
import { useSpeech } from 'react-text-to-speech';
|
||||||
import ThinkBox from './ThinkBox';
|
import ThinkBox from './ThinkBox';
|
||||||
|
|
||||||
const ThinkTagProcessor = ({ children }: { children: React.ReactNode }) => {
|
const ThinkTagProcessor = ({
|
||||||
return <ThinkBox content={children as string} />;
|
children,
|
||||||
|
thinkingEnded,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
thinkingEnded: boolean;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ThinkBox content={children as string} thinkingEnded={thinkingEnded} />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const MessageBox = ({
|
const MessageBox = ({
|
||||||
@@ -46,6 +54,7 @@ const MessageBox = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [parsedMessage, setParsedMessage] = useState(message.content);
|
const [parsedMessage, setParsedMessage] = useState(message.content);
|
||||||
const [speechMessage, setSpeechMessage] = useState(message.content);
|
const [speechMessage, setSpeechMessage] = useState(message.content);
|
||||||
|
const [thinkingEnded, setThinkingEnded] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const citationRegex = /\[([^\]]+)\]/g;
|
const citationRegex = /\[([^\]]+)\]/g;
|
||||||
@@ -61,6 +70,10 @@ const MessageBox = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.role === 'assistant' && message.content.includes('</think>')) {
|
||||||
|
setThinkingEnded(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
message.role === 'assistant' &&
|
message.role === 'assistant' &&
|
||||||
message?.sources &&
|
message?.sources &&
|
||||||
@@ -88,7 +101,7 @@ const MessageBox = ({
|
|||||||
if (url) {
|
if (url) {
|
||||||
return `<a href="${url}" target="_blank" className="bg-light-secondary dark:bg-dark-secondary px-1 rounded ml-1 no-underline text-xs text-black/70 dark:text-white/70 relative">${numStr}</a>`;
|
return `<a href="${url}" target="_blank" className="bg-light-secondary dark:bg-dark-secondary px-1 rounded ml-1 no-underline text-xs text-black/70 dark:text-white/70 relative">${numStr}</a>`;
|
||||||
} else {
|
} else {
|
||||||
return `[${numStr}]`;
|
return ``;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
@@ -99,6 +112,14 @@ const MessageBox = ({
|
|||||||
);
|
);
|
||||||
setSpeechMessage(message.content.replace(regex, ''));
|
setSpeechMessage(message.content.replace(regex, ''));
|
||||||
return;
|
return;
|
||||||
|
} else if (
|
||||||
|
message.role === 'assistant' &&
|
||||||
|
message?.sources &&
|
||||||
|
message.sources.length === 0
|
||||||
|
) {
|
||||||
|
setParsedMessage(processedMessage.replace(regex, ''));
|
||||||
|
setSpeechMessage(message.content.replace(regex, ''));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSpeechMessage(message.content.replace(regex, ''));
|
setSpeechMessage(message.content.replace(regex, ''));
|
||||||
@@ -111,6 +132,9 @@ const MessageBox = ({
|
|||||||
overrides: {
|
overrides: {
|
||||||
think: {
|
think: {
|
||||||
component: ThinkTagProcessor,
|
component: ThinkTagProcessor,
|
||||||
|
props: {
|
||||||
|
thinkingEnded: thinkingEnded,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -1,15 +1,23 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
import { ChevronDown, ChevronUp, BrainCircuit } from 'lucide-react';
|
import { ChevronDown, ChevronUp, BrainCircuit } from 'lucide-react';
|
||||||
|
|
||||||
interface ThinkBoxProps {
|
interface ThinkBoxProps {
|
||||||
content: string;
|
content: string;
|
||||||
|
thinkingEnded: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ThinkBox = ({ content }: ThinkBoxProps) => {
|
const ThinkBox = ({ content, thinkingEnded }: ThinkBoxProps) => {
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (thinkingEnded) {
|
||||||
|
setIsExpanded(false);
|
||||||
|
} else {
|
||||||
|
setIsExpanded(true);
|
||||||
|
}
|
||||||
|
}, [thinkingEnded]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-4 bg-light-secondary/50 dark:bg-dark-secondary/50 rounded-xl border border-light-200 dark:border-dark-200 overflow-hidden">
|
<div className="my-4 bg-light-secondary/50 dark:bg-dark-secondary/50 rounded-xl border border-light-200 dark:border-dark-200 overflow-hidden">
|
||||||
|
@@ -38,7 +38,7 @@ export const loadAimlApiChatModels = async () => {
|
|||||||
chatModels[model.id] = {
|
chatModels[model.id] = {
|
||||||
displayName: model.name || model.id,
|
displayName: model.name || model.id,
|
||||||
model: new ChatOpenAI({
|
model: new ChatOpenAI({
|
||||||
openAIApiKey: apiKey,
|
apiKey: apiKey,
|
||||||
modelName: model.id,
|
modelName: model.id,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
@@ -76,7 +76,7 @@ export const loadAimlApiEmbeddingModels = async () => {
|
|||||||
embeddingModels[model.id] = {
|
embeddingModels[model.id] = {
|
||||||
displayName: model.name || model.id,
|
displayName: model.name || model.id,
|
||||||
model: new OpenAIEmbeddings({
|
model: new OpenAIEmbeddings({
|
||||||
openAIApiKey: apiKey,
|
apiKey: apiKey,
|
||||||
modelName: model.id,
|
modelName: model.id,
|
||||||
configuration: {
|
configuration: {
|
||||||
baseURL: API_URL,
|
baseURL: API_URL,
|
||||||
|
@@ -31,7 +31,7 @@ export const loadDeepseekChatModels = async () => {
|
|||||||
chatModels[model.key] = {
|
chatModels[model.key] = {
|
||||||
displayName: model.displayName,
|
displayName: model.displayName,
|
||||||
model: new ChatOpenAI({
|
model: new ChatOpenAI({
|
||||||
openAIApiKey: deepseekApiKey,
|
apiKey: deepseekApiKey,
|
||||||
modelName: model.key,
|
modelName: model.key,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
|
@@ -75,7 +75,7 @@ export const loadGeminiChatModels = async () => {
|
|||||||
displayName: model.displayName,
|
displayName: model.displayName,
|
||||||
model: new ChatGoogleGenerativeAI({
|
model: new ChatGoogleGenerativeAI({
|
||||||
apiKey: geminiApiKey,
|
apiKey: geminiApiKey,
|
||||||
modelName: model.key,
|
model: model.key,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
}) as unknown as BaseChatModel,
|
}) as unknown as BaseChatModel,
|
||||||
};
|
};
|
||||||
|
@@ -29,12 +29,15 @@ export const loadGroqChatModels = async () => {
|
|||||||
chatModels[model.id] = {
|
chatModels[model.id] = {
|
||||||
displayName: model.id,
|
displayName: model.id,
|
||||||
model: new ChatOpenAI({
|
model: new ChatOpenAI({
|
||||||
openAIApiKey: groqApiKey,
|
apiKey: groqApiKey,
|
||||||
modelName: model.id,
|
modelName: model.id,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
baseURL: 'https://api.groq.com/openai/v1',
|
baseURL: 'https://api.groq.com/openai/v1',
|
||||||
},
|
},
|
||||||
|
metadata: {
|
||||||
|
'model-type': 'groq',
|
||||||
|
},
|
||||||
}) as unknown as BaseChatModel,
|
}) as unknown as BaseChatModel,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@@ -118,7 +118,7 @@ export const getAvailableChatModelProviders = async () => {
|
|||||||
[customOpenAiModelName]: {
|
[customOpenAiModelName]: {
|
||||||
displayName: customOpenAiModelName,
|
displayName: customOpenAiModelName,
|
||||||
model: new ChatOpenAI({
|
model: new ChatOpenAI({
|
||||||
openAIApiKey: customOpenAiApiKey,
|
apiKey: customOpenAiApiKey,
|
||||||
modelName: customOpenAiModelName,
|
modelName: customOpenAiModelName,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
configuration: {
|
configuration: {
|
||||||
|
@@ -47,7 +47,7 @@ export const loadLMStudioChatModels = async () => {
|
|||||||
chatModels[model.id] = {
|
chatModels[model.id] = {
|
||||||
displayName: model.name || model.id,
|
displayName: model.name || model.id,
|
||||||
model: new ChatOpenAI({
|
model: new ChatOpenAI({
|
||||||
openAIApiKey: 'lm-studio',
|
apiKey: 'lm-studio',
|
||||||
configuration: {
|
configuration: {
|
||||||
baseURL: ensureV1Endpoint(endpoint),
|
baseURL: ensureV1Endpoint(endpoint),
|
||||||
},
|
},
|
||||||
@@ -83,7 +83,7 @@ export const loadLMStudioEmbeddingsModels = async () => {
|
|||||||
embeddingsModels[model.id] = {
|
embeddingsModels[model.id] = {
|
||||||
displayName: model.name || model.id,
|
displayName: model.name || model.id,
|
||||||
model: new OpenAIEmbeddings({
|
model: new OpenAIEmbeddings({
|
||||||
openAIApiKey: 'lm-studio',
|
apiKey: 'lm-studio',
|
||||||
configuration: {
|
configuration: {
|
||||||
baseURL: ensureV1Endpoint(endpoint),
|
baseURL: ensureV1Endpoint(endpoint),
|
||||||
},
|
},
|
||||||
|
@@ -6,8 +6,8 @@ export const PROVIDER_INFO = {
|
|||||||
key: 'ollama',
|
key: 'ollama',
|
||||||
displayName: 'Ollama',
|
displayName: 'Ollama',
|
||||||
};
|
};
|
||||||
import { ChatOllama } from '@langchain/community/chat_models/ollama';
|
import { ChatOllama } from '@langchain/ollama';
|
||||||
import { OllamaEmbeddings } from '@langchain/community/embeddings/ollama';
|
import { OllamaEmbeddings } from '@langchain/ollama';
|
||||||
|
|
||||||
export const loadOllamaChatModels = async () => {
|
export const loadOllamaChatModels = async () => {
|
||||||
const ollamaApiEndpoint = getOllamaApiEndpoint();
|
const ollamaApiEndpoint = getOllamaApiEndpoint();
|
||||||
|
@@ -67,7 +67,7 @@ export const loadOpenAIChatModels = async () => {
|
|||||||
chatModels[model.key] = {
|
chatModels[model.key] = {
|
||||||
displayName: model.displayName,
|
displayName: model.displayName,
|
||||||
model: new ChatOpenAI({
|
model: new ChatOpenAI({
|
||||||
openAIApiKey: openaiApiKey,
|
apiKey: openaiApiKey,
|
||||||
modelName: model.key,
|
modelName: model.key,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
}) as unknown as BaseChatModel,
|
}) as unknown as BaseChatModel,
|
||||||
@@ -93,7 +93,7 @@ export const loadOpenAIEmbeddingModels = async () => {
|
|||||||
embeddingModels[model.key] = {
|
embeddingModels[model.key] = {
|
||||||
displayName: model.displayName,
|
displayName: model.displayName,
|
||||||
model: new OpenAIEmbeddings({
|
model: new OpenAIEmbeddings({
|
||||||
openAIApiKey: openaiApiKey,
|
apiKey: openaiApiKey,
|
||||||
modelName: model.key,
|
modelName: model.key,
|
||||||
}) as unknown as Embeddings,
|
}) as unknown as Embeddings,
|
||||||
};
|
};
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
import { BaseMessage } from '@langchain/core/messages';
|
import { BaseMessage, isAIMessage } from '@langchain/core/messages';
|
||||||
|
|
||||||
const formatChatHistoryAsString = (history: BaseMessage[]) => {
|
const formatChatHistoryAsString = (history: BaseMessage[]) => {
|
||||||
return history
|
return history
|
||||||
.map((message) => `${message._getType()}: ${message.content}`)
|
.map(
|
||||||
|
(message) =>
|
||||||
|
`${isAIMessage(message) ? 'AI' : 'User'}: ${message.content}`,
|
||||||
|
)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user