mirror of
				https://github.com/ItzCrazyKns/Perplexica.git
				synced 2025-10-31 03:18:16 +00:00 
			
		
		
		
	feat(ui): update packages, add config, add searxng
This commit is contained in:
		
							
								
								
									
										117
									
								
								ui/lib/config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								ui/lib/config.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| import fs from 'fs'; | ||||
| import path from 'path'; | ||||
| import toml from '@iarna/toml'; | ||||
|  | ||||
| const configFileName = 'config.toml'; | ||||
|  | ||||
| interface Config { | ||||
|   GENERAL: { | ||||
|     PORT: number; | ||||
|     SIMILARITY_MEASURE: string; | ||||
|     KEEP_ALIVE: string; | ||||
|   }; | ||||
|   MODELS: { | ||||
|     OPENAI: { | ||||
|       API_KEY: string; | ||||
|     }; | ||||
|     GROQ: { | ||||
|       API_KEY: string; | ||||
|     }; | ||||
|     ANTHROPIC: { | ||||
|       API_KEY: string; | ||||
|     }; | ||||
|     GEMINI: { | ||||
|       API_KEY: string; | ||||
|     }; | ||||
|     OLLAMA: { | ||||
|       API_URL: string; | ||||
|     }; | ||||
|     CUSTOM_OPENAI: { | ||||
|       API_URL: string; | ||||
|       API_KEY: string; | ||||
|       MODEL_NAME: string; | ||||
|     }; | ||||
|   }; | ||||
|   API_ENDPOINTS: { | ||||
|     SEARXNG: string; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| type RecursivePartial<T> = { | ||||
|   [P in keyof T]?: RecursivePartial<T[P]>; | ||||
| }; | ||||
|  | ||||
| const loadConfig = () => | ||||
|   toml.parse( | ||||
|     fs.readFileSync(path.join(process.cwd(), `${configFileName}`), 'utf-8'), | ||||
|   ) as any as Config; | ||||
|  | ||||
| export const getPort = () => loadConfig().GENERAL.PORT; | ||||
|  | ||||
| export const getSimilarityMeasure = () => | ||||
|   loadConfig().GENERAL.SIMILARITY_MEASURE; | ||||
|  | ||||
| export const getKeepAlive = () => loadConfig().GENERAL.KEEP_ALIVE; | ||||
|  | ||||
| export const getOpenaiApiKey = () => loadConfig().MODELS.OPENAI.API_KEY; | ||||
|  | ||||
| export const getGroqApiKey = () => loadConfig().MODELS.GROQ.API_KEY; | ||||
|  | ||||
| export const getAnthropicApiKey = () => loadConfig().MODELS.ANTHROPIC.API_KEY; | ||||
|  | ||||
| export const getGeminiApiKey = () => loadConfig().MODELS.GEMINI.API_KEY; | ||||
|  | ||||
| export const getSearxngApiEndpoint = () => | ||||
|   process.env.SEARXNG_API_URL || loadConfig().API_ENDPOINTS.SEARXNG; | ||||
|  | ||||
| export const getOllamaApiEndpoint = () => loadConfig().MODELS.OLLAMA.API_URL; | ||||
|  | ||||
| export const getCustomOpenaiApiKey = () => | ||||
|   loadConfig().MODELS.CUSTOM_OPENAI.API_KEY; | ||||
|  | ||||
| export const getCustomOpenaiApiUrl = () => | ||||
|   loadConfig().MODELS.CUSTOM_OPENAI.API_URL; | ||||
|  | ||||
| export const getCustomOpenaiModelName = () => | ||||
|   loadConfig().MODELS.CUSTOM_OPENAI.MODEL_NAME; | ||||
|  | ||||
| const mergeConfigs = (current: any, update: any): any => { | ||||
|   if (update === null || update === undefined) { | ||||
|     return current; | ||||
|   } | ||||
|  | ||||
|   if (typeof current !== 'object' || current === null) { | ||||
|     return update; | ||||
|   } | ||||
|  | ||||
|   const result = { ...current }; | ||||
|  | ||||
|   for (const key in update) { | ||||
|     if (Object.prototype.hasOwnProperty.call(update, key)) { | ||||
|       const updateValue = update[key]; | ||||
|  | ||||
|       if ( | ||||
|         typeof updateValue === 'object' && | ||||
|         updateValue !== null && | ||||
|         typeof result[key] === 'object' && | ||||
|         result[key] !== null | ||||
|       ) { | ||||
|         result[key] = mergeConfigs(result[key], updateValue); | ||||
|       } else if (updateValue !== undefined) { | ||||
|         result[key] = updateValue; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return result; | ||||
| }; | ||||
|  | ||||
| export const updateConfig = (config: RecursivePartial<Config>) => { | ||||
|   const currentConfig = loadConfig(); | ||||
|   const mergedConfig = mergeConfigs(currentConfig, config); | ||||
|  | ||||
|   fs.writeFileSync( | ||||
|     path.join(__dirname, `../${configFileName}`), | ||||
|     toml.stringify(mergedConfig), | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										48
									
								
								ui/lib/searxng.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								ui/lib/searxng.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| import axios from 'axios'; | ||||
| import { getSearxngApiEndpoint } from './config'; | ||||
|  | ||||
| interface SearxngSearchOptions { | ||||
|   categories?: string[]; | ||||
|   engines?: string[]; | ||||
|   language?: string; | ||||
|   pageno?: number; | ||||
| } | ||||
|  | ||||
| interface SearxngSearchResult { | ||||
|   title: string; | ||||
|   url: string; | ||||
|   img_src?: string; | ||||
|   thumbnail_src?: string; | ||||
|   thumbnail?: string; | ||||
|   content?: string; | ||||
|   author?: string; | ||||
|   iframe_src?: string; | ||||
| } | ||||
|  | ||||
| export const searchSearxng = async ( | ||||
|   query: string, | ||||
|   opts?: SearxngSearchOptions, | ||||
| ) => { | ||||
|   const searxngURL = getSearxngApiEndpoint(); | ||||
|  | ||||
|   const url = new URL(`${searxngURL}/search?format=json`); | ||||
|   url.searchParams.append('q', query); | ||||
|  | ||||
|   if (opts) { | ||||
|     Object.keys(opts).forEach((key) => { | ||||
|       const value = opts[key as keyof SearxngSearchOptions]; | ||||
|       if (Array.isArray(value)) { | ||||
|         url.searchParams.append(key, value.join(',')); | ||||
|         return; | ||||
|       } | ||||
|       url.searchParams.append(key, value as string); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   const res = await axios.get(url.toString()); | ||||
|  | ||||
|   const results: SearxngSearchResult[] = res.data.results; | ||||
|   const suggestions: string[] = res.data.suggestions; | ||||
|  | ||||
|   return { results, suggestions }; | ||||
| }; | ||||
| @@ -7,6 +7,7 @@ const nextConfig = { | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   serverExternalPackages: ['pdf-parse'] | ||||
| }; | ||||
|  | ||||
| export default nextConfig; | ||||
|   | ||||
| @@ -12,26 +12,35 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@headlessui/react": "^2.2.0", | ||||
|     "@iarna/toml": "^2.2.5", | ||||
|     "@icons-pack/react-simple-icons": "^9.4.0", | ||||
|     "@langchain/openai": "^0.0.25", | ||||
|     "@tailwindcss/typography": "^0.5.12", | ||||
|     "axios": "^1.8.3", | ||||
|     "clsx": "^2.1.0", | ||||
|     "compute-cosine-similarity": "^1.1.0", | ||||
|     "compute-dot": "^1.1.0", | ||||
|     "html-to-text": "^9.0.5", | ||||
|     "langchain": "^0.1.30", | ||||
|     "lucide-react": "^0.363.0", | ||||
|     "markdown-to-jsx": "^7.7.2", | ||||
|     "next": "14.1.4", | ||||
|     "next": "^15.2.2", | ||||
|     "next-themes": "^0.3.0", | ||||
|     "pdf-parse": "^1.1.1", | ||||
|     "react": "^18", | ||||
|     "react-dom": "^18", | ||||
|     "react-text-to-speech": "^0.14.5", | ||||
|     "react-textarea-autosize": "^8.5.3", | ||||
|     "sonner": "^1.4.41", | ||||
|     "tailwind-merge": "^2.2.2", | ||||
|     "winston": "^3.17.0", | ||||
|     "yet-another-react-lightbox": "^3.17.2", | ||||
|     "zod": "^3.22.4" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/html-to-text": "^9.0.4", | ||||
|     "@types/node": "^20", | ||||
|     "@types/pdf-parse": "^1.1.4", | ||||
|     "@types/react": "^18", | ||||
|     "@types/react-dom": "^18", | ||||
|     "autoprefixer": "^10.0.1", | ||||
|   | ||||
| @@ -1,6 +1,10 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "lib": ["dom", "dom.iterable", "esnext"], | ||||
|     "lib": [ | ||||
|       "dom", | ||||
|       "dom.iterable", | ||||
|       "esnext" | ||||
|     ], | ||||
|     "allowJs": true, | ||||
|     "skipLibCheck": true, | ||||
|     "strict": true, | ||||
| @@ -18,9 +22,19 @@ | ||||
|       } | ||||
|     ], | ||||
|     "paths": { | ||||
|       "@/*": ["./*"] | ||||
|     } | ||||
|       "@/*": [ | ||||
|         "./*" | ||||
|       ] | ||||
|     }, | ||||
|     "target": "ES2017" | ||||
|   }, | ||||
|   "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], | ||||
|   "exclude": ["node_modules"] | ||||
|   "include": [ | ||||
|     "next-env.d.ts", | ||||
|     "**/*.ts", | ||||
|     "**/*.tsx", | ||||
|     ".next/types/**/*.ts" | ||||
|   ], | ||||
|   "exclude": [ | ||||
|     "node_modules" | ||||
|   ] | ||||
| } | ||||
|   | ||||
							
								
								
									
										693
									
								
								ui/yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										693
									
								
								ui/yarn.lock
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user