From ae10e9e365128fcd56da9721dc786264f6476c9b Mon Sep 17 00:00:00 2001 From: hex2077 Date: Wed, 4 Feb 2026 18:36:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(GeminiConverter):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=BB=8EGemini=E5=93=8D=E5=BA=94=E4=B8=AD=E6=8F=90=E5=8F=96too?= =?UTF-8?q?l=5Fcalls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在toOpenAIResponse方法中添加对Gemini响应中functionCall部分的解析,将其转换为OpenAI格式的tool_calls。当检测到工具调用时,自动设置finish_reason为"tool_calls",并构建包含tool_calls字段的assistant消息。 --- src/converters/strategies/GeminiConverter.js | 49 +++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/src/converters/strategies/GeminiConverter.js b/src/converters/strategies/GeminiConverter.js index a8c2753..456c92c 100644 --- a/src/converters/strategies/GeminiConverter.js +++ b/src/converters/strategies/GeminiConverter.js @@ -284,6 +284,46 @@ export class GeminiConverter extends BaseConverter { toOpenAIResponse(geminiResponse, model) { const content = this.processGeminiResponseContent(geminiResponse); + // 提取 tool_calls + const toolCalls = []; + let finishReason = "stop"; + + if (geminiResponse && geminiResponse.candidates) { + for (const candidate of geminiResponse.candidates) { + if (candidate.content && candidate.content.parts) { + for (const part of candidate.content.parts) { + if (part.functionCall) { + toolCalls.push({ + id: part.functionCall.id || `call_${uuidv4()}`, + type: 'function', + function: { + name: part.functionCall.name, + arguments: typeof part.functionCall.args === 'string' + ? part.functionCall.args + : JSON.stringify(part.functionCall.args) + } + }); + } + } + } + } + } + + // 如果有工具调用,设置 finish_reason 为 tool_calls + if (toolCalls.length > 0) { + finishReason = "tool_calls"; + } + + const message = { + role: "assistant", + content: content + }; + + // 只有在有 tool_calls 时才添加该字段 + if (toolCalls.length > 0) { + message.tool_calls = toolCalls; + } + return { id: `chatcmpl-${uuidv4()}`, object: "chat.completion", @@ -291,11 +331,8 @@ export class GeminiConverter extends BaseConverter { model: model, choices: [{ index: 0, - message: { - role: "assistant", - content: content - }, - finish_reason: "stop", + message: message, + finish_reason: finishReason, }], usage: geminiResponse.usageMetadata ? { prompt_tokens: geminiResponse.usageMetadata.promptTokenCount || 0, @@ -318,7 +355,7 @@ export class GeminiConverter extends BaseConverter { }, completion_tokens_details: { reasoning_tokens: 0 - } + } }, }; }