Context
GoModel is used as an OpenAI-compatible gateway in front of several backends behind a single virtual model alias, so clients have no way to know which upstream serves a given request. When a request is routed to the official DeepSeek API in thinking mode, tool-calling calls fail with:
400 invalid_request_error: The reasoning_content in the thinking mode must be passed back to the API.
Root cause
The official DeepSeek API enforces that every assistant message in a tool-calling request echoes reasoning_content. This only surfaces on the direct DeepSeek route: other routes (e.g. OpenRouter, OpenCode Zen) terminate the request at their own API surface, so the DeepSeek validator never sees the client payload.
Clients can't unconditionally attach DeepSeek-specific fields because the model name is a virtual alias and the upstream can rotate. GoModel is the only component that knows the actual backend.
Request
When GoModel forwards a chat/completions request to the direct DeepSeek provider and the request includes tools, ensure assistant messages with tool_calls carry reasoning_content:
- Preserve the client-sent value if present.
- Otherwise synthesize a placeholder — a single space
" " (DeepSeek V4 rejects the empty string "" with the same 400).
- Apply only on the DeepSeek route; never inject for other providers.
- Natural insertion point: the DeepSeek provider's
adaptChatRequest hook (internal/providers/deepseek/deepseek.go).
Related: #623 covers the case where the client already sends reasoning; this covers the case where it can't.
Context
GoModel is used as an OpenAI-compatible gateway in front of several backends behind a single virtual model alias, so clients have no way to know which upstream serves a given request. When a request is routed to the official DeepSeek API in thinking mode, tool-calling calls fail with:
400 invalid_request_error: Thereasoning_contentin the thinking mode must be passed back to the API.Root cause
The official DeepSeek API enforces that every assistant message in a tool-calling request echoes reasoning_content. This only surfaces on the direct DeepSeek route: other routes (e.g. OpenRouter, OpenCode Zen) terminate the request at their own API surface, so the DeepSeek validator never sees the client payload.
Clients can't unconditionally attach DeepSeek-specific fields because the model name is a virtual alias and the upstream can rotate. GoModel is the only component that knows the actual backend.
Request
When GoModel forwards a chat/completions request to the direct DeepSeek provider and the request includes tools, ensure assistant messages with tool_calls carry reasoning_content:
" "(DeepSeek V4 rejects the empty string""with the same 400).adaptChatRequesthook (internal/providers/deepseek/deepseek.go).Related: #623 covers the case where the client already sends reasoning; this covers the case where it can't.