Repository navigation
fix(anthropic-adapter): translate stop_sequences and disabled thinking for non-Claude targets - #34589
Conversation
Greptile SummaryUpdates the experimental Anthropic messages adapter to:
Confidence Score: 5/5The PR appears safe to merge. No blocking failures remain.
|
| Filename | Overview |
|---|---|
| litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py | Adds stop-sequence translation and consistently preserves disabled-thinking semantics across both reasoning translation paths. |
| tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py | Adds focused unit coverage for stop translation, empty stop sequences, disabled thinking, and automatic-summary interaction. |
| tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py | Extends handler-level coverage for disabled-thinking translation and downstream request behavior. |
Reviews (4): Last reviewed commit: "fix(anthropic-adapter): drop redundant c..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…g for non-Claude targets
Claude Code's auto-mode classifier sends stop_sequences and thinking:
{type: disabled} on /v1/messages. The Anthropic adapter passed
stop_sequences through unchanged instead of mapping it to OpenAI's stop,
which Fireworks' OpenAI-compatible endpoint rejects with HTTP 400. It also
dropped disabled thinking instead of mapping it to reasoning_effort: none,
so the model spent its output budget on reasoning it was told to skip.
Resolves LIT-4798
…in string Guard against reasoning_auto_summary wrapping "none" into a dict when thinking is disabled — there's no reasoning trace to summarize, and non-Claude providers (e.g. Fireworks) expect reasoning_effort as a plain string.
a6ce7b1 to
9da21f3
Compare
Codecov flagged the empty-list early-return in _translate_stop_sequences_to_openai as an uncovered line in the diff — add a regression test asserting stop_sequences=[] does not set new_kwargs["stop"].
|
bugbot run |
…ling gap
translate_thinking_for_model duplicated the same summary/auto_summary
wrapping logic as _translate_thinking_to_openai without the
disabled-thinking guard, so it could still wrap "none" into an
{effort, summary} dict when reasoning_auto_summary is enabled (caught
by Cursor Bugbot). Extract the wrapping rule into one shared
_apply_reasoning_summary_wrapping helper used by both call sites so
this invariant can't drift apart again.
|
Good catch — |
…ne budget _apply_reasoning_summary_wrapping already returns Any, so wrapping its dict-literal returns in cast(Any, ...) was a no-op that only inflated the LIT006 cast-count budget the lint gate enforces.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit d478b99. Configure here.
TLDR
Problem this solves:
/v1/messageswithstop_sequencesto non-Claude targets (e.g. Fireworks GLM)stop_sequencesoutright (HTTP 400), since its OpenAI-compatible API only acceptsstopthinking: {type: "disabled"}not mapped toreasoning_effort: "none", so the model still burns budget reasoningHow it solves it:
stop_sequences->stopfor all non-Anthropic-native targets (mirrors howthinkingis already handled)thinking: {type: "disabled"}now maps toreasoning_effort: "none"instead ofNonereasoning_auto_summarywrapping so a disabled-thinking"none"stays a plain string instead of becoming{"effort": "none", "summary": "detailed"}(there's no reasoning trace to summarize when thinking is disabled, and non-Claude providers expect a plain string)Relevant issues
stop_sequences, so it fell through to verbatim copy-through instead of translationtranslate_anthropic_thinking_to_reasoning_effortreturnedNonefor the"disabled"case instead of"none""disabled"truthy surfaced a latent second issue: it now reaches thereasoning_auto_summarywrapping logic, which needed an explicit guardLinear ticket
Resolves LIT-4798
Pre-Submission checklist
Screenshots / Proof of Fix
Live proxy against a local stub standing in for Fireworks (no Fireworks credentials in this environment; stub enforces the same strict "extra field" rejection Fireworks applies).
Before fix (commit
78348fd1c7):After fix (commit
adeb46b157, stop_sequences + disabled-thinking core fix):Follow-up fix (commit
a6ce7b10f9, mutation-tested unit repro/verify since it's an interaction with a global opt-in flag rather than a Fireworks-specific wire issue):Type
🐛 Bug Fix
Note
Low Risk
Scoped to the experimental Anthropic→OpenAI adapter and covered by new unit tests; behavior change is intentional for non-Claude routing with no auth or data-path impact.
Overview
Fixes Anthropic
/v1/messagespass-through when the upstream target is OpenAI-compatible (e.g. Fireworks) instead of native Claude.Stop sequences:
stop_sequencesis now a translatable param and is mapped to OpenAIstopduringtranslate_anthropic_to_openai, so providers that reject unknownstop_sequencesno longer get a verbatim copy-through. Empty lists are ignored.Disabled thinking:
thinking: {type: "disabled"}now becomesreasoning_effort: "none"for non-Claude models (previously dropped asNone), so reasoning can actually be turned off.Reasoning summary wrapping: Duplicated summary/auto-summary logic is centralized in
_apply_reasoning_summary_wrapping. When thinking is disabled,"none"stays a plain string even ifreasoning_auto_summaryis on—avoiding{"effort": "none", "summary": "detailed"}that non-Claude backends reject.Unit tests cover stop translation, disabled thinking, and the auto-summary interaction.
Reviewed by Cursor Bugbot for commit d478b99. Bugbot is set up for automated code reviews on this repo. Configure here.