Skip to content

Keep DeepSeek reasoning through tool calls and conversation replay - #1241

Merged
WaylandYang merged 2 commits into
deeplethe:devfrom
Maya-Kid:codex/deepseek-reasoning-content
Oct 16, 2026
Merged

WaylandYang merged 2 commits into
deeplethe:devfrom
Maya-Kid:codex/deepseek-reasoning-content

Conversation

@Maya-Kid

Copy link
Copy Markdown
Contributor

Why

In DeepSeek thinking mode, a reply can contain both reasoning_content and a tool call. The client discarded the reasoning, so the request carrying the tool result failed with HTTP 400. Conversation replay also discarded it, including the reasoning attached to completed answers, which DeepSeek requires on later requests carrying tools: provider protocol.

What changes

  • Carry optional reasoning separately from visible text through streaming and non-streaming LlmClient responses, rig messages and subsequent tool requests. Preserve empty strings and omit absent fields.
  • Save tool-turn and completed-answer reasoning in the existing internal exchange JSON. Restore it by message position when loading history and retain it with its answer when trimming context. This also covers the reserved final-answer call and RAG fallback.
  • Keep reasoning out of published SSE text and public conversation content.

The code and tests exceed 500 added lines because parsing, rig translation, persistence and replay form one protocol path: all must retain the field for both tool continuation and the next user question to work. The tests cover those loss boundaries and an actual three-turn database round trip in this one fix.

Previously stored conversation rows remain readable; reasoning already discarded by older code cannot be reconstructed.

How it was checked

macOS arm64, Rust 1.98.1, commit 830b7a4 based on dev at 70bf7a7f:

  • cargo +1.98.1 fmt --all --check
  • cargo +1.98.1 clippy --locked --offline --workspace --all-targets -- -D warnings
  • UTOPIA_DATABASE_URL=<isolated PostgreSQL 16 + pgvector> UTOPIA_TEST_REQUIRE_DB=1 cargo +1.98.1 test --locked --offline --workspace: 1,540 reported passed, 0 failed, 6 ignored. Database-backed tests ran against a freshly migrated, dedicated local cluster; external-service smoke tests retain their usual skips when endpoints are unset.
  • The new low-level reasoning regressions failed before the fix and passed afterward. The rig regression uses a strict HTTP mock that returns 400 when the continuation loses the field, for both streaming and non-streaming runners.
  • The database regression makes three user turns with identical visible answers but different reasoning, then checks replay, tool continuation and SSE isolation. Additional regressions cover UTF-8/frame boundaries, empty fields and context trimming.

Before review

  • Every commit is signed off (git commit -s).
  • Format, workspace Clippy and workspace tests pass.
  • The conversation persistence/replay regression ran with UTOPIA_DATABASE_URL set and UTOPIA_TEST_REQUIRE_DB=1.

Signed-off-by: dada-yan <BinjunYann@gmail.com>

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

Thank you, this is the right fix and it goes through every place the field could get lost: parsing, rig, the final-answer call, the RAG fallback, storage and trimming. With tools in the request DeepSeek needs every previous turn's reasoning_content back, so replaying it for finished answers and not only tool turns is needed. Keeping it in tool_exchange and filtering it out of the replayed exchange avoids a migration, and counting it in the history budget keeps trimming honest.

One thing for later, not for this PR: other OpenAI-compatible endpoints that emit reasoning_content will now get it back too and spend history budget on it. If one of them turns out to reject it, the place to switch it off is push_message / to_message. Thanks again.

@WaylandYang
WaylandYang merged commit 7b0ccc4 into deeplethe:dev Oct 16, 2026
6 checks passed
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