diff --git a/internal/runtime/executor/antigravity_executor.go b/internal/runtime/executor/antigravity_executor.go index 8b4e37e..6be5bf4 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 def1cfb..facd26e 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) }