From 3a5c715e8f70227aa1a5e00fb0b9632947096303 Mon Sep 17 00:00:00 2001 From: worldmozara Date: Tue, 28 Jul 2026 23:32:02 +0800 Subject: [PATCH] core/deepseek: inject thinking parameter to satisfy API reasoning mode requirement DeepSeek's reasoning model requires the `thinking` parameter to be explicitly set in every request (default is enabled). The agent loop constructs the base ChatCompletionsParam which lacks this field, so the DeepseekOpenAIClient now injects `thinking: {type: "enabled"}` into the JSON request body after msgspec encoding. Fixes the HTTP 400 error: "The `reasoning_content` in the thinking mode must be passed back to the API." which occurred when a cancelled turn was retried and the request history contained assistant messages with reasoning_content but no thinking param. --- .../lmproto/deepseek/openai_compat/client.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/plyngent/lmproto/deepseek/openai_compat/client.py b/src/plyngent/lmproto/deepseek/openai_compat/client.py index 2a9c2a3..a195a51 100644 --- a/src/plyngent/lmproto/deepseek/openai_compat/client.py +++ b/src/plyngent/lmproto/deepseek/openai_compat/client.py @@ -1,3 +1,6 @@ +from __future__ import annotations + +import json from typing import TYPE_CHECKING, Literal, overload import msgspec @@ -13,6 +16,19 @@ if TYPE_CHECKING: from .model import ChatCompletionsParam +def _inject_thinking(data: bytes) -> bytes: + """Inject ``thinking: {type: "enabled"}`` into the encoded request body. + + DeepSeek's reasoning model requires the ``thinking`` parameter to be + explicitly set (default is ``enabled``). The agent loop constructs the + base ``ChatCompletionsParam`` which lacks this field, so we add it + after msgspec encoding. + """ + body = json.loads(data) + body["thinking"] = {"type": "enabled"} + return json.dumps(body, separators=(",", ":")).encode("utf-8") + + class DeepseekOpenAIClient(BaseOpenAIClient): def __init__(self, config: OpenAIConfig) -> None: super().__init__(config) @@ -31,7 +47,7 @@ class DeepseekOpenAIClient(BaseOpenAIClient): self, param: ChatCompletionsParam, *, stream: bool = False ) -> ChatCompletionResponse | AsyncIterator[ChatCompletionChunk]: param = coerce_chat_completions_param_any(msgspec.structs.replace(param, stream=stream)) - data = self.encoder.encode(param) + data = _inject_thinking(self.encoder.encode(param)) if stream: resp = await self.session.post( "/chat/completions",