Skip to content

fix(deepseek): preserve reasoning_content in thinking mode for tool calls#1020

Open
tlotli wants to merge 1 commit into
prism-php:mainfrom
tlotli:fix/deepseek-reasoning-content-preservation
Open

fix(deepseek): preserve reasoning_content in thinking mode for tool calls#1020
tlotli wants to merge 1 commit into
prism-php:mainfrom
tlotli:fix/deepseek-reasoning-content-preservation

Conversation

@tlotli
Copy link
Copy Markdown

@tlotli tlotli commented May 16, 2026

Summary

  • DeepSeek's thinking mode requires reasoning_content to be passed back in all subsequent requests when tool calls are involved. Without this, the API returns 400: The reasoning_content in the thinking mode must be passed back to the API.
  • This PR fixes multi-turn conversations with tools in deepseek-v4-flash (and other DeepSeek thinking-mode models).

Problem

When using DeepSeek models in thinking mode with tools, Prism discards the reasoning_content from the API response:

  1. Text.php handler creates AssistantMessage with empty additionalContent — the reasoning_content from the raw response is lost
  2. MessageMap.php does not merge additionalContent into the API payload — even if reasoning_content was in the message object, it was never sent back to the API

When Prism recursively calls handle() for tool-use continuation, the request includes an AssistantMessage without reasoning_content → DeepSeek returns 400.

Changes

src/Providers/DeepSeek/Handlers/Text.php

  • Extract reasoning_content from raw response (choices.0.message.reasoning_content)
  • Include it in AssistantMessage.additionalContent for both:
    • Tool call intermediate steps (line 66-73)
    • Final response step via addStep() (line 128)

src/Providers/DeepSeek/Maps/MessageMap.php

  • Merge $message->additionalContent into the API payload via array_merge() (line 102-106)
  • This ensures reasoning_content is sent back to the API in multi-turn conversations

Testing

Verified with 4-turn conversation using tools:

  • Turn 1 (no history) ✅
  • Turn 2 (with history) ✅
  • Turn 3 (deeper history) ✅
  • Turn 4 (4-turn conversation) ✅

References

  • DeepSeek Thinking Mode Docs"Between two user messages, if the model performed a tool call, the intermediate assistant's reasoning_content must participate in the context concatenation and must be passed back to the API in all subsequent user interaction turns."

…alls

DeepSeek's thinking mode requires reasoning_content to be passed back
in all subsequent requests when tool calls are involved. Without this,
the API returns 400: 'The reasoning_content in the thinking mode must
be passed back to the API.'

Two fixes:
- Text.php: Extract reasoning_content from raw response and include
  it in AssistantMessage.additionalContent for both tool call steps
  and the final response step.
- MessageMap.php: Merge additionalContent into the API payload so
  reasoning_content is sent back to the API in multi-turn conversations.

Fixes multi-turn conversations with tools in deepseek-v4-flash.
@Lintume
Copy link
Copy Markdown

Lintume commented May 22, 2026

Hit this exact bug independently while migrating our diary agent (Calorize, ~200 paid users) to direct DeepSeek API in thinking mode. Confirmed deepseek-v4-flash (thinking) 400s on the second iteration of any multi-step tool flow without this fix.

Been running a local patch with the same approach (extract reasoning_content into additionalContent, merge it back in MessageMap) for several days — works as expected, no regressions. Would love to see this merged so we can drop the override.

Separately: DeepSeek\Handlers\Text::addStep() and Stream::extractUsage() also drop prompt_cache_hit_tokens and completion_tokens_details.reasoning_tokens from the usage payload. That's a different concern from this PR — I'll open a focused PR for it, non-overlapping with this change.

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.

2 participants