Skip to content

fix(llm-drivers): treat deepseek-v4-pro as thinking-with-tools so reasoning_content is echoed#6098

Merged
houko merged 2 commits into
librefang:mainfrom
DaBlitzStein:fix/deepseek-v4-pro-thinking
Jun 15, 2026
Merged

fix(llm-drivers): treat deepseek-v4-pro as thinking-with-tools so reasoning_content is echoed#6098
houko merged 2 commits into
librefang:mainfrom
DaBlitzStein:fix/deepseek-v4-pro-thinking

Conversation

@DaBlitzStein

Copy link
Copy Markdown
Contributor

Summary

Fixes #6097. deepseek-v4-pro agents 400 on every multi-turn tool-call conversation with "The reasoning_content in the thinking mode must be passed back to the API."

is_deepseek_v4_thinking_with_tools only matched deepseek-v4-flash; V4 Pro was excluded on the #4842 assumption that it "works out-of-the-box". Production disproves that — V4 Pro has the same echo requirement, so without ReasoningEchoPolicy::Echo its reasoning_content is dropped and DeepSeek rejects the next request. A delegated agent on deepseek-v4-pro therefore died on its first tool-call follow-up turn (so agent_send / shared-queue tasks to it never executed).

Change

  • is_deepseek_v4_thinking_with_tools now matches deepseek-v4-pro as well as deepseek-v4-flash (substring match keeps date-stamped variants working).
  • Doc comment updated to record why the original exclusion was wrong.
  • Moved deepseek-v4-pro from the does_not_match_others test to the matches test (plus DeepSeek-V4-Pro and a date-stamped variant).

Verification

  • cargo test -p librefang-llm-drivers --lib is_deepseek_v4 → 2 passed, 0 failed.
  • Scope kept minimal (single function + its tests + CHANGELOG); no catalog/history-fold changes bundled.

…soning_content is echoed

deepseek-v4-pro returns 400 "The reasoning_content in the thinking mode
must be passed back to the API." on multi-turn tool-call conversations —
the same echo requirement as V4 Flash — but it was excluded from
is_deepseek_v4_thinking_with_tools on the librefang#4842 "works out-of-the-box"
assumption. Production proved that wrong: a delegated agent on
deepseek-v4-pro failed every turn once its history held a tool-call
thinking turn. Match V4 Pro too so the Echo reasoning-echo policy applies
and the thinking text round-trips intact.
@github-actions github-actions Bot added size/S 10-49 lines changed area/docs Documentation and guides labels Jun 12, 2026
@DaBlitzStein
DaBlitzStein marked this pull request as draft June 12, 2026 22:08
@DaBlitzStein

Copy link
Copy Markdown
Contributor Author

Moving to draft — honesty note on the evidence.

This change is a defensible contract-alignment (deepseek-v4-pro genuinely is a thinking-with-tools model: its assistant turns carry thinking + tool_use blocks), and the original exclusion rested on the now-questionable "#4842 works out-of-the-box" assumption.

But I can't currently confirm it fixes a problem that manifests in normal use. The incident that prompted it was an agent on deepseek-v4-pro 400ing with "reasoning_content must be passed back". On closer inspection a sibling agent on the same model runs 100+ thinking + tool_use assistant turns without the echo policy and never hits the 400 — so the missing echo policy can't be the whole story. The real differentiator between the two agents is max_history (aggressive trimming on the failing one), not the echo policy.

Holding as draft until I can confirm whether deepseek-v4-pro actually requires the echo (and isn't getting it some other way for the working agent). Did not want to put an unconfirmed fix in the review queue.

@DaBlitzStein
DaBlitzStein marked this pull request as ready for review June 12, 2026 22:45
@DaBlitzStein

Copy link
Copy Markdown
Contributor Author

Confirmed live — promoting out of draft.

Deployed this fix to a host running a deepseek-v4-pro agent that was 400ing on every tool-call continuation with "reasoning_content must be passed back". After the deploy the agent runs its multi-turn tool-calling task cleanly — no 400, no agent-loop failure, reasoning_content round-tripped on each turn:

usage prompt_tokens=23238 completion_tokens=100   ✓
Captured reasoning_content from response len=1417  → completion_tokens=696  ✓
completion_tokens=3185                              ✓
Captured reasoning_content len=4852                 ✓

So deepseek-v4-pro does require the same reasoning_content echo as V4 Flash; treating it as thinking-with-tools fixes it. (The sibling agent on the same model that appeared to work without the echo turned out to be a red herring for the fix — both echo it now and both run fine.)

@houko

houko commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Reviewed — the change is correct, minimal, and well-tested. Two non-blocking notes.

1. Fix targets the fallback, not the catalog — confirm the policy actually reaches the wire.
fallback_reasoning_echo_policy only fires when the request carries no explicit policy (ReasoningEchoPolicy::None — catalog miss or pre-registry build), per its own doc comment.
If a community-catalog entry for deepseek-v4-pro ever sets a non-None reasoning_echo_policy, the request carries that value and this fallback never runs, making the change a no-op for catalog-resolved requests.
The added test verifies is_deepseek_v4_thinking_with_tools("deepseek-v4-pro") == true at the function level, but not end-to-end that Echo lands on the actual outbound request.
Low risk in practice: there is no in-tree catalog entry for the model (builtins sync from the external registry into home_dir/providers/, default None), and the production 400 was observed on exactly this fallback path — so the fix targets the right layer.
Flagging only so a follow-up that adds a catalog entry doesn't silently shadow this.

2. Breadth check — no parallel site needs the same change.
The other model-aware thinking path, parse_labeled_summaries / strip_think_preamble in crates/librefang-runtime/src/history_fold.rs, is model-agnostic (it strips any leading <think>…</think> block; deepseek-v4-flash is only named as an example, not gated on). So is_deepseek_v4_thinking_with_tools is the only model-gated point, and this PR covers it.

Nit: the CHANGELOG bullet carries only (@DaBlitzStein) — sibling entries in the same ### Fixed block include the issue/PR number too (e.g. (#6083)). Consider appending (#6097) for consistency.

@houko houko 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.

Approved. Correct, minimal, well-tested fix; the two notes on my earlier comment are non-blocking.

@houko
houko merged commit 4530f9d into librefang:main Jun 15, 2026
32 checks passed
@github-actions github-actions Bot added the ready-for-review PR is ready for maintainer review label Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation and guides ready-for-review PR is ready for maintainer review size/S 10-49 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

deepseek-v4-pro hits 400 "reasoning_content must be passed back" — not recognised as thinking-with-tools

2 participants