/** * 多 API 配置和路由 * 根据模型名称自动选择正确的 API 端点和密钥 */ /** * 清理 API 密钥 - 移除换行符、空格等特殊字符 */ function cleanApiKey(key) { if (!key) return null; return String(key).trim().replace(/\r?\n/g, '').replace(/\s+/g, ''); } // API 配置 const API_CONFIGS = { // Google Gemini gemini: { baseUrl: 'https://generativelanguage.googleapis.com/v1', apiKey: cleanApiKey(process.env.GEMINI_API_KEY), models: [ 'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite', 'gemini-2.0-flash', 'gemini-2.0-flash-001', 'gemini-2.0-flash-lite', 'gemini-2.0-flash-lite-001', // 保留旧模型名称以兼容 'gemini-1.5-pro', 'gemini-1.5-flash', 'gemini-pro', 'gemini-3-pro-preview' ], format: 'gemini', // 使用 Gemini 原生格式 }, // OpenAI (ChatGPT) openai: { baseUrl: 'https://api.openai.com/v1', apiKey: cleanApiKey(process.env.OPENAI_API_KEY), models: [ 'gpt-5.1', 'gpt-5-mini', 'gpt-5-nano', 'gpt-4.1', 'gpt-4.1-mini', 'gpt-4.1-nano', 'gpt-4', 'gpt-4-turbo', 'gpt-4o', 'gpt-4o-mini', 'gpt-3.5-turbo' // 保留旧模型以兼容 ], format: 'openai', // 使用 OpenAI 格式 }, // Grok (xAI) grok: { baseUrl: 'https://api.x.ai/v1', apiKey: cleanApiKey(process.env.GROK_API_KEY), models: [ 'grok-4-0709', 'grok-4-1-fast-reasoning', 'grok-4-1-fast-non-reasoning', 'grok-4-fast-reasoning', 'grok-4-fast-non-reasoning', 'grok-3', 'grok-3-mini', 'grok-code-fast-1', 'grok-4', 'grok-4-auto', 'grok-beta' // 保留旧名称以兼容 ], format: 'openai', // Grok 使用 OpenAI 兼容格式 }, // Claude (Anthropic) claude: { baseUrl: 'https://api.anthropic.com/v1', apiKey: cleanApiKey(process.env.CLAUDE_API_KEY), models: [ 'claude-opus-4-5', 'claude-sonnet-4-5', 'claude-haiku-4-20250514', 'claude-opus-4-1', 'claude-opus-4', 'claude-sonnet-4', 'claude-3-5-sonnet-20241022', 'claude-3-5-sonnet-20240620', 'claude-3-opus-20240229', 'claude-3-5-haiku-20241022', 'claude-3-sonnet-20240229', 'claude-3-haiku-20240307' ], format: 'claude', // 使用 Claude 格式 }, }; /** * 根据模型名称获取 API 配置 */ export function getApiConfigForModel(modelName) { // 检查每个 API 提供商的模型列表 for (const [provider, config] of Object.entries(API_CONFIGS)) { if (config.models.includes(modelName)) { return { ...config, provider }; } } // 默认使用 Gemini return { ...API_CONFIGS.gemini, provider: 'gemini' }; } /** * 构建 API 请求 */ export function buildApiRequest(modelName, systemPrompt, userPrompt, temperature = 0.7) { const config = getApiConfigForModel(modelName); if (config.format === 'gemini') { // Google Gemini 格式 - 使用 v1 API // v1 API 使用 models/{model}:generateContent 格式 if (!config.apiKey) { throw new Error('GEMINI_API_KEY not configured'); } const geminiModel = modelName.replace('-latest', ''); // 移除 -latest 后缀 return { url: `${config.baseUrl}/models/${geminiModel}:generateContent?key=${config.apiKey}`, headers: { 'Content-Type': 'application/json' }, body: { contents: [{ parts: [{ text: `${systemPrompt}\n\n${userPrompt}` }] }], generationConfig: { temperature: temperature, } } }; } else if (config.format === 'claude') { // Claude 格式 if (!config.apiKey) { throw new Error('CLAUDE_API_KEY not configured'); } return { url: `${config.baseUrl}/messages`, headers: { 'Content-Type': 'application/json', 'x-api-key': config.apiKey, 'anthropic-version': '2023-06-01', }, body: { model: modelName, max_tokens: 8192, temperature: temperature, messages: [ { role: 'user', content: `${systemPrompt}\n\n${userPrompt}` } ] } }; } else { // OpenAI 兼容格式 (OpenAI, Grok) // GPT-5 系列模型只支持 temperature=1(默认值) const isGPT5 = modelName.startsWith('gpt-5'); const requestBody = { model: modelName, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt }, ], }; // 只有非 GPT-5 模型才设置 temperature if (!isGPT5) { requestBody.temperature = temperature; } // 确保 API 密钥存在 if (!config.apiKey) { throw new Error(`API key not configured for ${config.provider || 'unknown provider'}`); } return { url: `${config.baseUrl}/chat/completions`, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.apiKey}`, }, body: requestBody }; } } /** * 解析 API 响应 */ export function parseApiResponse(responseJson, modelName) { const config = getApiConfigForModel(modelName); if (config.format === 'gemini') { // Google Gemini 响应格式 return responseJson.candidates?.[0]?.content?.parts?.[0]?.text || null; } else if (config.format === 'claude') { // Claude 响应格式 return responseJson.content?.[0]?.text || null; } else { // OpenAI 兼容格式 return responseJson.choices?.[0]?.message?.content || null; } } export default API_CONFIGS;