From edc40d8fe6f9381803e8eee83b6371b1303533bb Mon Sep 17 00:00:00 2001 From: ItzCrazyKns Date: Wed, 1 May 2024 19:43:06 +0530 Subject: [PATCH 001/434] feat(providers): add Groq provider --- sample.config.toml | 3 +- src/config.ts | 3 ++ src/lib/providers.ts | 66 ++++++++++++++++++++++++++++++-- src/routes/config.ts | 3 ++ ui/components/SettingsDialog.tsx | 16 ++++++++ 5 files changed, 86 insertions(+), 5 deletions(-) diff --git a/sample.config.toml b/sample.config.toml index 2d09b4b..e283826 100644 --- a/sample.config.toml +++ b/sample.config.toml @@ -1,11 +1,12 @@ [GENERAL] PORT = 3001 # Port to run the server on SIMILARITY_MEASURE = "cosine" # "cosine" or "dot" -CHAT_MODEL_PROVIDER = "openai" # "openai" or "ollama" +CHAT_MODEL_PROVIDER = "openai" # "openai" or "ollama" or "groq" CHAT_MODEL = "gpt-3.5-turbo" # Name of the model to use [API_KEYS] OPENAI = "" # OpenAI API key - sk-1234567890abcdef1234567890abcdef +GROQ = "" # Groq API key - gsk_1234567890abcdef1234567890abcdef [API_ENDPOINTS] SEARXNG = "http://localhost:32768" # SearxNG API URL diff --git a/src/config.ts b/src/config.ts index f373847..25dcbf4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -13,6 +13,7 @@ interface Config { }; API_KEYS: { OPENAI: string; + GROQ: string; }; API_ENDPOINTS: { SEARXNG: string; @@ -41,6 +42,8 @@ export const getChatModel = () => loadConfig().GENERAL.CHAT_MODEL; export const getOpenaiApiKey = () => loadConfig().API_KEYS.OPENAI; +export const getGroqApiKey = () => loadConfig().API_KEYS.GROQ; + export const getSearxngApiEndpoint = () => loadConfig().API_ENDPOINTS.SEARXNG; export const getOllamaApiEndpoint = () => loadConfig().API_ENDPOINTS.OLLAMA; diff --git a/src/lib/providers.ts b/src/lib/providers.ts index 71ed079..aea69de 100644 --- a/src/lib/providers.ts +++ b/src/lib/providers.ts @@ -1,11 +1,16 @@ import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai'; import { ChatOllama } from '@langchain/community/chat_models/ollama'; import { OllamaEmbeddings } from '@langchain/community/embeddings/ollama'; -import { getOllamaApiEndpoint, getOpenaiApiKey } from '../config'; +import { + getGroqApiKey, + getOllamaApiEndpoint, + getOpenaiApiKey, +} from '../config'; import logger from '../utils/logger'; export const getAvailableProviders = async () => { const openAIApiKey = getOpenaiApiKey(); + const groqApiKey = getGroqApiKey(); const ollamaEndpoint = getOllamaApiEndpoint(); const models = {}; @@ -13,17 +18,17 @@ export const getAvailableProviders = async () => { if (openAIApiKey) { try { models['openai'] = { - 'gpt-3.5-turbo': new ChatOpenAI({ + 'GPT-3.5 turbo': new ChatOpenAI({ openAIApiKey, modelName: 'gpt-3.5-turbo', temperature: 0.7, }), - 'gpt-4': new ChatOpenAI({ + 'GPT-4': new ChatOpenAI({ openAIApiKey, modelName: 'gpt-4', temperature: 0.7, }), - 'gpt-4-turbo': new ChatOpenAI({ + 'GPT-4 turbo': new ChatOpenAI({ openAIApiKey, modelName: 'gpt-4-turbo', temperature: 0.7, @@ -38,6 +43,59 @@ export const getAvailableProviders = async () => { } } + if (groqApiKey) { + try { + models['groq'] = { + 'LLaMA3 8b': new ChatOpenAI( + { + openAIApiKey: groqApiKey, + modelName: 'llama3-8b-8192', + temperature: 0.7, + }, + { + baseURL: 'https://api.groq.com/openai/v1', + }, + ), + 'LLaMA3 70b': new ChatOpenAI( + { + openAIApiKey: groqApiKey, + modelName: 'llama3-70b-8192', + temperature: 0.7, + }, + { + baseURL: 'https://api.groq.com/openai/v1', + }, + ), + 'Mixtral 8x7b': new ChatOpenAI( + { + openAIApiKey: groqApiKey, + modelName: 'gemma-7b-it', + temperature: 0.7, + }, + { + baseURL: 'https://api.groq.com/openai/v1', + }, + ), + 'Gemma 7b': new ChatOpenAI( + { + openAIApiKey: groqApiKey, + modelName: 'llama3-70b-8192', + temperature: 0.7, + }, + { + baseURL: 'https://api.groq.com/openai/v1', + }, + ), + embeddings: new OpenAIEmbeddings({ + openAIApiKey: openAIApiKey, + modelName: 'text-embedding-3-large', + }), + }; + } catch (err) { + logger.error(`Error loading Groq models: ${err}`); + } + } + if (ollamaEndpoint) { try { const response = await fetch(`${ollamaEndpoint}/api/tags`); diff --git a/src/routes/config.ts b/src/routes/config.ts index ecdec17..4d22ec5 100644 --- a/src/routes/config.ts +++ b/src/routes/config.ts @@ -3,6 +3,7 @@ import { getAvailableProviders } from '../lib/providers'; import { getChatModel, getChatModelProvider, + getGroqApiKey, getOllamaApiEndpoint, getOpenaiApiKey, updateConfig, @@ -30,6 +31,7 @@ router.get('/', async (_, res) => { config['openeaiApiKey'] = getOpenaiApiKey(); config['ollamaApiUrl'] = getOllamaApiEndpoint(); + config['groqApiKey'] = getGroqApiKey(); res.status(200).json(config); }); @@ -44,6 +46,7 @@ router.post('/', async (req, res) => { }, API_KEYS: { OPENAI: config.openeaiApiKey, + GROQ: config.groqApiKey, }, API_ENDPOINTS: { OLLAMA: config.ollamaApiUrl, diff --git a/ui/components/SettingsDialog.tsx b/ui/components/SettingsDialog.tsx index c62c967..f005b8c 100644 --- a/ui/components/SettingsDialog.tsx +++ b/ui/components/SettingsDialog.tsx @@ -9,6 +9,7 @@ interface SettingsType { selectedProvider: string; selectedChatModel: string; openeaiApiKey: string; + groqApiKey: string; ollamaApiUrl: string; } @@ -194,6 +195,21 @@ const SettingsDialog = ({ className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" /> +
+

GROQ API Key

+ + setConfig({ + ...config, + groqApiKey: e.target.value, + }) + } + className="bg-[#111111] px-3 py-2 flex items-center overflow-hidden border border-[#1C1C1C] text-white rounded-lg text-sm" + /> +
)} {isLoading && ( From f21f5c9611ed85958cdd487ab3c94c5919b32f3d Mon Sep 17 00:00:00 2001 From: ItzCrazyKns Date: Wed, 1 May 2024 20:12:58 +0530 Subject: [PATCH 002/434] feat(readme): correct spellings, closes #32 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38743e6..50e0e1d 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Using SearxNG to stay current and fully open source, Perplexica ensures you alwa - **YouTube Search Mode:** Finds YouTube videos based on the search query. - **Wolfram Alpha Search Mode:** Answers queries that need calculations or data analysis using Wolfram Alpha. - **Reddit Search Mode:** Searches Reddit for discussions and opinions related to the query. -- **Current Information:** Some search tools might give you outdated info because they use data from crawling bots and convert them into embeddings and store them in a index. Unlike them, Perplexica uses SearxNG, a metasearch engine to get the results and rerank and get the most relevent source out of it, ensuring you always get the latest information without the overhead of daily data updates. +- **Current Information:** Some search tools might give you outdated info because they use data from crawling bots and convert them into embeddings and store them in a index. Unlike them, Perplexica uses SearxNG, a metasearch engine to get the results and rerank and get the most relevant source out of it, ensuring you always get the latest information without the overhead of daily data updates. It has many more features like image and video search. Some of the planned features are mentioned in [upcoming features](#upcoming-features). From ed9ff3c20fd8218fff139cf9a13c82555b5d3d28 Mon Sep 17 00:00:00 2001 From: ItzCrazyKns Date: Thu, 2 May 2024 12:09:25 +0530 Subject: [PATCH 003/434] feat(providers): use correct model name --- src/lib/providers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/providers.ts b/src/lib/providers.ts index aea69de..9b62ce0 100644 --- a/src/lib/providers.ts +++ b/src/lib/providers.ts @@ -69,7 +69,7 @@ export const getAvailableProviders = async () => { 'Mixtral 8x7b': new ChatOpenAI( { openAIApiKey: groqApiKey, - modelName: 'gemma-7b-it', + modelName: 'mixtral-8x7b-32768', temperature: 0.7, }, { @@ -79,7 +79,7 @@ export const getAvailableProviders = async () => { 'Gemma 7b': new ChatOpenAI( { openAIApiKey: groqApiKey, - modelName: 'llama3-70b-8192', + modelName: 'gemma-7b-it', temperature: 0.7, }, { From f618b713afdfa2a1596ea8c258a47e6e70becb1a Mon Sep 17 00:00:00 2001 From: ItzCrazyKns Date: Thu, 2 May 2024 12:14:26 +0530 Subject: [PATCH 004/434] feat(chatModels): load model from localstorage --- README.md | 10 +++--- package.json | 2 +- sample.config.toml | 2 -- src/config.ts | 17 +++------- src/routes/config.ts | 9 ------ src/routes/images.ts | 13 ++++---- src/routes/index.ts | 2 ++ src/routes/models.ts | 18 +++++++++++ src/routes/videos.ts | 13 ++++---- src/websocket/connectionManager.ts | 16 +++++++--- src/websocket/websocketServer.ts | 4 +-- ui/components/ChatWindow.tsx | 36 ++++++++++++++++++--- ui/components/SearchImages.tsx | 6 ++++ ui/components/SearchVideos.tsx | 6 ++++ ui/components/SettingsDialog.tsx | 51 ++++++++++++++++-------------- ui/package.json | 2 +- 16 files changed, 126 insertions(+), 81 deletions(-) create mode 100644 src/routes/models.ts diff --git a/README.md b/README.md index 50e0e1d..bb7171b 100644 --- a/README.md +++ b/README.md @@ -59,13 +59,11 @@ There are mainly 2 ways of installing Perplexica - With Docker, Without Docker. 4. Rename the `sample.config.toml` file to `config.toml`. For Docker setups, you need only fill in the following fields: - - `CHAT_MODEL`: The name of the LLM to use. Like `llama3:latest` (using Ollama), `gpt-3.5-turbo` (using OpenAI), etc. - - `CHAT_MODEL_PROVIDER`: The chat model provider, either `openai` or `ollama`. Depending upon which provider you use you would have to fill in the following fields: + - `OPENAI`: Your OpenAI API key. **You only need to fill this if you wish to use OpenAI's models**. + - `OLLAMA`: Your Ollama API URL. You should enter it as `http://host.docker.internal:PORT_NUMBER`. If you installed Ollama on port 11434, use `http://host.docker.internal:11434`. For other ports, adjust accordingly. **You need to fill this if you wish to use Ollama's models instead of OpenAI's**. + - `GROQ`: Your Groq API key. **You only need to fill this if you wish to use Groq's hosted models** - - `OPENAI`: Your OpenAI API key. **You only need to fill this if you wish to use OpenAI's models**. - - `OLLAMA`: Your Ollama API URL. You should enter it as `http://host.docker.internal:PORT_NUMBER`. If you installed Ollama on port 11434, use `http://host.docker.internal:11434`. For other ports, adjust accordingly. **You need to fill this if you wish to use Ollama's models instead of OpenAI's**. - - **Note**: You can change these and use different models after running Perplexica as well from the settings page. + **Note**: You can change these after starting Perplexica from the settings dialog. - `SIMILARITY_MEASURE`: The similarity measure to use (This is filled by default; you can leave it as is if you are unsure about it.) diff --git a/package.json b/package.json index a4b91e1..9434569 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "perplexica-backend", - "version": "1.0.0", + "version": "1.1.0", "license": "MIT", "author": "ItzCrazyKns", "scripts": { diff --git a/sample.config.toml b/sample.config.toml index e283826..7bc8880 100644 --- a/sample.config.toml +++ b/sample.config.toml @@ -1,8 +1,6 @@ [GENERAL] PORT = 3001 # Port to run the server on SIMILARITY_MEASURE = "cosine" # "cosine" or "dot" -CHAT_MODEL_PROVIDER = "openai" # "openai" or "ollama" or "groq" -CHAT_MODEL = "gpt-3.5-turbo" # Name of the model to use [API_KEYS] OPENAI = "" # OpenAI API key - sk-1234567890abcdef1234567890abcdef diff --git a/src/config.ts b/src/config.ts index 25dcbf4..7c0c7f1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -8,8 +8,6 @@ interface Config { GENERAL: { PORT: number; SIMILARITY_MEASURE: string; - CHAT_MODEL_PROVIDER: string; - CHAT_MODEL: string; }; API_KEYS: { OPENAI: string; @@ -35,11 +33,6 @@ export const getPort = () => loadConfig().GENERAL.PORT; export const getSimilarityMeasure = () => loadConfig().GENERAL.SIMILARITY_MEASURE; -export const getChatModelProvider = () => - loadConfig().GENERAL.CHAT_MODEL_PROVIDER; - -export const getChatModel = () => loadConfig().GENERAL.CHAT_MODEL; - export const getOpenaiApiKey = () => loadConfig().API_KEYS.OPENAI; export const getGroqApiKey = () => loadConfig().API_KEYS.GROQ; @@ -52,21 +45,19 @@ export const updateConfig = (config: RecursivePartial) => { const currentConfig = loadConfig(); for (const key in currentConfig) { - /* if (currentConfig[key] && !config[key]) { - config[key] = currentConfig[key]; - } */ + if (!config[key]) config[key] = {}; - if (currentConfig[key] && typeof currentConfig[key] === 'object') { + if (typeof currentConfig[key] === 'object' && currentConfig[key] !== null) { for (const nestedKey in currentConfig[key]) { if ( - currentConfig[key][nestedKey] && !config[key][nestedKey] && + currentConfig[key][nestedKey] && config[key][nestedKey] !== '' ) { config[key][nestedKey] = currentConfig[key][nestedKey]; } } - } else if (currentConfig[key] && !config[key] && config[key] !== '') { + } else if (currentConfig[key] && config[key] !== '') { config[key] = currentConfig[key]; } } diff --git a/src/routes/config.ts b/src/routes/config.ts index 4d22ec5..9518c5f 100644 --- a/src/routes/config.ts +++ b/src/routes/config.ts @@ -1,8 +1,6 @@ import express from 'express'; import { getAvailableProviders } from '../lib/providers'; import { - getChatModel, - getChatModelProvider, getGroqApiKey, getOllamaApiEndpoint, getOpenaiApiKey, @@ -26,9 +24,6 @@ router.get('/', async (_, res) => { config['providers'][provider] = Object.keys(providers[provider]); } - config['selectedProvider'] = getChatModelProvider(); - config['selectedChatModel'] = getChatModel(); - config['openeaiApiKey'] = getOpenaiApiKey(); config['ollamaApiUrl'] = getOllamaApiEndpoint(); config['groqApiKey'] = getGroqApiKey(); @@ -40,10 +35,6 @@ router.post('/', async (req, res) => { const config = req.body; const updatedConfig = { - GENERAL: { - CHAT_MODEL_PROVIDER: config.selectedProvider, - CHAT_MODEL: config.selectedChatModel, - }, API_KEYS: { OPENAI: config.openeaiApiKey, GROQ: config.groqApiKey, diff --git a/src/routes/images.ts b/src/routes/images.ts index 066a3ee..3906689 100644 --- a/src/routes/images.ts +++ b/src/routes/images.ts @@ -2,7 +2,6 @@ import express from 'express'; import handleImageSearch from '../agents/imageSearchAgent'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { getAvailableProviders } from '../lib/providers'; -import { getChatModel, getChatModelProvider } from '../config'; import { HumanMessage, AIMessage } from '@langchain/core/messages'; import logger from '../utils/logger'; @@ -10,7 +9,7 @@ const router = express.Router(); router.post('/', async (req, res) => { try { - let { query, chat_history } = req.body; + let { query, chat_history, chat_model_provider, chat_model } = req.body; chat_history = chat_history.map((msg: any) => { if (msg.role === 'user') { @@ -20,14 +19,14 @@ router.post('/', async (req, res) => { } }); - const models = await getAvailableProviders(); - const provider = getChatModelProvider(); - const chatModel = getChatModel(); + const chatModels = await getAvailableProviders(); + const provider = chat_model_provider || Object.keys(chatModels)[0]; + const chatModel = chat_model || Object.keys(chatModels[provider])[0]; let llm: BaseChatModel | undefined; - if (models[provider] && models[provider][chatModel]) { - llm = models[provider][chatModel] as BaseChatModel | undefined; + if (chatModels[provider] && chatModels[provider][chatModel]) { + llm = chatModels[provider][chatModel] as BaseChatModel | undefined; } if (!llm) { diff --git a/src/routes/index.ts b/src/routes/index.ts index bcfc3d3..04390cd 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -2,11 +2,13 @@ import express from 'express'; import imagesRouter from './images'; import videosRouter from './videos'; import configRouter from './config'; +import modelsRouter from './models'; const router = express.Router(); router.use('/images', imagesRouter); router.use('/videos', videosRouter); router.use('/config', configRouter); +router.use('/models', modelsRouter); export default router; diff --git a/src/routes/models.ts b/src/routes/models.ts new file mode 100644 index 0000000..f2332f4 --- /dev/null +++ b/src/routes/models.ts @@ -0,0 +1,18 @@ +import express from 'express'; +import logger from '../utils/logger'; +import { getAvailableProviders } from '../lib/providers'; + +const router = express.Router(); + +router.get('/', async (req, res) => { + try { + const providers = await getAvailableProviders(); + + res.status(200).json({ providers }); + } catch (err) { + res.status(500).json({ message: 'An error has occurred.' }); + logger.error(err.message); + } +}); + +export default router; diff --git a/src/routes/videos.ts b/src/routes/videos.ts index bfd5fa8..fecd874 100644 --- a/src/routes/videos.ts +++ b/src/routes/videos.ts @@ -1,7 +1,6 @@ import express from 'express'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { getAvailableProviders } from '../lib/providers'; -import { getChatModel, getChatModelProvider } from '../config'; import { HumanMessage, AIMessage } from '@langchain/core/messages'; import logger from '../utils/logger'; import handleVideoSearch from '../agents/videoSearchAgent'; @@ -10,7 +9,7 @@ const router = express.Router(); router.post('/', async (req, res) => { try { - let { query, chat_history } = req.body; + let { query, chat_history, chat_model_provider, chat_model } = req.body; chat_history = chat_history.map((msg: any) => { if (msg.role === 'user') { @@ -20,14 +19,14 @@ router.post('/', async (req, res) => { } }); - const models = await getAvailableProviders(); - const provider = getChatModelProvider(); - const chatModel = getChatModel(); + const chatModels = await getAvailableProviders(); + const provider = chat_model_provider || Object.keys(chatModels)[0]; + const chatModel = chat_model || Object.keys(chatModels[provider])[0]; let llm: BaseChatModel | undefined; - if (models[provider] && models[provider][chatModel]) { - llm = models[provider][chatModel] as BaseChatModel | undefined; + if (chatModels[provider] && chatModels[provider][chatModel]) { + llm = chatModels[provider][chatModel] as BaseChatModel | undefined; } if (!llm) { diff --git a/src/websocket/connectionManager.ts b/src/websocket/connectionManager.ts index afaaf44..c2f3798 100644 --- a/src/websocket/connectionManager.ts +++ b/src/websocket/connectionManager.ts @@ -1,15 +1,23 @@ import { WebSocket } from 'ws'; import { handleMessage } from './messageHandler'; -import { getChatModel, getChatModelProvider } from '../config'; import { getAvailableProviders } from '../lib/providers'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import type { Embeddings } from '@langchain/core/embeddings'; +import type { IncomingMessage } from 'http'; import logger from '../utils/logger'; -export const handleConnection = async (ws: WebSocket) => { +export const handleConnection = async ( + ws: WebSocket, + request: IncomingMessage, +) => { + const searchParams = new URL(request.url, `http://${request.headers.host}`) + .searchParams; + const models = await getAvailableProviders(); - const provider = getChatModelProvider(); - const chatModel = getChatModel(); + const provider = + searchParams.get('chatModelProvider') || Object.keys(models)[0]; + const chatModel = + searchParams.get('chatModel') || Object.keys(models[provider])[0]; let llm: BaseChatModel | undefined; let embeddings: Embeddings | undefined; diff --git a/src/websocket/websocketServer.ts b/src/websocket/websocketServer.ts index bc84f52..3ab0b51 100644 --- a/src/websocket/websocketServer.ts +++ b/src/websocket/websocketServer.ts @@ -10,9 +10,7 @@ export const initServer = ( const port = getPort(); const wss = new WebSocketServer({ server }); - wss.on('connection', (ws) => { - handleConnection(ws); - }); + wss.on('connection', handleConnection); logger.info(`WebSocket server started on port ${port}`); }; diff --git a/ui/components/ChatWindow.tsx b/ui/components/ChatWindow.tsx index 4c138ff..68a2ba0 100644 --- a/ui/components/ChatWindow.tsx +++ b/ui/components/ChatWindow.tsx @@ -19,14 +19,42 @@ const useSocket = (url: string) => { useEffect(() => { if (!ws) { - const ws = new WebSocket(url); - ws.onopen = () => { - console.log('[DEBUG] open'); - setWs(ws); + const connectWs = async () => { + let chatModel = localStorage.getItem('chatModel'); + let chatModelProvider = localStorage.getItem('chatModelProvider'); + + if (!chatModel || !chatModelProvider) { + const chatModelProviders = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/models`, + ).then(async (res) => (await res.json())['providers']); + + if ( + !chatModelProviders || + Object.keys(chatModelProviders).length === 0 + ) + return console.error('No chat models available'); + + chatModelProvider = Object.keys(chatModelProviders)[0]; + chatModel = Object.keys(chatModelProviders[chatModelProvider])[0]; + + localStorage.setItem('chatModel', chatModel!); + localStorage.setItem('chatModelProvider', chatModelProvider); + } + + const ws = new WebSocket( + `${url}?chatModel=${chatModel}&chatModelProvider=${chatModelProvider}`, + ); + ws.onopen = () => { + console.log('[DEBUG] open'); + setWs(ws); + }; }; + + connectWs(); } return () => { + 1; ws?.close(); console.log('[DEBUG] closed'); }; diff --git a/ui/components/SearchImages.tsx b/ui/components/SearchImages.tsx index 137571c..aa70c96 100644 --- a/ui/components/SearchImages.tsx +++ b/ui/components/SearchImages.tsx @@ -29,6 +29,10 @@ const SearchImages = ({ - diff --git a/ui/package.json b/ui/package.json index 700768c..4ac9fc5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "perplexica-frontend", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "author": "ItzCrazyKns", "scripts": { @@ -22,6 +22,7 @@ "next": "14.1.4", "react": "^18", "react-dom": "^18", + "react-speech-kit": "^3.0.1", "react-textarea-autosize": "^8.5.3", "tailwind-merge": "^2.2.2", "yet-another-react-lightbox": "^3.17.2", diff --git a/ui/yarn.lock b/ui/yarn.lock index 37480fd..6f8dd11 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2632,6 +2632,11 @@ react-is@^16.13.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-speech-kit@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/react-speech-kit/-/react-speech-kit-3.0.1.tgz#8bd936adfe064be1c5a07e2992dfdfd772e80d14" + integrity sha512-MXNOciISanhmnxpHJkBOev3M3NPDpW1T7nTc/eGw5pO9cXpoUccRxZkmr/IlpTPbPEneDNeTmbwri/YweyctZg== + react-textarea-autosize@^8.5.3: version "8.5.3" resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz#d1e9fe760178413891484847d3378706052dd409" @@ -2845,6 +2850,7 @@ streamsearch@^1.1.0: integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: + name string-width-cjs version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== diff --git a/yarn.lock b/yarn.lock index 205b0ed..64863b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1291,6 +1291,11 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +react-text-to-speech@^0.14.5: + version "0.14.5" + resolved "https://registry.yarnpkg.com/react-text-to-speech/-/react-text-to-speech-0.14.5.tgz#f918786ab283311535682011045bd49777193300" + integrity sha512-3brr/IrK/5YTtOZSTo+Y8b+dnWelzfZiDZvkXnOct1e7O7fgA/h9bYAVrtwSRo/VxKfdw+wh6glkj6M0mlQuQQ== + readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" From 79f6a52b5b5a6cd47f3503bd0d9183a6df436f39 Mon Sep 17 00:00:00 2001 From: ItzCrazyKns Date: Fri, 3 May 2024 21:16:48 +0530 Subject: [PATCH 011/434] feat(ui-packages): add `react-text-to-speech`, bump version --- package.json | 3 +-- ui/package.json | 4 ++-- ui/yarn.lock | 8 ++++---- yarn.lock | 5 ----- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index ee75225..5afcb17 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "perplexica-backend", - "version": "1.2.0", + "version": "1.2.1", "license": "MIT", "author": "ItzCrazyKns", "scripts": { @@ -29,7 +29,6 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "langchain": "^0.1.30", - "react-text-to-speech": "^0.14.5", "winston": "^3.13.0", "ws": "^8.16.0", "zod": "^3.22.4" diff --git a/ui/package.json b/ui/package.json index 4ac9fc5..ad1ff8b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "perplexica-frontend", - "version": "1.2.0", + "version": "1.2.1", "license": "MIT", "author": "ItzCrazyKns", "scripts": { @@ -22,7 +22,7 @@ "next": "14.1.4", "react": "^18", "react-dom": "^18", - "react-speech-kit": "^3.0.1", + "react-text-to-speech": "^0.14.5", "react-textarea-autosize": "^8.5.3", "tailwind-merge": "^2.2.2", "yet-another-react-lightbox": "^3.17.2", diff --git a/ui/yarn.lock b/ui/yarn.lock index 6f8dd11..5da13f8 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2632,10 +2632,10 @@ react-is@^16.13.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-speech-kit@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/react-speech-kit/-/react-speech-kit-3.0.1.tgz#8bd936adfe064be1c5a07e2992dfdfd772e80d14" - integrity sha512-MXNOciISanhmnxpHJkBOev3M3NPDpW1T7nTc/eGw5pO9cXpoUccRxZkmr/IlpTPbPEneDNeTmbwri/YweyctZg== +react-text-to-speech@^0.14.5: + version "0.14.5" + resolved "https://registry.yarnpkg.com/react-text-to-speech/-/react-text-to-speech-0.14.5.tgz#f918786ab283311535682011045bd49777193300" + integrity sha512-3brr/IrK/5YTtOZSTo+Y8b+dnWelzfZiDZvkXnOct1e7O7fgA/h9bYAVrtwSRo/VxKfdw+wh6glkj6M0mlQuQQ== react-textarea-autosize@^8.5.3: version "8.5.3" diff --git a/yarn.lock b/yarn.lock index 64863b9..205b0ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1291,11 +1291,6 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -react-text-to-speech@^0.14.5: - version "0.14.5" - resolved "https://registry.yarnpkg.com/react-text-to-speech/-/react-text-to-speech-0.14.5.tgz#f918786ab283311535682011045bd49777193300" - integrity sha512-3brr/IrK/5YTtOZSTo+Y8b+dnWelzfZiDZvkXnOct1e7O7fgA/h9bYAVrtwSRo/VxKfdw+wh6glkj6M0mlQuQQ== - readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" From c710f4f88c9183ff9689d14fff7cde67a2b1f7e1 Mon Sep 17 00:00:00 2001 From: ItzCrazyKns Date: Sat, 4 May 2024 10:48:42 +0530 Subject: [PATCH 012/434] feat(message-box): fix bugs --- ui/components/MessageBox.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/components/MessageBox.tsx b/ui/components/MessageBox.tsx index 9ee7245..9712a23 100644 --- a/ui/components/MessageBox.tsx +++ b/ui/components/MessageBox.tsx @@ -34,15 +34,13 @@ const MessageBox = ({ const [speechMessage, setSpeechMessage] = useState(message.content); useEffect(() => { + const regex = /\[(\d+)\]/g; + if ( message.role === 'assistant' && message?.sources && message.sources.length > 0 ) { - const regex = /\[(\d+)\]/g; - - setSpeechMessage(message.content.replace(regex, '')); - return setParsedMessage( message.content.replace( regex, @@ -51,6 +49,8 @@ const MessageBox = ({ ), ); } + + setSpeechMessage(message.content.replace(regex, '')); setParsedMessage(message.content); }, [message.content, message.sources, message.role]); @@ -95,7 +95,7 @@ const MessageBox = ({ {parsedMessage} - {!loading && ( + {loading && isLast ? null : (
+ ); +} const Sidebar = ({ children }: { children: React.ReactNode }) => { const segments = useSelectedLayoutSegments(); @@ -38,31 +45,39 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => { return (
-
+
- + -
+ {navLinks.map((link, i) => ( {link.active && ( -
+
)} ))} -
- setIsSettingsOpen(!isSettingsOpen)} - className="text-white cursor-pointer" - /> + + + + + + setIsSettingsOpen(!isSettingsOpen)} + className="cursor-pointer" + /> + + {
-
+
{navLinks.map((link, i) => ( Date: Fri, 24 May 2024 21:58:14 +0800 Subject: [PATCH 069/434] refactor(SettingDialog): extract reduplicate code to common component DO NOT REPEAT YOURSELF! --- ui/components/SettingsDialog.tsx | 213 ++++++++++++++++++------------- 1 file changed, 127 insertions(+), 86 deletions(-) diff --git a/ui/components/SettingsDialog.tsx b/ui/components/SettingsDialog.tsx index 1942179..4d80b9c 100644 --- a/ui/components/SettingsDialog.tsx +++ b/ui/components/SettingsDialog.tsx @@ -1,6 +1,51 @@ +import { cn } from '@/lib/utils'; import { Dialog, Transition } from '@headlessui/react'; import { CloudUpload, RefreshCcw, RefreshCw } from 'lucide-react'; -import React, { Fragment, useEffect, useState } from 'react'; +import React, { + Fragment, + useEffect, + useMemo, + useState, + type SelectHTMLAttributes, +} from 'react'; + +interface InputProps extends React.InputHTMLAttributes {} + +function Input({ className, ...restProps }: InputProps) { + return ( + + ); +} + +interface SelectProps extends SelectHTMLAttributes { + options: { value: string; label: string; disabled?: boolean }[]; +} + +function Select({ className, options, ...restProps }: SelectProps) { + return ( + + ); +} interface SettingsType { chatModelProviders: { @@ -169,7 +214,7 @@ const SettingsDialog = ({

Chat model Provider

- + />
)} {selectedChatModelProvider && @@ -196,37 +239,40 @@ const SettingsDialog = ({

Chat Model

- + ]; + + return chatModelProvider + ? chatModelProvider.length > 0 + ? chatModelProvider.map((model) => ({ + value: model, + label: model, + })) + : [ + { + value: '', + label: 'No models available', + disabled: true, + }, + ] + : [ + { + value: '', + label: + 'Invalid provider, please check backend logs', + disabled: true, + }, + ]; + })()} + />
)} {selectedChatModelProvider && @@ -236,42 +282,39 @@ const SettingsDialog = ({

Model name

- setSelectedChatModel(e.target.value) } - className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm" />

Custom OpenAI API Key

- setCustomOpenAIApiKey(e.target.value) } - className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm" />

Custom OpenAI Base URL

- setCustomOpenAIBaseURL(e.target.value) } - className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm" />
@@ -282,7 +325,7 @@ const SettingsDialog = ({

Embedding model Provider

- + options={Object.keys( + config.embeddingModelProviders, + ).map((provider) => ({ + label: + provider.charAt(0).toUpperCase() + + provider.slice(1), + value: provider, + }))} + />
)} {selectedEmbeddingModelProvider && ( @@ -308,44 +349,47 @@ const SettingsDialog = ({

Embedding Model

- + ]; + + return embeddingModelProvider + ? embeddingModelProvider.length > 0 + ? embeddingModelProvider.map((model) => ({ + label: model, + value: model, + })) + : [ + { + label: 'No embedding models available', + value: '', + disabled: true, + }, + ] + : [ + { + label: + 'Invalid provider, please check backend logs', + value: '', + disabled: true, + }, + ]; + })()} + />
)}

OpenAI API Key

-

Ollama API URL

-

GROQ API Key

-
From 89c30530bc44013d6c7d088f8a7011c11c4c450c Mon Sep 17 00:00:00 2001 From: WanQuanXie Date: Fri, 24 May 2024 22:08:47 +0800 Subject: [PATCH 070/434] update(Navbar): update Navbar light mode background --- ui/components/Navbar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/Navbar.tsx b/ui/components/Navbar.tsx index c07d6fd..f95a455 100644 --- a/ui/components/Navbar.tsx +++ b/ui/components/Navbar.tsx @@ -38,7 +38,7 @@ const Navbar = ({ messages }: { messages: Message[] }) => { }, []); return ( -
+
Date: Fri, 24 May 2024 22:41:06 +0800 Subject: [PATCH 071/434] update(SearchVideos): video cover label style adapt light mode --- ui/components/SearchVideos.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/SearchVideos.tsx b/ui/components/SearchVideos.tsx index 05c3180..eaad078 100644 --- a/ui/components/SearchVideos.tsx +++ b/ui/components/SearchVideos.tsx @@ -118,7 +118,7 @@ const Searchvideos = ({ alt={video.title} className="relative h-full w-full aspect-video object-cover rounded-lg" /> -
+

Video

From 382fa295e57593fb80b9371af47dbd1e9abfecee Mon Sep 17 00:00:00 2001 From: Devin Stokes Date: Fri, 24 May 2024 08:19:15 -0700 Subject: [PATCH 072/434] fix: add extra_hosts to docker-compose.yaml to allow connection to ollama --- docker-compose.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose.yaml b/docker-compose.yaml index ac83575..d8e1047 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -18,6 +18,8 @@ services: - searxng ports: - 3001:3001 + extra_hosts: + - "host.docker.internal:host-gateway" networks: - perplexica-network @@ -32,6 +34,8 @@ services: - perplexica-backend ports: - 3000:3000 + extra_hosts: + - "host.docker.internal:host-gateway" networks: - perplexica-network From c97a4347230f505a68339d0c08d13baf2cf67696 Mon Sep 17 00:00:00 2001 From: WanQuanXie Date: Sat, 25 May 2024 06:57:24 +0800 Subject: [PATCH 073/434] fix(ui): hover style class uses --- ui/components/MessageActions/Rewrite.tsx | 2 +- ui/components/MessageBox.tsx | 4 ++-- ui/components/MessageInputActions.tsx | 2 +- ui/components/MessageSources.tsx | 6 +++--- ui/components/SearchImages.tsx | 4 ++-- ui/components/SearchVideos.tsx | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui/components/MessageActions/Rewrite.tsx b/ui/components/MessageActions/Rewrite.tsx index b5f865b..76a422f 100644 --- a/ui/components/MessageActions/Rewrite.tsx +++ b/ui/components/MessageActions/Rewrite.tsx @@ -10,7 +10,7 @@ const Rewrite = ({ return ( */} @@ -127,7 +127,7 @@ const MessageBox = ({ start(); } }} - className="p-2 text-black/70 dark:text-white/70 rounded-xl hover(bg-secondLight dark:bg-secondDark) transition duration-200 hover:text-black dark:hover:text-white" + className="p-2 text-black/70 dark:text-white/70 rounded-xl hover:bg-secondLight dark:hover:bg-secondDark transition duration-200 hover:text-black dark:hover:text-white" > {speechStatus === 'started' ? ( diff --git a/ui/components/MessageInputActions.tsx b/ui/components/MessageInputActions.tsx index 22fc708..8b6d784 100644 --- a/ui/components/MessageInputActions.tsx +++ b/ui/components/MessageInputActions.tsx @@ -85,7 +85,7 @@ export const Focus = ({ {focusMode !== 'webSearch' ? (
diff --git a/ui/components/MessageSources.tsx b/ui/components/MessageSources.tsx index 476c73c..292b8c6 100644 --- a/ui/components/MessageSources.tsx +++ b/ui/components/MessageSources.tsx @@ -20,7 +20,7 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
{sources.slice(0, 3).map((source, i) => ( { {sources.length > 3 && ( diff --git a/ui/components/MessageActions/Rewrite.tsx b/ui/components/MessageActions/Rewrite.tsx index 76a422f..80fadb3 100644 --- a/ui/components/MessageActions/Rewrite.tsx +++ b/ui/components/MessageActions/Rewrite.tsx @@ -10,7 +10,7 @@ const Rewrite = ({ return ( */} @@ -127,7 +127,7 @@ const MessageBox = ({ start(); } }} - className="p-2 text-black/70 dark:text-white/70 rounded-xl hover:bg-secondLight dark:hover:bg-secondDark transition duration-200 hover:text-black dark:hover:text-white" + className="p-2 text-black/70 dark:text-white/70 rounded-xl hover:bg-light-secondary dark:hover:bg-dark-secondary transition duration-200 hover:text-black dark:hover:text-white" > {speechStatus === 'started' ? ( @@ -144,7 +144,7 @@ const MessageBox = ({ message.role === 'assistant' && !loading && ( <> -
+
@@ -156,7 +156,7 @@ const MessageBox = ({ className="flex flex-col space-y-3 text-sm" key={i} > -
+
{ sendMessage(suggestion); diff --git a/ui/components/MessageBoxLoading.tsx b/ui/components/MessageBoxLoading.tsx index 3a80fe8..caa6f18 100644 --- a/ui/components/MessageBoxLoading.tsx +++ b/ui/components/MessageBoxLoading.tsx @@ -1,9 +1,9 @@ const MessageBoxLoading = () => { return ( -
-
-
-
+
+
+
+
); }; diff --git a/ui/components/MessageInput.tsx b/ui/components/MessageInput.tsx index 4fbea7c..d215787 100644 --- a/ui/components/MessageInput.tsx +++ b/ui/components/MessageInput.tsx @@ -40,7 +40,7 @@ const MessageInput = ({ } }} className={cn( - 'bg-primaryLight dark:bg-primaryDark p-4 flex items-center overflow-hidden border border-light dark:border-dark', + 'bg-light-primary dark:bg-dark-primary p-4 flex items-center overflow-hidden border border-light-300 dark:border-dark-200', mode === 'multi' ? 'flex-col rounded-lg' : 'flex-row rounded-full', )} > diff --git a/ui/components/MessageInputActions.tsx b/ui/components/MessageInputActions.tsx index 8b6d784..80b2797 100644 --- a/ui/components/MessageInputActions.tsx +++ b/ui/components/MessageInputActions.tsx @@ -16,7 +16,7 @@ export const Attach = () => { return ( @@ -85,7 +85,7 @@ export const Focus = ({ {focusMode !== 'webSearch' ? (
@@ -109,7 +109,7 @@ export const Focus = ({ leaveTo="opacity-0 translate-y-1" > -
+
{focusModes.map((mode, i) => ( setFocusMode(mode.key)} @@ -117,8 +117,8 @@ export const Focus = ({ className={cn( 'p-2 rounded-lg flex flex-col items-start justify-start text-start space-y-2 duration-200 cursor-pointer transition', focusMode === mode.key - ? 'bg-primaryLight dark:bg-primaryDark' - : 'hover:bg-primaryLight dark:bg-primaryDark', + ? 'bg-light-primary dark:bg-dark-primary' + : 'hover:bg-light-primary dark:bg-dark-primary', )} >
Copilot {
{sources.slice(0, 3).map((source, i) => ( { {sources.length > 3 && (