Perplexica/src/lib/config.ts
2025-03-21 09:26:01 -04:00

143 lines
3.5 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import toml from '@iarna/toml';
const configFileName = 'config.toml';
interface Config {
GENERAL: {
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;
const getEnvVar = (key: string): string | undefined => {
return process.env[key];
};
const getConfigValue = (path: string[], defaultValue: string): string => {
// Convert path to environment variable name (e.g., ['MODELS', 'GROQ', 'API_KEY'] -> 'GROQ_API_KEY')
const envKey = path.slice(1).join('_').toUpperCase();
const envValue = getEnvVar(envKey);
if (envValue !== undefined) {
return envValue;
}
// Fall back to config.toml
let value: any = loadConfig();
for (const key of path) {
value = value[key];
if (value === undefined) {
return defaultValue;
}
}
return value;
};
export const getSimilarityMeasure = () =>
getConfigValue(['GENERAL', 'SIMILARITY_MEASURE'], 'cosine');
export const getKeepAlive = () =>
getConfigValue(['GENERAL', 'KEEP_ALIVE'], '30s');
export const getOpenaiApiKey = () =>
getConfigValue(['MODELS', 'OPENAI', 'API_KEY'], '');
export const getGroqApiKey = () =>
getConfigValue(['MODELS', 'GROQ', 'API_KEY'], '');
export const getAnthropicApiKey = () =>
getConfigValue(['MODELS', 'ANTHROPIC', 'API_KEY'], '');
export const getGeminiApiKey = () =>
getConfigValue(['MODELS', 'GEMINI', 'API_KEY'], '');
export const getSearxngApiEndpoint = () =>
getConfigValue(['API_ENDPOINTS', 'SEARXNG'], '');
export const getOllamaApiEndpoint = () =>
getConfigValue(['MODELS', 'OLLAMA', 'API_URL'], 'http://localhost:11434');
export const getCustomOpenaiApiKey = () =>
getConfigValue(['MODELS', 'CUSTOM_OPENAI', 'API_KEY'], '');
export const getCustomOpenaiApiUrl = () =>
getConfigValue(['MODELS', 'CUSTOM_OPENAI', 'API_URL'], '');
export const getCustomOpenaiModelName = () =>
getConfigValue(['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(path.join(process.cwd(), `${configFileName}`)),
toml.stringify(mergedConfig),
);
};