mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 20:12:39 +08:00
fix(antigravity): drop mixed web_search when client tools present
Antigravity v1internal rejects googleSearch/codeExecution mixed with functionDeclarations even with includeServerSideToolInvocations (#6464). Codex enables web_search by default alongside shell tools, which made Sub2API return opaque upstream_error. Prefer client function tools so agent sessions keep working; keep web_search-only fallback intact. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -88,11 +88,14 @@ func TransformClaudeToGeminiWithOptions(claudeReq *ClaudeRequest, projectID, map
|
||||
// 用于存储 tool_use id -> name 映射
|
||||
toolIDToName := make(map[string]string)
|
||||
|
||||
// 检测是否有 web_search 工具
|
||||
hasWebSearchTool := hasWebSearchTool(claudeReq.Tools)
|
||||
// 仅在「只有内置 web_search、没有客户端 function tools」时走 web_search 降级模型。
|
||||
// Antigravity v1internal 不支持内置工具与 functionDeclarations 混用(即使设置
|
||||
// includeServerSideToolInvocations 仍会 400,见 issue #6464),混用时会丢弃内置搜索,
|
||||
// 因此不能再强制切到 gemini-2.5-flash,否则 Codex 等带 shell 工具的请求会整单失败。
|
||||
useWebSearchRequest := hasWebSearchTool(claudeReq.Tools) && !hasClientFunctionTools(claudeReq.Tools)
|
||||
requestType := "agent"
|
||||
targetModel := mappedModel
|
||||
if hasWebSearchTool {
|
||||
if useWebSearchRequest {
|
||||
requestType = "web_search"
|
||||
if targetModel != webSearchFallbackModel {
|
||||
targetModel = webSearchFallbackModel
|
||||
@@ -156,13 +159,6 @@ func TransformClaudeToGeminiWithOptions(claudeReq *ClaudeRequest, projectID, map
|
||||
Mode: "VALIDATED",
|
||||
},
|
||||
}
|
||||
// 内置工具(googleSearch)与函数调用混用时,上游要求显式开启
|
||||
// includeServerSideToolInvocations,否则返回 400(issue #5709)。
|
||||
// 与 raw 透传路的 enableMixedGeminiToolInvocations 注入保持同一语义。
|
||||
if hasMixedToolInvocations(tools) {
|
||||
enabled := true
|
||||
innerRequest.ToolConfig.IncludeServerSideToolInvocations = &enabled
|
||||
}
|
||||
}
|
||||
|
||||
if systemInstruction != nil {
|
||||
@@ -697,6 +693,20 @@ func hasWebSearchTool(tools []ClaudeTool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// hasClientFunctionTools 判断是否存在可转发的客户端 function/custom 工具。
|
||||
// 内置 web_search / code_execution 不算客户端工具。
|
||||
func hasClientFunctionTools(tools []ClaudeTool) bool {
|
||||
for _, tool := range tools {
|
||||
if isWebSearchTool(tool) || isCodeExecutionTool(tool) {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(tool.Name) != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isWebSearchTool(tool ClaudeTool) bool {
|
||||
if strings.HasPrefix(tool.Type, "web_search") || tool.Type == "google_search" {
|
||||
return true
|
||||
@@ -715,21 +725,6 @@ func isCodeExecutionTool(tool ClaudeTool) bool {
|
||||
return strings.TrimSpace(tool.Type) == "code_execution"
|
||||
}
|
||||
|
||||
// hasMixedToolInvocations 判断构建后的工具声明是否同时包含函数声明与内置工具
|
||||
// (googleSearch)。仅在两者并存时需要开启 includeServerSideToolInvocations。
|
||||
func hasMixedToolInvocations(declarations []GeminiToolDeclaration) bool {
|
||||
hasFunc, hasBuiltin := false, false
|
||||
for _, d := range declarations {
|
||||
if len(d.FunctionDeclarations) > 0 {
|
||||
hasFunc = true
|
||||
}
|
||||
if d.GoogleSearch != nil || d.CodeExecution != nil {
|
||||
hasBuiltin = true
|
||||
}
|
||||
}
|
||||
return hasFunc && hasBuiltin
|
||||
}
|
||||
|
||||
// buildTools 构建 tools
|
||||
func buildTools(tools []ClaudeTool) []GeminiToolDeclaration {
|
||||
if len(tools) == 0 {
|
||||
@@ -795,6 +790,18 @@ func buildTools(tools []ClaudeTool) []GeminiToolDeclaration {
|
||||
})
|
||||
}
|
||||
|
||||
// Antigravity v1internal 协议不支持内置工具与 functionDeclarations 混用:
|
||||
// 即便带上 includeServerSideToolInvocations 仍返回 400(issue #6464)。
|
||||
// Codex 默认同时带 web_search 与 shell 等客户端工具,优先保留客户端工具,
|
||||
// 使代理会话可继续,而不是整单 upstream_error。
|
||||
if len(funcDecls) > 0 {
|
||||
if hasWebSearch || hasCodeExecution {
|
||||
log.Printf("[antigravity] dropping built-in tools (web_search/code_execution) because client function tools are present; Antigravity v1internal rejects the mix")
|
||||
}
|
||||
hasWebSearch = false
|
||||
hasCodeExecution = false
|
||||
}
|
||||
|
||||
var declarations []GeminiToolDeclaration
|
||||
if len(funcDecls) > 0 {
|
||||
declarations = append(declarations, GeminiToolDeclaration{
|
||||
|
||||
@@ -555,18 +555,22 @@ func TestTransformClaudeToGeminiWithOptions_PreservesWebSearchAlongsideFunctions
|
||||
},
|
||||
}
|
||||
|
||||
body, err := TransformClaudeToGeminiWithOptions(claudeReq, "project-1", "gemini-2.5-flash", DefaultTransformOptions())
|
||||
body, err := TransformClaudeToGeminiWithOptions(claudeReq, "project-1", "claude-sonnet-4-6", DefaultTransformOptions())
|
||||
require.NoError(t, err)
|
||||
|
||||
var req V1InternalRequest
|
||||
require.NoError(t, json.Unmarshal(body, &req))
|
||||
require.Len(t, req.Request.Tools, 2)
|
||||
// Antigravity v1internal cannot mix googleSearch with functionDeclarations
|
||||
// (#6464). Prefer client tools so Codex-style sessions keep working.
|
||||
require.Equal(t, "agent", req.RequestType)
|
||||
require.Equal(t, "claude-sonnet-4-6", req.Model)
|
||||
require.Len(t, req.Request.Tools, 1)
|
||||
require.Len(t, req.Request.Tools[0].FunctionDeclarations, 1)
|
||||
require.Equal(t, "get_weather", req.Request.Tools[0].FunctionDeclarations[0].Name)
|
||||
require.NotNil(t, req.Request.Tools[1].GoogleSearch)
|
||||
require.Nil(t, req.Request.Tools[0].GoogleSearch)
|
||||
}
|
||||
|
||||
func TestGeminiToolConfig_IncludeServerSideToolInvocations(t *testing.T) {
|
||||
func TestGeminiToolConfig_DropsBuiltinsWhenClientFunctionsPresent(t *testing.T) {
|
||||
functionTool := ClaudeTool{
|
||||
Name: "get_weather",
|
||||
Description: "Get weather information",
|
||||
@@ -577,7 +581,7 @@ func TestGeminiToolConfig_IncludeServerSideToolInvocations(t *testing.T) {
|
||||
Name: "web_search",
|
||||
}
|
||||
|
||||
transform := func(t *testing.T, tools []ClaudeTool) (V1InternalRequest, string) {
|
||||
transform := func(t *testing.T, tools []ClaudeTool, mappedModel string) (V1InternalRequest, string) {
|
||||
t.Helper()
|
||||
body, err := TransformClaudeToGeminiWithOptions(&ClaudeRequest{
|
||||
Model: "claude-3-5-sonnet-latest",
|
||||
@@ -588,7 +592,7 @@ func TestGeminiToolConfig_IncludeServerSideToolInvocations(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Tools: tools,
|
||||
}, "project-1", "gemini-2.5-flash", DefaultTransformOptions())
|
||||
}, "project-1", mappedModel, DefaultTransformOptions())
|
||||
require.NoError(t, err)
|
||||
|
||||
var req V1InternalRequest
|
||||
@@ -596,27 +600,33 @@ func TestGeminiToolConfig_IncludeServerSideToolInvocations(t *testing.T) {
|
||||
return req, string(body)
|
||||
}
|
||||
|
||||
t.Run("mixed builtin and function tools enable server-side tool invocations", func(t *testing.T) {
|
||||
req, raw := transform(t, []ClaudeTool{functionTool, webSearchTool})
|
||||
t.Run("mixed builtin and function tools keep only client functions", func(t *testing.T) {
|
||||
req, raw := transform(t, []ClaudeTool{functionTool, webSearchTool}, "claude-sonnet-4-6")
|
||||
|
||||
require.NotNil(t, req.Request.ToolConfig)
|
||||
require.NotNil(t, req.Request.ToolConfig.IncludeServerSideToolInvocations)
|
||||
require.True(t, *req.Request.ToolConfig.IncludeServerSideToolInvocations)
|
||||
require.Contains(t, raw, `"includeServerSideToolInvocations":true`)
|
||||
require.Equal(t, "agent", req.RequestType)
|
||||
require.Equal(t, "claude-sonnet-4-6", req.Model)
|
||||
require.Len(t, req.Request.Tools, 1)
|
||||
require.Len(t, req.Request.Tools[0].FunctionDeclarations, 1)
|
||||
require.Nil(t, req.Request.ToolConfig.IncludeServerSideToolInvocations)
|
||||
require.NotContains(t, raw, "googleSearch")
|
||||
require.NotContains(t, raw, "includeServerSideToolInvocations")
|
||||
})
|
||||
|
||||
t.Run("function tools only leave the flag unset", func(t *testing.T) {
|
||||
req, raw := transform(t, []ClaudeTool{functionTool})
|
||||
req, raw := transform(t, []ClaudeTool{functionTool}, "gemini-2.5-flash")
|
||||
|
||||
require.NotNil(t, req.Request.ToolConfig)
|
||||
require.Nil(t, req.Request.ToolConfig.IncludeServerSideToolInvocations)
|
||||
require.NotContains(t, raw, "includeServerSideToolInvocations")
|
||||
})
|
||||
|
||||
t.Run("web search only leaves the flag unset", func(t *testing.T) {
|
||||
req, raw := transform(t, []ClaudeTool{webSearchTool})
|
||||
t.Run("web search only keeps googleSearch and fallback model", func(t *testing.T) {
|
||||
req, raw := transform(t, []ClaudeTool{webSearchTool}, "claude-sonnet-4-6")
|
||||
|
||||
require.NotNil(t, req.Request.ToolConfig)
|
||||
require.Equal(t, "web_search", req.RequestType)
|
||||
require.Equal(t, "gemini-2.5-flash", req.Model)
|
||||
require.Len(t, req.Request.Tools, 1)
|
||||
require.NotNil(t, req.Request.Tools[0].GoogleSearch)
|
||||
require.Nil(t, req.Request.ToolConfig.IncludeServerSideToolInvocations)
|
||||
require.NotContains(t, raw, "includeServerSideToolInvocations")
|
||||
})
|
||||
|
||||
@@ -286,35 +286,72 @@ func (s *AntigravityGatewayService) buildAntigravityCompatGeminiBody(
|
||||
return antigravity.TransformClaudeToGeminiWithOptions(claudeRequest, projectID, mappedModel, options)
|
||||
}
|
||||
|
||||
// enableMixedGeminiToolInvocations reconciles Antigravity v1internal tool payloads.
|
||||
//
|
||||
// Antigravity's cloudcode-pa v1internal endpoint rejects mixing built-in tools
|
||||
// (googleSearch / codeExecution) with client functionDeclarations — even when
|
||||
// includeServerSideToolInvocations is set (issue #6464). Prefer client function
|
||||
// tools (Codex/agent workflows) and drop the incompatible built-ins.
|
||||
func enableMixedGeminiToolInvocations(body []byte) ([]byte, error) {
|
||||
var request map[string]any
|
||||
if err := json.Unmarshal(body, &request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var hasGoogleSearch, hasFunctionDeclarations bool
|
||||
if tools, ok := request["tools"].([]any); ok {
|
||||
for _, rawTool := range tools {
|
||||
tool, ok := rawTool.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
_, hasSearch := tool["googleSearch"]
|
||||
declarations, hasFunctions := tool["functionDeclarations"].([]any)
|
||||
hasGoogleSearch = hasGoogleSearch || hasSearch
|
||||
hasFunctionDeclarations = hasFunctionDeclarations || hasFunctions && len(declarations) > 0
|
||||
}
|
||||
}
|
||||
if !hasGoogleSearch || !hasFunctionDeclarations {
|
||||
tools, ok := request["tools"].([]any)
|
||||
if !ok || len(tools) == 0 {
|
||||
return body, nil
|
||||
}
|
||||
|
||||
toolConfig, _ := request["toolConfig"].(map[string]any)
|
||||
if toolConfig == nil {
|
||||
toolConfig = make(map[string]any)
|
||||
request["toolConfig"] = toolConfig
|
||||
hasFunctionDeclarations := false
|
||||
for _, rawTool := range tools {
|
||||
tool, ok := rawTool.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
declarations, hasFunctions := tool["functionDeclarations"].([]any)
|
||||
if hasFunctions && len(declarations) > 0 {
|
||||
hasFunctionDeclarations = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasFunctionDeclarations {
|
||||
return body, nil
|
||||
}
|
||||
|
||||
filtered := make([]any, 0, len(tools))
|
||||
droppedBuiltin := false
|
||||
for _, rawTool := range tools {
|
||||
tool, ok := rawTool.(map[string]any)
|
||||
if !ok {
|
||||
filtered = append(filtered, rawTool)
|
||||
continue
|
||||
}
|
||||
if _, hasSearch := tool["googleSearch"]; hasSearch {
|
||||
delete(tool, "googleSearch")
|
||||
droppedBuiltin = true
|
||||
}
|
||||
if _, hasCodeExecution := tool["codeExecution"]; hasCodeExecution {
|
||||
delete(tool, "codeExecution")
|
||||
droppedBuiltin = true
|
||||
}
|
||||
if len(tool) == 0 {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, tool)
|
||||
}
|
||||
if !droppedBuiltin {
|
||||
return body, nil
|
||||
}
|
||||
|
||||
request["tools"] = filtered
|
||||
if toolConfig, ok := request["toolConfig"].(map[string]any); ok {
|
||||
delete(toolConfig, "includeServerSideToolInvocations")
|
||||
delete(toolConfig, "include_server_side_tool_invocations")
|
||||
if len(toolConfig) == 0 {
|
||||
delete(request, "toolConfig")
|
||||
}
|
||||
}
|
||||
toolConfig["includeServerSideToolInvocations"] = true
|
||||
return json.Marshal(request)
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ func newAntigravityCompatAccount(accountType string) *Account {
|
||||
"model_mapping": map[string]any{
|
||||
"gemini-3.1-pro-high": "gemini-3.1-pro-high",
|
||||
"claude-sonnet-4-5": "claude-sonnet-4-5",
|
||||
"claude-sonnet-4-6": "claude-sonnet-4-6",
|
||||
"claude-opus-4-6-thinking": "claude-opus-4-6-thinking",
|
||||
},
|
||||
},
|
||||
@@ -228,22 +229,27 @@ func TestAntigravityCompatRejectsUnsupportedAccountType(t *testing.T) {
|
||||
func TestBuildAntigravityCompatGeminiBody_ConfiguresMixedToolInvocations(t *testing.T) {
|
||||
svc := &AntigravityGatewayService{}
|
||||
tests := []struct {
|
||||
name string
|
||||
tools string
|
||||
wantField bool
|
||||
name string
|
||||
tools string
|
||||
wantFuncs bool
|
||||
wantGoogle bool
|
||||
wantServerFlag bool
|
||||
}{
|
||||
{
|
||||
name: "mixed server and client tools",
|
||||
tools: `[{"name":"get_weather","input_schema":{"type":"object"}},{"type":"web_search_20250305","name":"web_search"}]`,
|
||||
wantField: true,
|
||||
name: "mixed server and client tools prefer client tools",
|
||||
tools: `[{"name":"get_weather","input_schema":{"type":"object"}},{"type":"web_search_20250305","name":"web_search"}]`,
|
||||
wantFuncs: true,
|
||||
wantGoogle: false,
|
||||
},
|
||||
{
|
||||
name: "client tools only",
|
||||
tools: `[{"name":"get_weather","input_schema":{"type":"object"}}]`,
|
||||
name: "client tools only",
|
||||
tools: `[{"name":"get_weather","input_schema":{"type":"object"}}]`,
|
||||
wantFuncs: true,
|
||||
},
|
||||
{
|
||||
name: "server tools only",
|
||||
tools: `[{"type":"web_search_20250305","name":"web_search"}]`,
|
||||
name: "server tools only",
|
||||
tools: `[{"type":"web_search_20250305","name":"web_search"}]`,
|
||||
wantGoogle: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -258,19 +264,38 @@ func TestBuildAntigravityCompatGeminiBody_ConfiguresMixedToolInvocations(t *test
|
||||
require.NoError(t, json.Unmarshal(body, &wrapped))
|
||||
request, ok := wrapped["request"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
|
||||
tools, _ := request["tools"].([]any)
|
||||
hasFuncs, hasGoogle := false, false
|
||||
for _, raw := range tools {
|
||||
tool, ok := raw.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if decls, ok := tool["functionDeclarations"].([]any); ok && len(decls) > 0 {
|
||||
hasFuncs = true
|
||||
}
|
||||
if _, ok := tool["googleSearch"]; ok {
|
||||
hasGoogle = true
|
||||
}
|
||||
}
|
||||
require.Equal(t, tt.wantFuncs, hasFuncs)
|
||||
require.Equal(t, tt.wantGoogle, hasGoogle)
|
||||
|
||||
toolConfig, exists := request["toolConfig"].(map[string]any)
|
||||
if !tt.wantField {
|
||||
require.False(t, exists)
|
||||
if !tt.wantServerFlag {
|
||||
if exists {
|
||||
require.NotContains(t, toolConfig, "includeServerSideToolInvocations")
|
||||
}
|
||||
return
|
||||
}
|
||||
require.True(t, exists)
|
||||
require.Equal(t, true, toolConfig["includeServerSideToolInvocations"])
|
||||
require.NotContains(t, toolConfig, "include_server_side_tool_invocations")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAntigravityCompatChatMixedBuiltInToolsEnableServerSideInvocations(t *testing.T) {
|
||||
func TestAntigravityCompatChatMixedBuiltInToolsPreferClientFunctions(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
upstream := &queuedHTTPUpstreamStub{responses: []*http.Response{antigravityCompatSuccessResponse()}}
|
||||
svc := newAntigravityCompatService(config.GatewayConfig{MaxLineSize: defaultMaxLineSize}, upstream)
|
||||
@@ -293,10 +318,43 @@ func TestAntigravityCompatChatMixedBuiltInToolsEnableServerSideInvocations(t *te
|
||||
require.NotNil(t, result)
|
||||
require.Len(t, upstream.requestBodies, 1)
|
||||
requestBody := upstream.requestBodies[0]
|
||||
require.True(t, gjson.GetBytes(requestBody, "request.toolConfig.includeServerSideToolInvocations").Bool())
|
||||
require.False(t, gjson.GetBytes(requestBody, "request.toolConfig.includeServerSideToolInvocations").Exists())
|
||||
require.Len(t, gjson.GetBytes(requestBody, "request.tools.0.functionDeclarations").Array(), 2)
|
||||
require.True(t, gjson.GetBytes(requestBody, "request.tools.1.googleSearch").Exists())
|
||||
require.True(t, gjson.GetBytes(requestBody, "request.tools.2.codeExecution").Exists())
|
||||
// No built-in tool entries should remain once client functions are present.
|
||||
for _, tool := range gjson.GetBytes(requestBody, "request.tools").Array() {
|
||||
require.False(t, tool.Get("googleSearch").Exists())
|
||||
require.False(t, tool.Get("codeExecution").Exists())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAntigravityCompatResponsesCodexWebSearchMixedWithFunctionsDropsBuiltins(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
upstream := &queuedHTTPUpstreamStub{responses: []*http.Response{antigravityCompatSuccessResponse()}}
|
||||
svc := newAntigravityCompatService(config.GatewayConfig{MaxLineSize: defaultMaxLineSize}, upstream)
|
||||
body := []byte(`{
|
||||
"model":"claude-sonnet-4-6",
|
||||
"input":"Reply with exactly: pong",
|
||||
"stream":true,
|
||||
"tools":[
|
||||
{"type":"function","name":"shell","description":"Run a shell command","parameters":{"type":"object","properties":{"command":{"type":"array","items":{"type":"string"}}},"required":["command"],"additionalProperties":false},"strict":true},
|
||||
{"type":"web_search"}
|
||||
]
|
||||
}`)
|
||||
c, _ := newAntigravityCompatContext(http.MethodPost, "/v1/responses", body)
|
||||
|
||||
result, err := svc.ForwardAsResponses(context.Background(), c, newAntigravityCompatAccount(AccountTypeOAuth), body, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Len(t, upstream.requestBodies, 1)
|
||||
requestBody := upstream.requestBodies[0]
|
||||
require.Equal(t, "agent", gjson.GetBytes(requestBody, "requestType").String())
|
||||
require.NotEqual(t, "gemini-2.5-flash", gjson.GetBytes(requestBody, "model").String())
|
||||
require.True(t, gjson.GetBytes(requestBody, "request.tools.0.functionDeclarations.#(name==\"shell\")").Exists())
|
||||
for _, tool := range gjson.GetBytes(requestBody, "request.tools").Array() {
|
||||
require.False(t, tool.Get("googleSearch").Exists())
|
||||
}
|
||||
require.False(t, gjson.GetBytes(requestBody, "request.toolConfig.includeServerSideToolInvocations").Exists())
|
||||
}
|
||||
|
||||
func TestAntigravityCompatPreservesChatTokenLimit(t *testing.T) {
|
||||
|
||||
@@ -131,6 +131,11 @@ func (s *AntigravityGatewayService) ForwardGemini(ctx context.Context, c *gin.Co
|
||||
logger.LegacyPrintf("service.antigravity_gateway", "[Antigravity] Failed to clean schema: %v", err)
|
||||
}
|
||||
|
||||
// Antigravity v1internal rejects built-in + functionDeclarations mixes (#6464).
|
||||
if reconciled, err := enableMixedGeminiToolInvocations(injectedBody); err == nil {
|
||||
injectedBody = reconciled
|
||||
}
|
||||
|
||||
// 包装请求
|
||||
wrappedBody, err := s.wrapV1InternalRequest(projectID, mappedModel, injectedBody)
|
||||
if err != nil {
|
||||
|
||||
@@ -399,7 +399,7 @@ func TestAntigravityGatewayService_ForwardGemini_ImageUsesDefaultMappingAndOAuth
|
||||
require.Equal(t, []any{"TEXT", "IMAGE"}, generationConfig["responseModalities"])
|
||||
}
|
||||
|
||||
func TestAntigravityGatewayService_ForwardGemini_PreservesServerSideToolInvocationConfig(t *testing.T) {
|
||||
func TestAntigravityGatewayService_ForwardGemini_StripsBuiltinsWhenClientFunctionsPresent(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
body := []byte(`{"contents":[{"role":"user","parts":[{"text":"hello"}]}],"tools":[{"functionDeclarations":[{"name":"get_weather","parameters":{"type":"object","additionalProperties":false}}]},{"googleSearch":{}}],"toolConfig":{"includeServerSideToolInvocations":true}}`)
|
||||
writer := httptest.NewRecorder()
|
||||
@@ -431,10 +431,16 @@ func TestAntigravityGatewayService_ForwardGemini_PreservesServerSideToolInvocati
|
||||
require.NoError(t, json.Unmarshal(upstream.requestBodies[0], &wrapped))
|
||||
request, ok := wrapped["request"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
toolConfig, ok := request["toolConfig"].(map[string]any)
|
||||
tools, ok := request["tools"].([]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, true, toolConfig["includeServerSideToolInvocations"])
|
||||
require.NotContains(t, toolConfig, "include_server_side_tool_invocations")
|
||||
require.Len(t, tools, 1)
|
||||
tool, ok := tools[0].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Contains(t, tool, "functionDeclarations")
|
||||
require.NotContains(t, tool, "googleSearch")
|
||||
if toolConfig, exists := request["toolConfig"].(map[string]any); exists {
|
||||
require.NotContains(t, toolConfig, "includeServerSideToolInvocations")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAntigravityGatewayService_ForwardGemini_MissingProjectReturnsLocalError(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user