refactor: pull config to a sep json

This commit is contained in:
Han Xiao
2025-02-07 13:36:55 +08:00
parent 5a34983d03
commit 3e60f712d9
3 changed files with 166 additions and 99 deletions

59
config.json Normal file
View File

@@ -0,0 +1,59 @@
{
"env": {
"https_proxy": "",
"OPENAI_BASE_URL": "",
"GEMINI_API_KEY": "",
"OPENAI_API_KEY": "",
"JINA_API_KEY": "",
"BRAVE_API_KEY": "",
"DEFAULT_MODEL_NAME": ""
},
"defaults": {
"search_provider": "jina",
"llm_provider": "gemini",
"step_sleep": 1000
},
"providers": {
"gemini": {
"createClient": "createGoogleGenerativeAI"
},
"openai": {
"createClient": "createOpenAI",
"clientConfig": {
"compatibility": "strict"
}
}
},
"models": {
"gemini": {
"default": {
"model": "gemini-2.0-flash",
"temperature": 0,
"maxTokens": 8000
},
"tools": {
"dedup": { "temperature": 0.1 },
"evaluator": {},
"errorAnalyzer": {},
"queryRewriter": { "temperature": 0.1 },
"agent": { "temperature": 0.7 },
"agentBeastMode": { "temperature": 0.7 }
}
},
"openai": {
"default": {
"model": "gpt-4o-mini",
"temperature": 0,
"maxTokens": 8000
},
"tools": {
"dedup": { "temperature": 0.1 },
"evaluator": {},
"errorAnalyzer": {},
"queryRewriter": { "temperature": 0.1 },
"agent": { "temperature": 0.7 },
"agentBeastMode": { "temperature": 0.7 }
}
}
}
}

View File

@@ -1,50 +1,48 @@
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import { ProxyAgent, setGlobalDispatcher } from 'undici'; import { ProxyAgent, setGlobalDispatcher } from 'undici';
import { createGoogleGenerativeAI } from '@ai-sdk/google'; import { createGoogleGenerativeAI } from '@ai-sdk/google';
import {createOpenAI, OpenAIProviderSettings} from '@ai-sdk/openai'; import { createOpenAI, OpenAIProviderSettings } from '@ai-sdk/openai';
import configJson from '../config.json';
export type LLMProvider = 'openai' | 'gemini';
export type ToolName = keyof ToolConfigs;
function isValidProvider(provider: string): provider is LLMProvider {
return provider === 'openai' || provider === 'gemini';
}
function validateModelConfig(config: ModelConfig, toolName: string): ModelConfig {
if (typeof config.model !== 'string' || config.model.length === 0) {
throw new Error(`Invalid model name for ${toolName}`);
}
if (typeof config.temperature !== 'number' || config.temperature < 0 || config.temperature > 1) {
throw new Error(`Invalid temperature for ${toolName}`);
}
if (typeof config.maxTokens !== 'number' || config.maxTokens <= 0) {
throw new Error(`Invalid maxTokens for ${toolName}`);
}
return config;
}
export interface ModelConfig {
model: string;
temperature: number;
maxTokens: number;
}
export interface ToolConfigs {
dedup: ModelConfig;
evaluator: ModelConfig;
errorAnalyzer: ModelConfig;
queryRewriter: ModelConfig;
agent: ModelConfig;
agentBeastMode: ModelConfig;
}
// Load environment variables
dotenv.config(); dotenv.config();
// Setup the proxy globally if present // Types
if (process.env.https_proxy) { export type LLMProvider = 'openai' | 'gemini';
export type ToolName = keyof typeof configJson.models.gemini.tools;
// Type definitions for our config structure
type EnvConfig = typeof configJson.env;
interface ProviderConfigBase {
createClient: string;
}
interface OpenAIProviderConfig extends ProviderConfigBase {
clientConfig: {
compatibility: "strict" | "compatible";
};
}
interface GeminiProviderConfig extends ProviderConfigBase {}
type ProviderConfig = {
openai: OpenAIProviderConfig;
gemini: GeminiProviderConfig;
};
// Environment setup
const env: EnvConfig = { ...configJson.env };
(Object.keys(env) as (keyof EnvConfig)[]).forEach(key => {
if (process.env[key]) {
env[key] = process.env[key] || env[key];
}
});
// Setup proxy if present
if (env.https_proxy) {
try { try {
const proxyUrl = new URL(process.env.https_proxy).toString(); const proxyUrl = new URL(env.https_proxy).toString();
const dispatcher = new ProxyAgent({ uri: proxyUrl }); const dispatcher = new ProxyAgent({ uri: proxyUrl });
setGlobalDispatcher(dispatcher); setGlobalDispatcher(dispatcher);
} catch (error) { } catch (error) {
@@ -52,79 +50,73 @@ if (process.env.https_proxy) {
} }
} }
export const OPENAI_BASE_URL = process.env.OPENAI_BASE_URL; // Export environment variables
export const GEMINI_API_KEY = process.env.GEMINI_API_KEY as string; export const OPENAI_BASE_URL = env.OPENAI_BASE_URL;
export const OPENAI_API_KEY = process.env.OPENAI_API_KEY as string; export const GEMINI_API_KEY = env.GEMINI_API_KEY;
export const JINA_API_KEY = process.env.JINA_API_KEY as string; export const OPENAI_API_KEY = env.OPENAI_API_KEY;
export const BRAVE_API_KEY = process.env.BRAVE_API_KEY as string; export const JINA_API_KEY = env.JINA_API_KEY;
export const SEARCH_PROVIDER: 'brave' | 'jina' | 'duck' = 'jina'; export const BRAVE_API_KEY = env.BRAVE_API_KEY;
export const SEARCH_PROVIDER = configJson.defaults.search_provider;
export const STEP_SLEEP = configJson.defaults.step_sleep;
// Determine LLM provider
export const LLM_PROVIDER: LLMProvider = (() => { export const LLM_PROVIDER: LLMProvider = (() => {
const provider = process.env.LLM_PROVIDER || 'gemini'; const provider = process.env.LLM_PROVIDER || configJson.defaults.llm_provider;
if (!isValidProvider(provider)) { if (!isValidProvider(provider)) {
throw new Error(`Invalid LLM provider: ${provider}`); throw new Error(`Invalid LLM provider: ${provider}`);
} }
return provider; return provider;
})(); })();
const DEFAULT_GEMINI_MODEL = process.env.DEFAULT_MODEL_NAME || 'gemini-2.0-flash'; function isValidProvider(provider: string): provider is LLMProvider {
const DEFAULT_OPENAI_MODEL = process.env.DEFAULT_MODEL_NAME || 'gpt-4o-mini'; return provider === 'openai' || provider === 'gemini';
}
const defaultGeminiConfig: ModelConfig = { interface ToolConfig {
model: DEFAULT_GEMINI_MODEL, model: string;
temperature: 0, temperature: number;
maxTokens: 8000 maxTokens: number;
}; }
const defaultOpenAIConfig: ModelConfig = { interface ToolOverrides {
model: DEFAULT_OPENAI_MODEL, temperature?: number;
temperature: 0, maxTokens?: number;
maxTokens: 8000 }
};
export const modelConfigs: Record<LLMProvider, ToolConfigs> = { // Get tool configuration
gemini: { export function getToolConfig(toolName: ToolName): ToolConfig {
dedup: validateModelConfig({ ...defaultGeminiConfig, temperature: 0.1 }, 'dedup'), const providerConfig = configJson.models[LLM_PROVIDER];
evaluator: validateModelConfig({ ...defaultGeminiConfig, temperature: 0 }, 'evaluator'), const defaultConfig = providerConfig.default;
errorAnalyzer: validateModelConfig({ ...defaultGeminiConfig, temperature: 0 }, 'errorAnalyzer'), const toolOverrides = providerConfig.tools[toolName] as ToolOverrides;
queryRewriter: validateModelConfig({ ...defaultGeminiConfig, temperature: 0.1 }, 'queryRewriter'),
agent: validateModelConfig({ ...defaultGeminiConfig, temperature: 0.7 }, 'agent'),
agentBeastMode: validateModelConfig({ ...defaultGeminiConfig, temperature: 0.7 }, 'agentBeastMode')
},
openai: {
dedup: validateModelConfig({ ...defaultOpenAIConfig, temperature: 0.1 }, 'dedup'),
evaluator: validateModelConfig({ ...defaultOpenAIConfig, temperature: 0 }, 'evaluator'),
errorAnalyzer: validateModelConfig({ ...defaultOpenAIConfig, temperature: 0 }, 'errorAnalyzer'),
queryRewriter: validateModelConfig({ ...defaultOpenAIConfig, temperature: 0.1 }, 'queryRewriter'),
agent: validateModelConfig({ ...defaultOpenAIConfig, temperature: 0.7 }, 'agent'),
agentBeastMode: validateModelConfig({ ...defaultOpenAIConfig, temperature: 0.7 }, 'agentBeastMode')
}
};
export function getToolConfig(toolName: ToolName): ModelConfig { return {
if (!modelConfigs[LLM_PROVIDER][toolName]) { model: process.env.DEFAULT_MODEL_NAME || defaultConfig.model,
throw new Error(`Invalid tool name: ${toolName}`); temperature: toolOverrides.temperature ?? defaultConfig.temperature,
} maxTokens: toolOverrides.maxTokens ?? defaultConfig.maxTokens
return modelConfigs[LLM_PROVIDER][toolName]; };
} }
export function getMaxTokens(toolName: ToolName): number { export function getMaxTokens(toolName: ToolName): number {
return getToolConfig(toolName).maxTokens; return getToolConfig(toolName).maxTokens;
} }
// Get model instance
export function getModel(toolName: ToolName) { export function getModel(toolName: ToolName) {
const config = getToolConfig(toolName); const config = getToolConfig(toolName);
const providerConfig = configJson.providers[LLM_PROVIDER] as ProviderConfig[typeof LLM_PROVIDER];
if (LLM_PROVIDER === 'openai') { if (LLM_PROVIDER === 'openai') {
if (!OPENAI_API_KEY) { if (!OPENAI_API_KEY) {
throw new Error('OPENAI_API_KEY not found'); throw new Error('OPENAI_API_KEY not found');
} }
const opt: OpenAIProviderSettings = { const opt: OpenAIProviderSettings = {
apiKey: OPENAI_API_KEY, apiKey: OPENAI_API_KEY,
compatibility: 'strict' compatibility: (providerConfig as OpenAIProviderConfig).clientConfig.compatibility
} };
if (OPENAI_BASE_URL) { if (OPENAI_BASE_URL) {
opt.baseURL = OPENAI_BASE_URL opt.baseURL = OPENAI_BASE_URL;
} }
return createOpenAI(opt)(config.model); return createOpenAI(opt)(config.model);
@@ -133,19 +125,36 @@ export function getModel(toolName: ToolName) {
if (!GEMINI_API_KEY) { if (!GEMINI_API_KEY) {
throw new Error('GEMINI_API_KEY not found'); throw new Error('GEMINI_API_KEY not found');
} }
return createGoogleGenerativeAI({ apiKey: GEMINI_API_KEY })(config.model); return createGoogleGenerativeAI({ apiKey: GEMINI_API_KEY })(config.model);
} }
export const STEP_SLEEP = 1000; // Validate required environment variables
if (LLM_PROVIDER === 'gemini' && !GEMINI_API_KEY) throw new Error("GEMINI_API_KEY not found"); if (LLM_PROVIDER === 'gemini' && !GEMINI_API_KEY) throw new Error("GEMINI_API_KEY not found");
if (LLM_PROVIDER === 'openai' && !OPENAI_API_KEY) throw new Error("OPENAI_API_KEY not found"); if (LLM_PROVIDER === 'openai' && !OPENAI_API_KEY) throw new Error("OPENAI_API_KEY not found");
if (!JINA_API_KEY) throw new Error("JINA_API_KEY not found"); if (!JINA_API_KEY) throw new Error("JINA_API_KEY not found");
console.log('LLM Provider:', LLM_PROVIDER) // Log all configurations
if (LLM_PROVIDER === 'openai') { const configSummary = {
console.log('OPENAI_BASE_URL', OPENAI_BASE_URL) provider: {
console.log('Default Model', DEFAULT_OPENAI_MODEL) name: LLM_PROVIDER,
} else { model: LLM_PROVIDER === 'openai'
console.log('Default Model', DEFAULT_GEMINI_MODEL) ? configJson.models.openai.default.model
} : configJson.models.gemini.default.model,
...(LLM_PROVIDER === 'openai' && { baseUrl: OPENAI_BASE_URL })
},
search: {
provider: SEARCH_PROVIDER
},
tools: Object.entries(configJson.models[LLM_PROVIDER].tools).reduce((acc, [name, config]) => ({
...acc,
[name]: {
...getToolConfig(name as ToolName)
}
}), {}),
defaults: {
stepSleep: STEP_SLEEP
}
};
console.log('Configuration Summary:', JSON.stringify(configSummary, null, 2));

View File

@@ -285,14 +285,13 @@ export async function evaluateAnswer(
break; break;
} }
} catch (error) { } catch (error) {
console.error(`Error in ${evaluationType} evaluation:`, error);
const errorResult = await handleGenerateObjectError<EvaluationResponse>(error); const errorResult = await handleGenerateObjectError<EvaluationResponse>(error);
(tracker || new TokenTracker()).trackUsage('evaluator', errorResult.totalTokens || 0); (tracker || new TokenTracker()).trackUsage('evaluator', errorResult.totalTokens || 0);
if (!errorResult.object.pass) { // Always return from catch block to prevent undefined result
return { response: errorResult.object }; return { response: errorResult.object };
}
} }
} }
// Only reach this point if all evaluations pass
return { response: result!.object }; return { response: result!.object };
} }