From 4a0ba6ed63445d376060d3e83d97121dae3f7cbb Mon Sep 17 00:00:00 2001 From: shaw Date: Fri, 28 Nov 2025 16:20:26 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dgemini=20api=E8=B4=A6?= =?UTF-8?q?=E6=88=B7=E8=BD=AC=E5=8F=91=E7=9A=84=E4=BC=A0=E5=8F=82=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 +++++++++++ src/handlers/geminiHandlers.js | 50 ++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 30eef33d..0841f67e 100644 --- a/README.md +++ b/README.md @@ -504,6 +504,22 @@ Droid CLI 读取 `~/.factory/config.json`。可以在该文件中添加自定义 "api_key": "后台创建的API密钥", "provider": "openai", "max_tokens": 16384 + }, + { + "model_display_name": "Gemini-3-Pro [crs]", + "model": "gemini-3-pro-preview", + "base_url": "http://127.0.0.1:3000/droid/comm/v1/", + "api_key": "后台创建的API密钥", + "provider": "generic-chat-completion-api", + "max_tokens": 32000 + }, + { + "model_display_name": "GLM-4.6 [crs]", + "model": "glm-4.6", + "base_url": "http://127.0.0.1:3000/droid/comm/v1/", + "api_key": "后台创建的API密钥", + "provider": "generic-chat-completion-api", + "max_tokens": 32000 } ] } diff --git a/src/handlers/geminiHandlers.js b/src/handlers/geminiHandlers.js index 8acd4c86..6aa85746 100644 --- a/src/handlers/geminiHandlers.js +++ b/src/handlers/geminiHandlers.js @@ -109,6 +109,42 @@ function isReadableStream(value) { return value && typeof value.on === 'function' && typeof value.pipe === 'function' } +/** + * 清理 contents 中 functionResponse 不被标准 Gemini API 支持的字段 + * 标准 Gemini API (generativelanguage.googleapis.com) 的 functionResponse 只支持 name 和 response 字段,不支持 id 字段 + * 注意:此函数仅用于 API Key 账户,OAuth 账户使用的 Cloud Code Assist API 可能支持额外字段 + */ +function sanitizeFunctionResponsesForApiKey(contents) { + if (!contents || !Array.isArray(contents)) { + return contents + } + + return contents.map((content) => { + if (!content.parts || !Array.isArray(content.parts)) { + return content + } + + const sanitizedParts = content.parts.map((part) => { + if (part.functionResponse) { + // 只保留标准 Gemini API 支持的字段:name 和 response + const { name, response } = part.functionResponse + return { + functionResponse: { + name, + response + } + } + } + return part + }) + + return { + ...content, + parts: sanitizedParts + } + }) +} + /** * 读取可读流内容为字符串 */ @@ -351,7 +387,9 @@ async function handleMessages(req, res) { url: apiUrl, data: requestBody, headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'x-api-key': account.apiKey, + 'x-goog-api-key': account.apiKey }, responseType: stream ? 'stream' : 'json', signal: abortController.signal @@ -1659,6 +1697,9 @@ async function handleStandardGenerateContent(req, res) { }) } + // API Key 账户:清理 functionResponse 中标准 Gemini API 不支持的字段(如 id) + actualRequestData.contents = sanitizeFunctionResponsesForApiKey(actualRequestData.contents) + logger.info(`Standard Gemini API generateContent request (${version}) - API Key Account`, { model, accountId: actualAccountId, @@ -1910,6 +1951,9 @@ async function handleStandardStreamGenerateContent(req, res) { }) } + // API Key 账户:清理 functionResponse 中标准 Gemini API 不支持的字段(如 id) + actualRequestData.contents = sanitizeFunctionResponsesForApiKey(actualRequestData.contents) + logger.info( `Standard Gemini API streamGenerateContent request (${version}) - API Key Account`, { @@ -1956,7 +2000,9 @@ async function handleStandardStreamGenerateContent(req, res) { url: apiUrl, data: actualRequestData, headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'x-api-key': account.apiKey, + 'x-goog-api-key': account.apiKey }, responseType: 'stream', signal: abortController.signal