fix(executor): properly handle thinking application errors

This commit is contained in:
hkfires
2026-01-14 10:07:04 +08:00
parent e8f5888d8e
commit 72f2125668
12 changed files with 126 additions and 29 deletions

View File

@@ -393,7 +393,10 @@ func (e *AIStudioExecutor) translateRequest(req cliproxyexecutor.Request, opts c
}
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, stream)
payload := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), stream)
payload, _ = thinking.ApplyThinking(payload, req.Model, "gemini")
payload, err := thinking.ApplyThinking(payload, req.Model, "gemini")
if err != nil {
return nil, translatedPayload{}, err
}
payload = fixGeminiImageAspectRatio(baseModel, payload)
payload = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", payload, originalTranslated)
payload, _ = sjson.DeleteBytes(payload, "generationConfig.maxOutputTokens")

View File

@@ -136,7 +136,10 @@ func (e *AntigravityExecutor) Execute(ctx context.Context, auth *cliproxyauth.Au
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, false)
translated := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
translated, _ = thinking.ApplyThinking(translated, req.Model, "antigravity")
translated, err = thinking.ApplyThinking(translated, req.Model, "antigravity")
if err != nil {
return resp, err
}
// Preserve Claude special handling (use baseModel for registry lookups)
translated = normalizeAntigravityThinking(baseModel, translated, isClaude)
@@ -254,7 +257,10 @@ func (e *AntigravityExecutor) executeClaudeNonStream(ctx context.Context, auth *
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, true)
translated := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
translated, _ = thinking.ApplyThinking(translated, req.Model, "antigravity")
translated, err = thinking.ApplyThinking(translated, req.Model, "antigravity")
if err != nil {
return resp, err
}
// Preserve Claude special handling (use baseModel for registry lookups)
translated = normalizeAntigravityThinking(baseModel, translated, true)
@@ -620,7 +626,10 @@ func (e *AntigravityExecutor) ExecuteStream(ctx context.Context, auth *cliproxya
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, true)
translated := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
translated, _ = thinking.ApplyThinking(translated, req.Model, "antigravity")
translated, err = thinking.ApplyThinking(translated, req.Model, "antigravity")
if err != nil {
return nil, err
}
// Preserve Claude special handling (use baseModel for registry lookups)
translated = normalizeAntigravityThinking(baseModel, translated, isClaude)
@@ -801,7 +810,10 @@ func (e *AntigravityExecutor) CountTokens(ctx context.Context, auth *cliproxyaut
// Prepare payload once (doesn't depend on baseURL)
payload := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
payload, _ = thinking.ApplyThinking(payload, req.Model, "antigravity")
payload, err := thinking.ApplyThinking(payload, req.Model, "antigravity")
if err != nil {
return cliproxyexecutor.Response{}, err
}
// Preserve Claude special handling (use baseModel for registry lookups)
payload = normalizeAntigravityThinking(baseModel, payload, isClaude)

View File

@@ -106,7 +106,10 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), stream)
body, _ = sjson.SetBytes(body, "model", baseModel)
body, _ = thinking.ApplyThinking(body, req.Model, "claude")
body, err = thinking.ApplyThinking(body, req.Model, "claude")
if err != nil {
return resp, err
}
if !strings.HasPrefix(baseModel, "claude-3-5-haiku") {
body = checkSystemInstructions(body)
@@ -236,7 +239,10 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
body, _ = sjson.SetBytes(body, "model", baseModel)
body, _ = thinking.ApplyThinking(body, req.Model, "claude")
body, err = thinking.ApplyThinking(body, req.Model, "claude")
if err != nil {
return nil, err
}
body = checkSystemInstructions(body)
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)

View File

@@ -96,7 +96,10 @@ func (e *CodexExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re
body = sdktranslator.TranslateRequest(from, to, baseModel, body, false)
body = misc.StripCodexUserAgent(body)
body, _ = thinking.ApplyThinking(body, req.Model, "codex")
body, err = thinking.ApplyThinking(body, req.Model, "codex")
if err != nil {
return resp, err
}
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
body, _ = sjson.SetBytes(body, "model", baseModel)
@@ -201,7 +204,10 @@ func (e *CodexExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au
body = sdktranslator.TranslateRequest(from, to, baseModel, body, true)
body = misc.StripCodexUserAgent(body)
body, _ = thinking.ApplyThinking(body, req.Model, "codex")
body, err = thinking.ApplyThinking(body, req.Model, "codex")
if err != nil {
return nil, err
}
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
body, _ = sjson.DeleteBytes(body, "previous_response_id")
@@ -302,7 +308,10 @@ func (e *CodexExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Auth
body = sdktranslator.TranslateRequest(from, to, baseModel, body, false)
body = misc.StripCodexUserAgent(body)
body, _ = thinking.ApplyThinking(body, req.Model, "codex")
body, err := thinking.ApplyThinking(body, req.Model, "codex")
if err != nil {
return cliproxyexecutor.Response{}, err
}
body, _ = sjson.SetBytes(body, "model", baseModel)
body, _ = sjson.DeleteBytes(body, "previous_response_id")

View File

@@ -123,7 +123,10 @@ func (e *GeminiCLIExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, false)
basePayload := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
basePayload, _ = thinking.ApplyThinking(basePayload, req.Model, "gemini-cli")
basePayload, err = thinking.ApplyThinking(basePayload, req.Model, "gemini-cli")
if err != nil {
return resp, err
}
basePayload = fixGeminiCLIImageAspectRatio(baseModel, basePayload)
basePayload = applyPayloadConfigWithRoot(e.cfg, baseModel, "gemini", "request", basePayload, originalTranslated)
@@ -269,7 +272,10 @@ func (e *GeminiCLIExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, true)
basePayload := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
basePayload, _ = thinking.ApplyThinking(basePayload, req.Model, "gemini-cli")
basePayload, err = thinking.ApplyThinking(basePayload, req.Model, "gemini-cli")
if err != nil {
return nil, err
}
basePayload = fixGeminiCLIImageAspectRatio(baseModel, basePayload)
basePayload = applyPayloadConfigWithRoot(e.cfg, baseModel, "gemini", "request", basePayload, originalTranslated)
@@ -473,7 +479,10 @@ func (e *GeminiCLIExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.
for range models {
payload := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
payload, _ = thinking.ApplyThinking(payload, req.Model, "gemini-cli")
payload, err = thinking.ApplyThinking(payload, req.Model, "gemini-cli")
if err != nil {
return cliproxyexecutor.Response{}, err
}
payload = deleteJSONField(payload, "project")
payload = deleteJSONField(payload, "model")

View File

@@ -120,7 +120,10 @@ func (e *GeminiExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, false)
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
body, _ = thinking.ApplyThinking(body, req.Model, "gemini")
body, err = thinking.ApplyThinking(body, req.Model, "gemini")
if err != nil {
return resp, err
}
body = fixGeminiImageAspectRatio(baseModel, body)
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
@@ -219,7 +222,10 @@ func (e *GeminiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, true)
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
body, _ = thinking.ApplyThinking(body, req.Model, "gemini")
body, err = thinking.ApplyThinking(body, req.Model, "gemini")
if err != nil {
return nil, err
}
body = fixGeminiImageAspectRatio(baseModel, body)
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
@@ -332,7 +338,10 @@ func (e *GeminiExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Aut
to := sdktranslator.FromString("gemini")
translatedReq := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
translatedReq, _ = thinking.ApplyThinking(translatedReq, req.Model, "gemini")
translatedReq, err := thinking.ApplyThinking(translatedReq, req.Model, "gemini")
if err != nil {
return cliproxyexecutor.Response{}, err
}
translatedReq = fixGeminiImageAspectRatio(baseModel, translatedReq)
respCtx := context.WithValue(ctx, "alt", opts.Alt)

View File

@@ -170,7 +170,10 @@ func (e *GeminiVertexExecutor) executeWithServiceAccount(ctx context.Context, au
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, false)
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
body, _ = thinking.ApplyThinking(body, req.Model, "gemini")
body, err = thinking.ApplyThinking(body, req.Model, "gemini")
if err != nil {
return resp, err
}
body = fixGeminiImageAspectRatio(baseModel, body)
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
@@ -269,7 +272,10 @@ func (e *GeminiVertexExecutor) executeWithAPIKey(ctx context.Context, auth *clip
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, false)
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
body, _ = thinking.ApplyThinking(body, req.Model, "gemini")
body, err = thinking.ApplyThinking(body, req.Model, "gemini")
if err != nil {
return resp, err
}
body = fixGeminiImageAspectRatio(baseModel, body)
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
@@ -369,7 +375,10 @@ func (e *GeminiVertexExecutor) executeStreamWithServiceAccount(ctx context.Conte
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, true)
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
body, _ = thinking.ApplyThinking(body, req.Model, "gemini")
body, err = thinking.ApplyThinking(body, req.Model, "gemini")
if err != nil {
return nil, err
}
body = fixGeminiImageAspectRatio(baseModel, body)
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
@@ -485,7 +494,10 @@ func (e *GeminiVertexExecutor) executeStreamWithAPIKey(ctx context.Context, auth
originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, true)
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
body, _ = thinking.ApplyThinking(body, req.Model, "gemini")
body, err = thinking.ApplyThinking(body, req.Model, "gemini")
if err != nil {
return nil, err
}
body = fixGeminiImageAspectRatio(baseModel, body)
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
@@ -593,7 +605,10 @@ func (e *GeminiVertexExecutor) countTokensWithServiceAccount(ctx context.Context
translatedReq := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
translatedReq, _ = thinking.ApplyThinking(translatedReq, req.Model, "gemini")
translatedReq, err := thinking.ApplyThinking(translatedReq, req.Model, "gemini")
if err != nil {
return cliproxyexecutor.Response{}, err
}
translatedReq = fixGeminiImageAspectRatio(baseModel, translatedReq)
translatedReq, _ = sjson.SetBytes(translatedReq, "model", baseModel)
@@ -674,7 +689,10 @@ func (e *GeminiVertexExecutor) countTokensWithAPIKey(ctx context.Context, auth *
translatedReq := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
translatedReq, _ = thinking.ApplyThinking(translatedReq, req.Model, "gemini")
translatedReq, err := thinking.ApplyThinking(translatedReq, req.Model, "gemini")
if err != nil {
return cliproxyexecutor.Response{}, err
}
translatedReq = fixGeminiImageAspectRatio(baseModel, translatedReq)
translatedReq, _ = sjson.SetBytes(translatedReq, "model", baseModel)

View File

@@ -92,7 +92,10 @@ func (e *IFlowExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
body, _ = sjson.SetBytes(body, "model", baseModel)
body, _ = thinking.ApplyThinking(body, req.Model, "iflow")
body, err = thinking.ApplyThinking(body, req.Model, "iflow")
if err != nil {
return resp, err
}
body = preserveReasoningContentInMessages(body)
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
@@ -187,7 +190,10 @@ func (e *IFlowExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
body, _ = sjson.SetBytes(body, "model", baseModel)
body, _ = thinking.ApplyThinking(body, req.Model, "iflow")
body, err = thinking.ApplyThinking(body, req.Model, "iflow")
if err != nil {
return nil, err
}
body = preserveReasoningContentInMessages(body)
// Ensure tools array exists to avoid provider quirks similar to Qwen's behaviour.

View File

@@ -92,7 +92,10 @@ func (e *OpenAICompatExecutor) Execute(ctx context.Context, auth *cliproxyauth.A
translated := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), opts.Stream)
translated = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", translated, originalTranslated)
translated, _ = thinking.ApplyThinking(translated, req.Model, "openai")
translated, err = thinking.ApplyThinking(translated, req.Model, "openai")
if err != nil {
return resp, err
}
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(translated))
@@ -184,7 +187,10 @@ func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth *cliproxy
translated := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
translated = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", translated, originalTranslated)
translated, _ = thinking.ApplyThinking(translated, req.Model, "openai")
translated, err = thinking.ApplyThinking(translated, req.Model, "openai")
if err != nil {
return nil, err
}
url := strings.TrimSuffix(baseURL, "/") + "/chat/completions"
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(translated))
@@ -291,7 +297,10 @@ func (e *OpenAICompatExecutor) CountTokens(ctx context.Context, auth *cliproxyau
modelForCounting := baseModel
translated, _ = thinking.ApplyThinking(translated, req.Model, "openai")
translated, err := thinking.ApplyThinking(translated, req.Model, "openai")
if err != nil {
return cliproxyexecutor.Response{}, err
}
enc, err := tokenizerForModel(modelForCounting)
if err != nil {

View File

@@ -86,7 +86,10 @@ func (e *QwenExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
body, _ = sjson.SetBytes(body, "model", baseModel)
body, _ = thinking.ApplyThinking(body, req.Model, "openai")
body, err = thinking.ApplyThinking(body, req.Model, "openai")
if err != nil {
return resp, err
}
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated)
@@ -169,7 +172,10 @@ func (e *QwenExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Aut
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), true)
body, _ = sjson.SetBytes(body, "model", baseModel)
body, _ = thinking.ApplyThinking(body, req.Model, "openai")
body, err = thinking.ApplyThinking(body, req.Model, "openai")
if err != nil {
return nil, err
}
toolsResult := gjson.GetBytes(body, "tools")
// I'm addressing the Qwen3 "poisoning" issue, which is caused by the model needing a tool to be defined. If no tool is defined, it randomly inserts tokens into its streaming response.