Skip to content

fix(deepseek): promote reasoning_content on multi-turn default thinking models - #42158

Open
truecallerabreham wants to merge 3 commits into
BerriAI:mainfrom
truecallerabreham:fix/37636-deepseek-default-thinking-reasoning-content
Open

truecallerabreham wants to merge 3 commits into
BerriAI:mainfrom
truecallerabreham:fix/37636-deepseek-default-thinking-reasoning-content

Conversation

@truecallerabreham

Copy link
Copy Markdown

TLDR

Problem this solves:

  • Multi-turn DeepSeek reasoning calls fail with HTTP 400 when thinking param is omitted
  • Stored reasoning_content is never promoted back onto assistant messages for default-on reasoning models

How it solves it:

  • Treats default reasoning models as active in _thinking_mode_active unless explicitly disabled
  • Prunes empty provider_specific_fields after promoting reasoning_content
  • Preserves opt-in behavior for dual-mode models like deepseek-v3.2

User Flow

Before: a developer makes a multi-turn conversation call to deepseek-reasoner or deepseek-r1 through LiteLLM

  1. Client sends POST https://litellm-domain/v1/chat/completions with model="deepseek/deepseek-reasoner" without an explicit thinking param
  2. The initial assistant response returns with reasoning_content in provider_specific_fields
  3. Client sends a second turn with the conversation history including the prior assistant message
  4. DeepSeek API rejects the second request with HTTP 400: "The reasoning_content in the thinking mode must be passed back to the API."

After: the second request succeeds and preserves the reasoning chain

  1. Client sends POST https://litellm-domain/v1/chat/completions with model="deepseek/deepseek-reasoner" without an explicit thinking param
  2. The initial assistant response returns with reasoning_content in provider_specific_fields
  3. Client sends a second turn with the conversation history including the prior assistant message
  4. _thinking_mode_active identifies the default reasoning model and promotes reasoning_content, allowing DeepSeek API to accept the request and respond

Relevant issues

Fixes #37636

Pre-Submission checklist

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Screenshots / Proof of Fix

Before (d9a3381)

multi-turn reasoning_content promotion on default reasoning model

  1. Send multi-turn request for deepseek-reasoner with prior assistant message containing provider_specific_fields={"reasoning_content": "step 1"}
  2. Request transformation skips _fill_reasoning_content because _thinking_mode_active returns False when thinking param is omitted; assistant message has no reasoning_content field on wire message

After (ee2b318)

multi-turn reasoning_content promotion on default reasoning model

  1. Send identical multi-turn request for deepseek-reasoner
  2. _thinking_mode_active identifies default reasoning model and runs _fill_reasoning_content; assistant message has reasoning_content: "step 1" promoted and empty provider_specific_fields removed
$ pytest tests/unit/llms/deepseek/chat/test_deepseek_chat_transformation.py -v
============================= 63 passed in 0.26s ==============================

Type

🐛 Bug Fix

Caveats (if any)

Low

  • Dual-mode models like deepseek-v3.2 still require thinking={"type": "enabled"} to activate reasoning mode as expected

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

…ng models

DeepSeek dedicated reasoning models (deepseek-reasoner, deepseek-r1) and default-on models (deepseek-v4) run thinking mode server-side without requiring explicit client-side thinking parameters. Previously, _thinking_mode_active required thinking={'type': 'enabled'}, which caused _fill_reasoning_content to be bypassed when calling these models without the parameter, resulting in HTTP 400 errors from DeepSeek's API in multi-turn conversations.

Treat default reasoning models as active in _thinking_mode_active unless explicitly disabled with thinking={'type': 'disabled'}, prune empty provider_specific_fields after promotion, and preserve opt-in behavior for dual-mode models like deepseek-v3.2. Add unit regression tests covering parameter matrices and multi-turn message transformation.
@truecallerabreham
truecallerabreham requested a review from a team September 20, 2026 19:03
@CLAassistant

CLAassistant commented Sep 20, 2026 •

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codspeed

codspeed Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing truecallerabreham:fix/37636-deepseek-default-thinking-reasoning-content (446ddf8) with main (65c6495)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with both previous findings resolved and no new actionable failures identified

Summary

This PR makes DeepSeek request transformation recognize default-on reasoning models through model metadata, promotes stored reasoning content during multi-turn requests, and removes empty provider-specific fields after promotion

  • Adds thinking_always_on metadata for the affected native DeepSeek models
  • Keeps explicit enablement and disablement behavior for dual-mode models
  • Extends the existing DeepSeek transformation tests with sync, async, alias, malformed-parameter, and field-preservation coverage
  • Removes the duplicate mapped test file introduced earlier

Reviews (3) · Last reviewed commit: "fix(deepseek): use thinking_always_on me..."

Comment thread litellm/llms/deepseek/chat/transformation.py Outdated
@codecov

codecov Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.46154% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/llms/deepseek/chat/transformation.py 88.46% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@truecallerabreham
truecallerabreham force-pushed the fix/37636-deepseek-default-thinking-reasoning-content branch from ee2b318 to 446ddf8 Compare September 20, 2026 22:30
@truecallerabreham

Copy link
Copy Markdown
Author

Please review the updated commit with get_model_info metadata lookup and added tests under tests/test_litellm @greptileai

Comment thread tests/test_litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py Outdated
@truecallerabreham

Copy link
Copy Markdown
Author

Added thinking_always_on to model_prices_and_context_window.json, removed hardcoded markers from transformation logic, and deleted the duplicate test file @greptileai

This branch has not been deployed

No deployments
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.

[Bug]: DeepSeek thinking-by-default models + tools: _fill_reasoning_content guard never fires without explicit reasoning params, upstream 400

3 participants