mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-04-30 16:22:29 +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;
|
export default nextConfig;
|
||||||
|
@ -12,26 +12,35 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.2.0",
|
"@headlessui/react": "^2.2.0",
|
||||||
|
"@iarna/toml": "^2.2.5",
|
||||||
"@icons-pack/react-simple-icons": "^9.4.0",
|
"@icons-pack/react-simple-icons": "^9.4.0",
|
||||||
"@langchain/openai": "^0.0.25",
|
"@langchain/openai": "^0.0.25",
|
||||||
"@tailwindcss/typography": "^0.5.12",
|
"@tailwindcss/typography": "^0.5.12",
|
||||||
|
"axios": "^1.8.3",
|
||||||
"clsx": "^2.1.0",
|
"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",
|
"langchain": "^0.1.30",
|
||||||
"lucide-react": "^0.363.0",
|
"lucide-react": "^0.363.0",
|
||||||
"markdown-to-jsx": "^7.7.2",
|
"markdown-to-jsx": "^7.7.2",
|
||||||
"next": "14.1.4",
|
"next": "^15.2.2",
|
||||||
"next-themes": "^0.3.0",
|
"next-themes": "^0.3.0",
|
||||||
|
"pdf-parse": "^1.1.1",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-text-to-speech": "^0.14.5",
|
"react-text-to-speech": "^0.14.5",
|
||||||
"react-textarea-autosize": "^8.5.3",
|
"react-textarea-autosize": "^8.5.3",
|
||||||
"sonner": "^1.4.41",
|
"sonner": "^1.4.41",
|
||||||
"tailwind-merge": "^2.2.2",
|
"tailwind-merge": "^2.2.2",
|
||||||
|
"winston": "^3.17.0",
|
||||||
"yet-another-react-lightbox": "^3.17.2",
|
"yet-another-react-lightbox": "^3.17.2",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/html-to-text": "^9.0.4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
"@types/pdf-parse": "^1.1.4",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
@ -18,9 +22,19 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": [
|
||||||
}
|
"./*"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"target": "ES2017"
|
||||||
"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