UNPKG

route-claudecode

Version:

Advanced routing and transformation system for Claude Code outputs to multiple AI providers

245 lines 9.77 kB
"use strict"; /** * Gemini格式解析器 * 处理Google Gemini API格式 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GeminiFormatParser = void 0; const base_parser_1 = require("./base-parser"); class GeminiFormatParser extends base_parser_1.BaseFormatParser { constructor() { super('gemini'); } /** * 检测是否为Gemini格式 */ canParse(data, context) { if (!data || typeof data !== 'object') { return false; } // Gemini格式特征: // 1. 有candidates数组 // 2. 没有choices数组(OpenAI特征) // 3. 没有content数组(Anthropic特征) const hasGeminiCandidates = data.candidates && Array.isArray(data.candidates); const hasOpenAIChoices = data.choices && Array.isArray(data.choices); const hasAnthropicContent = data.content && Array.isArray(data.content); const isGemini = hasGeminiCandidates && !hasOpenAIChoices && !hasAnthropicContent; console.log(`🔍 [GEMINI] Format detection:`, { hasGeminiCandidates, hasOpenAIChoices, hasAnthropicContent, isGemini, requestId: context.requestId }); return isGemini; } /** * 解析Gemini格式的工具调用 */ parseToolCalls(data, context) { const toolCalls = []; let toolCount = 0; let confidence = 0.8; // Gemini格式置信度中等 if (!data.candidates || !Array.isArray(data.candidates) || data.candidates.length === 0) { const result = { hasTools: false, toolCount: 0, toolCalls: [], confidence: 1.0 }; this.validateParseResult(result, context); this.logParseResult(result, context); return result; } // 解析candidates数组中的工具调用 for (const candidate of data.candidates) { if (!candidate || typeof candidate !== 'object') { continue; } // 检查content中的parts if (candidate.content && candidate.content.parts && Array.isArray(candidate.content.parts)) { for (const part of candidate.content.parts) { if (!part || typeof part !== 'object') { continue; } // 检查functionCall if (part.functionCall) { const parsedToolCall = this.parseGeminiFunctionCall(part.functionCall, context); if (parsedToolCall) { toolCalls.push(parsedToolCall); toolCount++; } } // 检查function_call(兼容格式) if (part.function_call) { const parsedToolCall = this.parseGeminiFunctionCall(part.function_call, context); if (parsedToolCall) { toolCalls.push(parsedToolCall); toolCount++; } } // 检查text中的工具调用 if (part.text && typeof part.text === 'string') { const textToolCalls = this.parseTextToolCalls(part.text, context); toolCalls.push(...textToolCalls); toolCount += textToolCalls.length; if (textToolCalls.length > 0) { confidence = Math.min(confidence, 0.5); // 文本解析置信度较低 } } } } } const result = { hasTools: toolCount > 0, toolCount, toolCalls, finishReason: this.getFinishReason(data, context), confidence }; this.validateParseResult(result, context); this.logParseResult(result, context); return result; } /** * 解析Gemini functionCall对象 */ parseGeminiFunctionCall(functionCall, context) { if (!functionCall || typeof functionCall !== 'object') { return null; } if (!functionCall.name) { console.warn(`🚨 [GEMINI] Function call missing name`, { requestId: context.requestId }); return null; } let input = {}; if (functionCall.args) { // Gemini使用args而不是arguments input = typeof functionCall.args === 'object' ? functionCall.args : {}; } const result = { id: `gemini_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, name: functionCall.name, input, type: 'function_call' }; try { this.validateToolCall(result, context); return result; } catch (error) { console.warn(`🚨 [GEMINI] Function call validation failed`, { functionCall: result, error: error instanceof Error ? error.message : String(error), requestId: context.requestId }); return null; } } /** * 从文本中解析工具调用(处理不规范格式) */ parseTextToolCalls(text, context) { const toolCalls = []; if (!text || typeof text !== 'string') { return toolCalls; } // Gemini特有的工具调用模式 const patterns = [ // Gemini函数调用格式 /\{\s*"name"\s*:\s*"([^"]+)"\s*,\s*"args"\s*:\s*(\{[^}]*\})\s*\}/gi, // 标准JSON格式 /\{\s*"functionCall"\s*:\s*\{\s*"name"\s*:\s*"([^"]+)"\s*,\s*"args"\s*:\s*(\{[^}]*\})\s*\}\s*\}/gi ]; for (const pattern of patterns) { let match; while ((match = pattern.exec(text)) !== null) { try { const name = match[1]; const input = JSON.parse(match[2]); const toolCall = { id: `gemini_text_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, name, input, type: 'function_call' }; this.validateToolCall(toolCall, context); toolCalls.push(toolCall); } catch (error) { console.warn(`🚨 [GEMINI] Failed to parse text tool call: ${match[0]}`, { error: error instanceof Error ? error.message : String(error), requestId: context.requestId }); // 不抛出错误,继续解析其他工具调用 } } } return toolCalls; } /** * 获取Gemini格式的finish reason */ getFinishReason(data, context) { if (!data || typeof data !== 'object') { return undefined; } if (!data.candidates || !Array.isArray(data.candidates) || data.candidates.length === 0) { return undefined; } const candidate = data.candidates[0]; // Gemini使用finishReason字段 const finishReason = candidate.finishReason; if (finishReason === undefined || finishReason === null) { return undefined; } if (typeof finishReason !== 'string') { throw new Error(`Gemini finishReason must be string, got ${typeof finishReason}`); } // 检查fallback值 if (finishReason === 'unknown' || finishReason === 'default') { throw new Error(`Gemini finishReason has fallback value: ${finishReason} - violates zero fallback principle`); } console.log(`🎯 [GEMINI] Finish reason: ${finishReason}`, { requestId: context.requestId }); return finishReason; } /** * 修复Gemini格式的finish reason */ fixFinishReason(data, targetReason, context) { if (!data || typeof data !== 'object') { throw new Error('Gemini data is null or not object - cannot fix finish reason'); } if (!targetReason) { throw new Error('Target reason is required - violates zero fallback principle'); } // 检查目标值是否为fallback if (targetReason === 'unknown' || targetReason === 'default') { throw new Error(`Target reason is fallback value: ${targetReason} - violates zero fallback principle`); } if (!data.candidates || !Array.isArray(data.candidates)) { throw new Error('Gemini data missing candidates array - cannot fix finish reason'); } // Gemini的finish reason映射 let mappedReason = targetReason; if (targetReason === 'tool_calls' || targetReason === 'tool_use') { mappedReason = 'STOP'; // Gemini使用STOP表示完成 } // 修复所有candidates中的finishReason for (const candidate of data.candidates) { if (candidate && typeof candidate === 'object') { const originalReason = candidate.finishReason; candidate.finishReason = mappedReason; console.log(`🔧 [GEMINI] Fixed finish reason: ${originalReason} -> ${mappedReason}`, { requestId: context.requestId }); } } return data; } } exports.GeminiFormatParser = GeminiFormatParser; //# sourceMappingURL=gemini-parser.js.map