Skip to content

fix(llm): missing anthropic thinking disabled transform#1552

Merged
looplj merged 1 commit into
unstablefrom
dev-tmp
Apr 30, 2026
Merged

fix(llm): missing anthropic thinking disabled transform#1552
looplj merged 1 commit into
unstablefrom
dev-tmp

Conversation

@looplj

@looplj looplj commented Apr 30, 2026

Copy link
Copy Markdown
Owner

@greptile-apps

greptile-apps Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the missing "disabled" thinking transform path across the Anthropic and ZAI outbound transformers. When a client sends thinking: {type: "disabled"}, the inbound layer now sets ReasoningEffort="none" and the TransformerMetadataKeyThinkingType marker, and the outbound layer correctly maps both signals to Thinking{Type: "disabled"} — including the DeepSeek output_config.effort guard that now skips "none". Additionally, TypeXiaomi is migrated from the generic OpenAI transformer to the ZAI transformer at Version="v1", and TypeCodex/TypeClaudecode are restricted to OAuth-only credentials.

Confidence Score: 5/5

Safe to merge — all findings are P2 or lower, production logic is correct and well-tested.

The core bug fix (disabled thinking not propagated to upstream) is correctly implemented with matching test coverage in both the Anthropic and ZAI transformers. The only finding is a P2 test-quality issue where the ZAI thinking tests assert an inline copy of the logic rather than invoking TransformRequest; this does not affect production behaviour.

llm/transformer/zai/thinking_test.go — tests replicate production logic inline instead of calling TransformRequest.

Important Files Changed

Filename Overview
llm/transformer/anthropic/inbound_convert.go Adds missing "disabled" case in thinking type switch — sets TransformerMetadata and ReasoningEffort="none" correctly.
llm/transformer/anthropic/outbound_convert.go Fixes buildBaseRequest to propagate disabled thinking: skips DeepSeek OutputConfig for "none", handles "disabled" metadata, and adds fallback when TransformerMetadata is absent but ReasoningEffort is "none".
llm/transformer/zai/outbound.go Adds configurable API version (defaults to "v4") and maps ReasoningEffort="none" to Thinking{Type:"disabled"}.
llm/transformer/zai/thinking_test.go Test cases added for "none" effort mapping, but all three test functions inline-replicate the switch logic rather than calling TransformRequest, so they don't guard the production path.
internal/server/biz/channel_llm.go TypeXiaomi extracted from the generic OpenAI case and given its own ZAI transformer with Version="v1"; endpoint/helper code moved to channel_endpoint.go.
internal/server/biz/channel_endpoint.go Receives defaultEndpointsForChannelType, mergeEndpoints, ResolveEndpoints, and platformTypeForGeminiEndpoint moved from channel_llm.go — logic is identical, just relocated.
internal/server/biz/provider_quota.go Adds early-return for TypeCodex and TypeClaudecode to restrict valid credentials to OAuth only (plain API keys rejected).
internal/server/biz/provider_quota_url_test.go Adds six test cases covering TypeCodex and TypeClaudecode credential validation; all scenarios match the new early-return logic correctly.
llm/transformer/anthropic/thinking_test.go New test cases verify disabled thinking round-trip (inbound + outbound) and DeepSeek "none" behaviour; correctly invokes the actual transformer methods.
llm/transformer/anthropic/inbound_test.go Updates assertion for disabled thinking from empty ReasoningEffort to "none", matching the new inbound_convert.go behaviour.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Inbound: thinking.type=disabled"] --> B["inbound_convert.go\nSet TransformerMetadata\nthinking_type=disabled\nReasoningEffort=none"]
    B --> C["llm.Request"]
    C --> D{"buildBaseRequest\noutbound_convert.go"}
    D --> E{"TransformerMetadata\nthinking_type=disabled?"}
    E -- yes --> F["req.Thinking =\nThinking{Type:disabled}"]
    E -- no --> G{"ReasoningEffort\n== none?"}
    G -- yes --> F
    G -- no --> H{"ReasoningEffort non-empty\nOR ReasoningBudget set?"}
    H -- yes --> I["buildThinking()\nenabled/budget"]
    H -- no --> J["No thinking"]
    F --> K["Upstream Anthropic API\nthinking: disabled"]
    I --> K
    J --> K
    C --> L{"ZAI outbound.go"}
    L --> M{"ReasoningEffort == none?"}
    M -- yes --> N["Thinking{Type:disabled}"]
    M -- no --> O["Thinking{Type:enabled}"]
Loading

Reviews (5): Last reviewed commit: "fix(llm): missing anthropic thinking dis..." | Re-trigger Greptile

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

🐛 1 issue in files not directly in the diff

🐛 Zai outbound transformer enables thinking when ReasoningEffort is "none" (disabled) (llm/transformer/zai/outbound.go:147-151)

The PR introduces ReasoningEffort = "none" in the Anthropic inbound path when thinking: {type: "disabled"} is received (llm/transformer/anthropic/inbound_convert.go:321). However, the Zai outbound transformer at llm/transformer/zai/outbound.go:147 checks if llmReq.ReasoningEffort != "" and unconditionally sets Thinking.Type = "enabled". Since "none" != "", any request that goes through the Anthropic inbound with disabled thinking and then routes to a Zai-backed channel (TypeZai, TypeZhipu, or the newly-added TypeXiaomi) will have thinking incorrectly enabled instead of disabled. The DeepSeek outbound correctly handles this by checking ReasoningEffort == "none" explicitly (llm/transformer/deepseek/outbound.go:109).

View 5 additional findings in Devin Review.

Open in Devin Review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors endpoint management by moving logic to channel_endpoint.go, updates the Xiaomi channel to utilize the zai transformer, and refines credential validation for Codex and Claudecode types. Additionally, it introduces support for a disabled thinking state within Anthropic transformers. Feedback was provided regarding an optimization in the mergeEndpoints function to improve efficiency by avoiding a redundant iteration over user endpoints.

Comment on lines +197 to +209
for _, ep := range userEndpoints {
if ep.APIFormat == "" {
continue
}

if _, ok := overrides[ep.APIFormat]; !ok {
continue
}

merged = append(merged, ep)

delete(overrides, ep.APIFormat)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The second loop over userEndpoints to append non-overriding endpoints can be made more efficient and concise. After the first loop, the overrides map contains only the user-defined endpoints that are not overrides. You can directly iterate over this map to append the remaining endpoints.

This simplifies the logic and avoids a second full iteration over userEndpoints.

Note: This change might alter the order of the appended non-overriding endpoints, as map iteration order is not guaranteed. If the order is not critical, this is a good simplification.

for _, ep := range overrides {
		merged = append(merged, ep)
	}

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@looplj looplj force-pushed the dev-tmp branch 2 times, most recently from b729aa5 to c0ce718 Compare April 30, 2026 16:30
@looplj looplj merged commit 78ab459 into unstable Apr 30, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant