From 15e676e9cd7250880e56e092976e8832abf82a67 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Wed, 31 Dec 2025 20:56:38 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix(upstream):=20=E6=94=AF=E6=8C=81=20Claud?= =?UTF-8?q?e=20custom=20=E7=B1=BB=E5=9E=8B=E5=B7=A5=E5=85=B7=20(MCP)=20?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ClaudeTool 结构体增加 Type 和 Custom 字段 - buildTools 函数支持从 custom 字段读取 input_schema - convertClaudeToolsToGeminiTools 函数支持 MCP 工具格式 - 修复 Antigravity upstream error 400: JSON schema invalid 修复 Issue 0.2: tools.X.custom.input_schema 验证错误 --- .../internal/pkg/antigravity/claude_types.go | 13 +++++++++- .../pkg/antigravity/request_transformer.go | 18 +++++++++++-- .../service/gemini_messages_compat_service.go | 26 ++++++++++++++++--- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/backend/internal/pkg/antigravity/claude_types.go b/backend/internal/pkg/antigravity/claude_types.go index 9cab4cea..f394d7e3 100644 --- a/backend/internal/pkg/antigravity/claude_types.go +++ b/backend/internal/pkg/antigravity/claude_types.go @@ -37,8 +37,19 @@ type ClaudeMetadata struct { } // ClaudeTool Claude 工具定义 +// 支持两种格式: +// 1. 标准格式: { "name": "...", "description": "...", "input_schema": {...} } +// 2. Custom 格式 (MCP): { "type": "custom", "name": "...", "custom": { "description": "...", "input_schema": {...} } } type ClaudeTool struct { - Name string `json:"name"` + Type string `json:"type,omitempty"` // "custom" 或空(标准格式) + Name string `json:"name"` + Description string `json:"description,omitempty"` // 标准格式使用 + InputSchema map[string]any `json:"input_schema,omitempty"` // 标准格式使用 + Custom *CustomToolSpec `json:"custom,omitempty"` // custom 格式使用 +} + +// CustomToolSpec MCP custom 工具规格 +type CustomToolSpec struct { Description string `json:"description,omitempty"` InputSchema map[string]any `json:"input_schema"` } diff --git a/backend/internal/pkg/antigravity/request_transformer.go b/backend/internal/pkg/antigravity/request_transformer.go index 2ff0ec02..51eb4299 100644 --- a/backend/internal/pkg/antigravity/request_transformer.go +++ b/backend/internal/pkg/antigravity/request_transformer.go @@ -379,12 +379,26 @@ func buildTools(tools []ClaudeTool) []GeminiToolDeclaration { // 普通工具 var funcDecls []GeminiFunctionDecl for _, tool := range tools { + var description string + var inputSchema map[string]any + + // 检查是否为 custom 类型工具 (MCP) + if tool.Type == "custom" && tool.Custom != nil { + // Custom 格式: 从 custom 字段获取 description 和 input_schema + description = tool.Custom.Description + inputSchema = tool.Custom.InputSchema + } else { + // 标准格式: 从顶层字段获取 + description = tool.Description + inputSchema = tool.InputSchema + } + // 清理 JSON Schema - params := cleanJSONSchema(tool.InputSchema) + params := cleanJSONSchema(inputSchema) funcDecls = append(funcDecls, GeminiFunctionDecl{ Name: tool.Name, - Description: tool.Description, + Description: description, Parameters: params, }) } diff --git a/backend/internal/service/gemini_messages_compat_service.go b/backend/internal/service/gemini_messages_compat_service.go index ee3ade16..e55d798a 100644 --- a/backend/internal/service/gemini_messages_compat_service.go +++ b/backend/internal/service/gemini_messages_compat_service.go @@ -2245,12 +2245,32 @@ func convertClaudeToolsToGeminiTools(tools any) []any { if !ok { continue } - name, _ := tm["name"].(string) - desc, _ := tm["description"].(string) - params := tm["input_schema"] + + var name, desc string + var params any + + // 检查是否为 custom 类型工具 (MCP) + toolType, _ := tm["type"].(string) + if toolType == "custom" { + // Custom 格式: 从 custom 字段获取 description 和 input_schema + custom, ok := tm["custom"].(map[string]any) + if !ok { + continue + } + name, _ = tm["name"].(string) + desc, _ = custom["description"].(string) + params = custom["input_schema"] + } else { + // 标准格式: 从顶层字段获取 + name, _ = tm["name"].(string) + desc, _ = tm["description"].(string) + params = tm["input_schema"] + } + if name == "" { continue } + funcDecls = append(funcDecls, map[string]any{ "name": name, "description": desc, From 35b768b71967740d30b9923fe7418f7eab7a4977 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Wed, 31 Dec 2025 21:35:41 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix(upstream):=20=E8=B7=B3=E8=BF=87=20Claud?= =?UTF-8?q?e=20=E6=A8=A1=E5=9E=8B=E6=97=A0=20signature=20=E7=9A=84=20think?= =?UTF-8?q?ing=20block?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - buildParts 函数检测 thinking block 的 signature - Claude 模型 (allowDummyThought=false) 时跳过无 signature 的 block - 记录警告日志以便调试 - Gemini 模型继续使用 dummy signature 兼容方案 修复 Issue 0.1: Claude thinking block signature 缺失错误 --- backend/internal/pkg/antigravity/request_transformer.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/internal/pkg/antigravity/request_transformer.go b/backend/internal/pkg/antigravity/request_transformer.go index 51eb4299..e5ab8ece 100644 --- a/backend/internal/pkg/antigravity/request_transformer.go +++ b/backend/internal/pkg/antigravity/request_transformer.go @@ -3,6 +3,7 @@ package antigravity import ( "encoding/json" "fmt" + "log" "strings" "github.com/google/uuid" @@ -205,6 +206,10 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu // 保留原有 signature(Claude 模型需要有效的 signature) if block.Signature != "" { part.ThoughtSignature = block.Signature + } else if !allowDummyThought { + // Claude 模型需要有效 signature,跳过无 signature 的 thinking block + log.Printf("Warning: skipping thinking block without signature for Claude model") + continue } parts = append(parts, part) From c1e25b7ecf745a97832b9a1cc8827d6e6123dc69 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Wed, 31 Dec 2025 21:44:56 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix(upstream):=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E8=BE=B9=E7=95=8C=E6=A3=80=E6=9F=A5=E5=92=8C=20thinking=20bloc?= =?UTF-8?q?k=20=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 基于 Gemini + Codex 审查结果的修复: 1. thinking block dummy signature 填充 - Gemini 模型现在会填充 dummyThoughtSignature - 与 tool_use 处理逻辑保持一致 2. 边界检查增强 - buildTools: 跳过空工具名称 - buildTools: 为 nil schema 提供默认值 - convertClaudeToolsToGeminiTools: 为 nil params 提供默认值 3. 防止下游 API 验证错误 - 确保所有工具都有有效的 parameters - 默认 schema: {type: 'object', properties: {}} 审查报告:Gemini 评分 95%, Codex 评分 8.2/10 --- .../pkg/antigravity/request_transformer.go | 17 +++++++++++++++++ .../service/gemini_messages_compat_service.go | 8 ++++++++ 2 files changed, 25 insertions(+) diff --git a/backend/internal/pkg/antigravity/request_transformer.go b/backend/internal/pkg/antigravity/request_transformer.go index e5ab8ece..e0b5b886 100644 --- a/backend/internal/pkg/antigravity/request_transformer.go +++ b/backend/internal/pkg/antigravity/request_transformer.go @@ -210,6 +210,9 @@ func buildParts(content json.RawMessage, toolIDToName map[string]string, allowDu // Claude 模型需要有效 signature,跳过无 signature 的 thinking block log.Printf("Warning: skipping thinking block without signature for Claude model") continue + } else { + // Gemini 模型使用 dummy signature + part.ThoughtSignature = dummyThoughtSignature } parts = append(parts, part) @@ -384,6 +387,12 @@ func buildTools(tools []ClaudeTool) []GeminiToolDeclaration { // 普通工具 var funcDecls []GeminiFunctionDecl for _, tool := range tools { + // 跳过无效工具名称 + if tool.Name == "" { + log.Printf("Warning: skipping tool with empty name") + continue + } + var description string var inputSchema map[string]any @@ -401,6 +410,14 @@ func buildTools(tools []ClaudeTool) []GeminiToolDeclaration { // 清理 JSON Schema params := cleanJSONSchema(inputSchema) + // 为 nil schema 提供默认值 + if params == nil { + params = map[string]any{ + "type": "OBJECT", + "properties": map[string]any{}, + } + } + funcDecls = append(funcDecls, GeminiFunctionDecl{ Name: tool.Name, Description: description, diff --git a/backend/internal/service/gemini_messages_compat_service.go b/backend/internal/service/gemini_messages_compat_service.go index e55d798a..a0bf1b6a 100644 --- a/backend/internal/service/gemini_messages_compat_service.go +++ b/backend/internal/service/gemini_messages_compat_service.go @@ -2271,6 +2271,14 @@ func convertClaudeToolsToGeminiTools(tools any) []any { continue } + // 为 nil params 提供默认值 + if params == nil { + params = map[string]any{ + "type": "object", + "properties": map[string]any{}, + } + } + funcDecls = append(funcDecls, map[string]any{ "name": name, "description": desc, From 8e55ee0e2ca9c5fd00e7afa5ded757bea43d2667 Mon Sep 17 00:00:00 2001 From: shaw Date: Wed, 31 Dec 2025 23:50:15 +0800 Subject: [PATCH 4/4] style: fix gofmt formatting in claude_types.go --- backend/internal/pkg/antigravity/claude_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/pkg/antigravity/claude_types.go b/backend/internal/pkg/antigravity/claude_types.go index f394d7e3..01b805cd 100644 --- a/backend/internal/pkg/antigravity/claude_types.go +++ b/backend/internal/pkg/antigravity/claude_types.go @@ -41,7 +41,7 @@ type ClaudeMetadata struct { // 1. 标准格式: { "name": "...", "description": "...", "input_schema": {...} } // 2. Custom 格式 (MCP): { "type": "custom", "name": "...", "custom": { "description": "...", "input_schema": {...} } } type ClaudeTool struct { - Type string `json:"type,omitempty"` // "custom" 或空(标准格式) + Type string `json:"type,omitempty"` // "custom" 或空(标准格式) Name string `json:"name"` Description string `json:"description,omitempty"` // 标准格式使用 InputSchema map[string]any `json:"input_schema,omitempty"` // 标准格式使用