Merge pull request #6674 from wuji-labs/fix/antigravity-toolconfig-always-present

fix(antigravity): always set toolConfig for reasoning models without tools
This commit is contained in:
Wesley Liddick
2026-09-07 18:44:41 +08:00
committed by GitHub
2 changed files with 49 additions and 15 deletions
@@ -148,21 +148,20 @@ func TransformClaudeToGeminiWithOptions(claudeReq *ClaudeRequest, projectID, map
}
// 针对 Gemini Reasoning 模型(如 gemini-3.1-pro-high等)过滤强制空 ToolConfig
isReasoning := IsGeminiReasoningModel(targetModel)
if !isReasoning || len(tools) > 0 {
// 总是设置 toolConfig,与官方客户端一致
innerRequest.ToolConfig = &GeminiToolConfig{
FunctionCallingConfig: &GeminiFunctionCallingConfig{
Mode: "VALIDATED",
},
}
// 内置工具(googleSearch)与函数调用混用时,上游要求显式开启
// includeServerSideToolInvocations,否则返回 400(issue #5709)。
// 与 raw 透传路的 enableMixedGeminiToolInvocations 注入保持同一语义。
if hasMixedToolInvocations(tools) {
enabled := true
innerRequest.ToolConfig.IncludeServerSideToolInvocations = &enabled
}
// toolConfig must always be present: upstream rejects requests without it,
// including reasoning models called without any tools.
// 总是设置 toolConfig,与官方客户端一致
innerRequest.ToolConfig = &GeminiToolConfig{
FunctionCallingConfig: &GeminiFunctionCallingConfig{
Mode: "VALIDATED",
},
}
// 内置工具(googleSearch)与函数调用混用时,上游要求显式开启
// includeServerSideToolInvocations,否则返回 400(issue #5709)。
// 与 raw 透传路的 enableMixedGeminiToolInvocations 注入保持同一语义。
if hasMixedToolInvocations(tools) {
enabled := true
innerRequest.ToolConfig.IncludeServerSideToolInvocations = &enabled
}
if systemInstruction != nil {
@@ -621,3 +621,38 @@ func TestGeminiToolConfig_IncludeServerSideToolInvocations(t *testing.T) {
require.NotContains(t, raw, "includeServerSideToolInvocations")
})
}
// TestToolConfigAlwaysPresent ensures toolConfig is always emitted, including for
// reasoning models without any tools: upstream rejects requests that omit it.
func TestToolConfigAlwaysPresent(t *testing.T) {
cases := []struct {
name string
model string
tools []ClaudeTool
}{
{name: "reasoning model without tools", model: "gemini-3.1-pro-high"},
{name: "reasoning model with tools", model: "gemini-3.1-pro-high", tools: []ClaudeTool{{
Name: "web_search",
Description: "Search the web",
InputSchema: map[string]any{"type": "object", "properties": map[string]any{}},
}}},
{name: "non-reasoning model without tools", model: "gemini-3.1-pro"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
claudeReq := &ClaudeRequest{
Model: tc.model,
Messages: []ClaudeMessage{{Role: "user", Content: json.RawMessage(`"Hello"`)}},
Tools: tc.tools,
}
body, err := TransformClaudeToGeminiWithOptions(claudeReq, "project-1", tc.model, DefaultTransformOptions())
require.NoError(t, err)
var req V1InternalRequest
require.NoError(t, json.Unmarshal(body, &req))
require.NotNil(t, req.Request.ToolConfig, "toolConfig must be present")
require.NotNil(t, req.Request.ToolConfig.FunctionCallingConfig)
require.Equal(t, "VALIDATED", req.Request.ToolConfig.FunctionCallingConfig.Mode)
})
}
}