Skip to content

feat(anthropic): per-deployment opt-out for legacy thinking → adaptive rewrite (port of #77) - #80

Merged
songkuan-zheng merged 1 commit into
ship/v1.87.0from
fix/anthropic-passthrough-thinking-v1.87.0
Jun 10, 2026
Merged

feat(anthropic): per-deployment opt-out for legacy thinking → adaptive rewrite (port of #77)#80
songkuan-zheng merged 1 commit into
ship/v1.87.0from
fix/anthropic-passthrough-thinking-v1.87.0

Conversation

@songkuan-zheng

Copy link
Copy Markdown
Collaborator

Tier classification

  • D — Universal mechanism + company opinion in litellm/ core

Tried upstream first?

  • No — same Tier D candidate already shipped on v1.83.10 via feat(anthropic): per-deployment opt-out for legacy thinking rewrite #77. Will be filed upstream as a single passthrough-hook PR once both ship lines have one dogfood cycle. Tracked under fix/billing-accuracy-phase-1's sibling row in UPSTREAM_PR_QUEUE.md (Wave 6d — disable_adaptive_thinking_rewrite opt-in flag).

Summary

Port of #77 from ship/v1.83.10 to ship/v1.87.0. The two ship lines diverge at the upstream pin and are maintained in parallel — landing the fix on v1.87.0 keeps them in sync.

Adds disable_adaptive_thinking_rewrite to GenericLiteLLMParams. When set on a model_list deployment, the /v1/messages passthrough preserves the caller's legacy thinking={"type":"enabled","budget_tokens":N} verbatim instead of rewriting it to the adaptive form ({"type":"adaptive"} + output_config.effort).

Motivation

On Claude Sonnet/Opus 4.6, the legacy thinking shape is deprecated upstream but still functional. LiteLLM rewrites it proactively, which discretizes budget_tokens into a 4-bucket effort band — callers that need byte-exact passthrough (precise cache pricing, A/B parity with non-LiteLLM clients) have no way to opt out today.

The flag is duck-typed: the gate reads from either a dict-shaped litellm_params or a GenericLiteLLMParams pydantic instance, mirroring the additional_drop_params pattern. It only short-circuits the rewrite path — it does NOT bypass upstream Anthropic's 400 rejection of the legacy shape on Opus 4.7+ (those models reject it at the provider, not in LiteLLM).

Files

  • litellm/llms/anthropic/experimental_pass_through/messages/transformation.py — rewrite gate.
  • litellm/types/router.pydisable_adaptive_thinking_rewrite: Optional[bool] on GenericLiteLLMParams.
  • tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_legacy_thinking_passthrough.py — 5 tests (default behavior, dict-shape opt-out, pydantic-model opt-out, no-op on non-adaptive models, default-False on a fresh pydantic instance).

Verification

  • ✅ Cherry-pick from fix/anthropic-passthrough-thinking (v1.83.10 base) onto v1.87.0 base resolved cleanly (auto-merge handled both production files).
  • 5/5 tests pass (tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_legacy_thinking_passthrough.py).
  • ✅ Default is False — existing rewrite behavior is preserved for all deployments that don't opt in.

Conflict resolutions

N/A — git cherry-pick 31f189c7b7 reported Auto-merging on both source files and produced no conflict markers.

Pre-submission

  • Tests in tests/test_litellm/ (5 tests in the per-file passthrough test)
  • make test-unit equivalent passes
  • PR scope: 1 commit, port of a single shipped feature

…e rewrite

Add `disable_adaptive_thinking_rewrite` to `GenericLiteLLMParams`. When
set on a model_list deployment, the /v1/messages passthrough preserves
the caller's legacy `thinking={"type":"enabled","budget_tokens":N}`
verbatim instead of rewriting it to the adaptive form
(`{"type":"adaptive"}` + `output_config.effort`).

Motivation: on Claude Sonnet/Opus 4.6, the legacy thinking shape is
deprecated upstream but still functional. LiteLLM rewrites it
proactively, which discretizes `budget_tokens` into a 4-bucket effort
band — callers that need byte-exact passthrough (precise cache pricing,
A/B parity with non-LiteLLM clients) have no way to opt out today.

The flag is duck-typed: the gate reads from either a dict-shaped
`litellm_params` or a `GenericLiteLLMParams` pydantic instance,
mirroring the `additional_drop_params` pattern. It only short-circuits
the rewrite path — it does NOT bypass upstream Anthropic's 400 rejection
of the legacy shape on Opus 4.7+ (those models reject it at the
provider, not in LiteLLM).

Usage:
  model_list:
    - model_name: claude-sonnet-4-6-raw
      litellm_params:
        model: anthropic/claude-sonnet-4-6
        api_key: os.environ/ANTHROPIC_API_KEY
        disable_adaptive_thinking_rewrite: true

Tests cover: default rewrite preserved, flag-via-dict opt-out, flag-via-
pydantic-model opt-out, no-op on non-adaptive models, and default-False
on a fresh pydantic instance.

Tier: D (universal mechanism + opinionated default of False preserves
existing behavior). To be filed upstream as a passthrough hook.
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