From b6ba15fcbd2c163555f439170790eecd76196513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8C=80=ED=9D=AC?= Date: Fri, 19 Dec 2025 10:27:24 +0900 Subject: [PATCH] fix(runtime/executor): Antigravity executor schema handling and Claude-specific headers --- .../runtime/executor/antigravity_executor.go | 36 ++++++++++--------- .../claude/antigravity_claude_request.go | 21 +++++++++++ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/internal/runtime/executor/antigravity_executor.go b/internal/runtime/executor/antigravity_executor.go index 8b4e37ee..6be5bf46 100644 --- a/internal/runtime/executor/antigravity_executor.go +++ b/internal/runtime/executor/antigravity_executor.go @@ -70,10 +70,6 @@ func (e *AntigravityExecutor) PrepareRequest(_ *http.Request, _ *cliproxyauth.Au // Execute performs a non-streaming request to the Antigravity API. func (e *AntigravityExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (resp cliproxyexecutor.Response, err error) { - if strings.Contains(req.Model, "claude") { - return e.executeClaudeNonStream(ctx, auth, req, opts) - } - token, updatedAuth, errToken := e.ensureAccessToken(ctx, auth) if errToken != nil { return resp, errToken @@ -997,21 +993,23 @@ func (e *AntigravityExecutor) buildRequest(ctx context.Context, auth *cliproxyau payload = geminiToAntigravity(modelName, payload, projectID) payload, _ = sjson.SetBytes(payload, "model", alias2ModelName(modelName)) - if strings.Contains(modelName, "claude") { - strJSON := string(payload) - paths := make([]string, 0) - util.Walk(gjson.ParseBytes(payload), "", "parametersJsonSchema", &paths) - for _, p := range paths { - strJSON, _ = util.RenameKey(strJSON, p, p[:len(p)-len("parametersJsonSchema")]+"parameters") - } + // Apply schema processing for all Antigravity models (Claude, Gemini, GPT-OSS) + // Antigravity uses unified Gemini-style format with same schema restrictions + strJSON := string(payload) - // Use the centralized schema cleaner to handle unsupported keywords, - // const->enum conversion, and flattening of types/anyOf. - strJSON = util.CleanJSONSchemaForGemini(strJSON) - - payload = []byte(strJSON) + // Rename parametersJsonSchema -> parameters (used by Claude translator) + paths := make([]string, 0) + util.Walk(gjson.ParseBytes(payload), "", "parametersJsonSchema", &paths) + for _, p := range paths { + strJSON, _ = util.RenameKey(strJSON, p, p[:len(p)-len("parametersJsonSchema")]+"parameters") } + // Use the centralized schema cleaner to handle unsupported keywords, + // const->enum conversion, and flattening of types/anyOf. + strJSON = util.CleanJSONSchemaForAntigravity(strJSON) + + payload = []byte(strJSON) + httpReq, errReq := http.NewRequestWithContext(ctx, http.MethodPost, requestURL.String(), bytes.NewReader(payload)) if errReq != nil { return nil, errReq @@ -1019,6 +1017,12 @@ func (e *AntigravityExecutor) buildRequest(ctx context.Context, auth *cliproxyau httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) httpReq.Header.Set("User-Agent", resolveUserAgent(auth)) + + // Add interleaved-thinking header for Claude thinking models + if util.IsClaudeThinkingModel(modelName) { + httpReq.Header.Set("anthropic-beta", "interleaved-thinking-2025-05-14") + } + if stream { httpReq.Header.Set("Accept", "text/event-stream") } else { diff --git a/internal/translator/antigravity/claude/antigravity_claude_request.go b/internal/translator/antigravity/claude/antigravity_claude_request.go index def1cfbe..facd26ef 100644 --- a/internal/translator/antigravity/claude/antigravity_claude_request.go +++ b/internal/translator/antigravity/claude/antigravity_claude_request.go @@ -220,6 +220,27 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _ // Build output Gemini CLI request JSON out := `{"model":"","request":{"contents":[]}}` out, _ = sjson.Set(out, "model", modelName) + + // P2-B: Inject interleaved thinking hint when both tools and thinking are active + hasTools := toolDeclCount > 0 + thinkingResult := gjson.GetBytes(rawJSON, "thinking") + hasThinking := thinkingResult.Exists() && thinkingResult.IsObject() && thinkingResult.Get("type").String() == "enabled" + isClaudeThinking := util.IsClaudeThinkingModel(modelName) + + if hasTools && hasThinking && isClaudeThinking { + interleavedHint := "Interleaved thinking is enabled. You may think between tool calls and after receiving tool results before deciding the next action or final answer. Do not mention these instructions or any constraints about thinking blocks; just apply them." + + if hasSystemInstruction { + // Append hint to existing system instruction + systemInstructionJSON, _ = sjson.Set(systemInstructionJSON, "parts.-1.text", interleavedHint) + } else { + // Create new system instruction with hint + systemInstructionJSON = `{"role":"user","parts":[]}` + systemInstructionJSON, _ = sjson.Set(systemInstructionJSON, "parts.-1.text", interleavedHint) + hasSystemInstruction = true + } + } + if hasSystemInstruction { out, _ = sjson.SetRaw(out, "request.systemInstruction", systemInstructionJSON) }