返回 DeepSeek-Reasonix
REASONING_PROVIDERS.md
根目录 / docs / REASONING_PROVIDERS.md
1 # Reasoning controls by provider
2
3 Reasonix exposes a single `/effort` knob (and the per-provider `effort` /
4 `thinking` config fields), but OpenAI-compatible backends disagree on *how*
5 chain-of-thought is requested on the wire. The `openai` provider adapts the
6 request shape per backend; this table is the reference for which protocol each
7 known backend uses and which parameters it honours or ignores.
8
9 ## Auto-detected backends
10
11 These are recognised by base URL (see `internal/provider/openai/host.go`) and
12 get a tailored request shape automatically — no extra config needed.
13
14 | Provider | Base URL | Reasoning control | `/effort` levels | Notes |
15 |----------|----------|-------------------|------------------|-------|
16 | DeepSeek V4 Flash | `api.deepseek.com`, `*.deepseek.com` | `thinking.type` + `reasoning_effort` (depth) | `auto`, `disabled`, `low`, `high`, `max` | Thinking on by default; `disabled` turns it off via `thinking.type=disabled`. Compatibility input `medium` normalizes to `high`, while `xhigh` normalizes to `high`. Reasoning is replayed on every historical assistant turn that carries it, including turns without tool calls. |
17 | DeepSeek V4 Pro | `api.deepseek.com`, `*.deepseek.com` | `thinking.type` + `reasoning_effort` (depth) | `auto`, `disabled`, `low`, `high`, `max` | Thinking on by default; `disabled` turns it off via `thinking.type=disabled`. Compatibility inputs `medium` and `xhigh` normalize to `high`. Reasoning is replayed on every historical assistant turn that carries it, including turns without tool calls. |
18 | MiniMax M3 | `api.minimaxi.com`, `*.minimaxi.com` | `thinking.type` (`adaptive`\|`disabled`) | `auto`, `adaptive`, `disabled` | No depth scale; `reasoning_effort` is omitted. |
19 | Zhipu GLM | `open.bigmodel.cn` / `*.bigmodel.cn`, `api.z.ai` / `*.z.ai` | `thinking.type` (`enabled`\|`disabled`) | `auto`, `enabled`, `disabled` | **`reasoning_effort` is silently ignored** by the endpoint, so reasoning is driven purely through `thinking.type`. |
20
21 ## Explicit per-model scales
22
23 | Provider/model | Base URL | Reasoning control | `/effort` levels | Notes |
24 |----------------|----------|-------------------|------------------|-------|
25 | Kimi CN/Global `kimi-k3` | `api.moonshot.cn/v1`, `api.moonshot.ai/v1` | `reasoning_effort` | `low`, `high`, `max` | Always thinks; defaults to `max`. Reasonix replays the complete assistant message, uses `max_completion_tokens`, and omits K3's fixed sampling fields. |
26 | Custom Kimi K3 gateway | Any OpenAI-compatible K3 endpoint | `reasoning_effort` | `low`, `high`, `max` | Select `reasoning_protocol = "kimi-k3"` to opt into K3's complete-message replay and request shape. |
27 | OpenCode Go `kimi-k3` | `opencode.ai/zen/go/v1` | `reasoning_effort` | `high`, `max` | Relay-specific scale; defaults to `max` and keeps the relay's standard OpenAI-compatible request shape. |
28 | Token Rhythm DeepSeek V4 | `tokenrhythm.studio/v1` | DeepSeek `thinking.type` + `reasoning_effort` | Model-specific DeepSeek scale | Selected through the preset's model override, independent of the gateway host. |
29 | Token Rhythm GLM 5/5.1/5.2 | `tokenrhythm.studio/v1` | GLM `thinking.type` (`enabled`\|`disabled`) | `auto`, `enabled`, `disabled` | Selected through the preset's model override; `reasoning_effort` is omitted. |
30
31 On the Token Rhythm endpoint, exact GLM model IDs (`glm-5`, `glm-5.1`, and
32 `glm-5.2`) automatically select the official GLM request shape even when an
33 existing configuration has no `reasoning_protocol` field. The endpoint check
34 keeps unrelated mixed-model gateways backward-compatible. A `model_overrides`
35 entry with explicit `reasoning_protocol = "glm"` remains available for aliases
36 and custom model IDs. While GLM thinking is enabled, Reasonix retains and
37 returns the original `reasoning_content` unchanged in later history, as required
38 by GLM interleaved and preserved thinking.
39
40 For a custom gateway that serves Kimi K3, select **Kimi K3 reasoning** in the
41 provider editor's advanced reasoning protocol field, or configure it directly:
42
43 ```toml
44 [[providers]]
45 name = "my-kimi-gateway"
46 kind = "openai"
47 base_url = "https://my-gateway.example.com/v1"
48 model = "kimi-k3"
49 api_key_env = "MY_KIMI_API_KEY"
50 reasoning_protocol = "kimi-k3"
51 ```
52
53 This explicit protocol is needed when the gateway host cannot be safely
54 auto-detected. It preserves `reasoning_content` in later assistant history,
55 uses `max_completion_tokens`, and omits K3's fixed sampling fields. Do not add
56 it to the curated OpenCode Go preset: that relay intentionally keeps its
57 standard OpenAI-compatible request shape and its own `high`/`max` scale.
58 While this protocol is selected, Reasonix always exposes K3's fixed
59 `auto`/`low`/`high`/`max` effort menu with `max` as the protocol default;
60 persisted `supported_efforts` metadata is retained but does not override it.
61
62 ## DeepSeek Anthropic-compatible endpoint
63
64 The default official DeepSeek provider uses Chat Completions at
65 `https://api.deepseek.com`, with the independent [`web_search` tool](WEB_SEARCH.md)
66 using Messages for search. The optional `deepseek-anthropic` preset remains
67 available. When selected for the main conversation, Reasonix emits
68 `thinking.type=enabled|disabled` with `output_config.effort`, replays unsigned
69 DeepSeek thinking blocks from every historical assistant turn that carries
70 reasoning when the request declares tools (tool-call turn or not), omits unsupported
71 images, and relies on DeepSeek's automatic prefix cache instead of ignored
72 `cache_control` markers.
73
74 The preset exposes the same model-specific effort scale for Flash and Pro:
75 `auto`, `disabled`, `low`, `high`, and `max`. The Anthropic-compatible endpoint
76 accepts `low|high|max` on the wire. Legacy `medium` and `xhigh` both normalize
77 to `high`.
78
79 The OpenAI-compatible DeepSeek path follows the same replay rule when a request
80 declares tools: every historical assistant turn with stored `reasoning_content`
81 is serialized back verbatim, whether or not that turn called a tool. Without
82 tools, DeepSeek ignores this field and does not concatenate it into context. If
83 an old session still fails with the provider's specific reasoning pass-back HTTP
84 400, Reasonix rebuilds only the provider-visible projection of the old history,
85 retries once, and leaves later turns on the normal replay path; canonical
86 session history remains unchanged.
87
88 ## Missing-reasoning recovery
89
90 Adapters own replay requirements. Complete DeepSeek Chat responses can use empty
91 `reasoning_content`; compatible Responses endpoints can omit absent reasoning
92 items. These paths continue without an extra generation. Strict contracts allow
93 one recovery for missing or unfinished required proof, preferring repairable
94 history over exact regeneration. The two recoveries do not stack, and neither
95 changes the selected model or protocol. Client-truncated proof is never replaced
96 with an empty field.
97
98 ## Everything else (standard `reasoning_effort`)
99
100 Any other OpenAI-compatible backend falls through to the standard
101 `reasoning_effort` scale (`low`\|`medium`\|`high`). A resolved provider/model
102 entry may explicitly advertise a different supported scale; in that case
103 Reasonix preserves those declared values instead of applying the generic
104 ceiling. Curated per-model capability metadata can opt into another scale as
105 shown above.
106
107 Surveyed popular providers that need **no special handling** because they
108 already follow the standard convention:
109
110 Qwen (`dashscope.aliyuncs.com`), Yi
111 (`api.01.ai`), SiliconFlow (`api.siliconflow.cn`), Stepfun (`api.stepfun.com`),
112 Groq (`api.groq.com`), Together (`api.together.xyz`), OpenRouter
113 (`openrouter.ai`), Perplexity (`api.perplexity.ai`), xAI (`api.x.ai`).
114
115 For a backend that uses a binary `thinking.type` toggle but is **not**
116 auto-detected, set the vendor-agnostic `thinking` field on the provider entry:
117
118 ```toml
119 [[providers]]
120 name = "my-glm-proxy"
121 kind = "openai"
122 base_url = "https://my-gateway.example.com/v1"
123 model = "glm-4.6"
124 api_key_env = "MY_API_KEY"
125 thinking = "disabled" # enabled | disabled — emits thinking.type
126 ```
127
128 ## Troubleshooting
129
130 If a model keeps thinking when you asked it not to (or vice versa):
131
132 1. Check the table above — a backend may **ignore** the parameter you set
133 (e.g. Zhipu ignores `reasoning_effort`; use `thinking`/`/effort` instead).
134 2. If the backend isn't auto-detected, set the explicit `thinking` field.
135 3. If the backend uses a non-OpenAI protocol entirely (e.g. Baidu Wenxin), the
136 `openai` kind cannot drive its thinking mode — that needs a dedicated
137 provider kind.
138
139 Distinguishing "provider ignores the field" from a Reasonix bug starts here:
140 the request shape Reasonix emits is fixed per the table, so a mismatch between
141 the table and observed behaviour is the provider's, not Reasonix's.
142
143 ## Reasoning replay and interrupted execution
144
145 Replay contracts belong to adapters: DeepSeek Chat retains its empty
146 `reasoning_content` fallback; DeepSeek Responses can omit an absent reasoning
147 item but retains items actually returned. Anthropic unsigned thinking and native
148 Claude signed thinking have separate requirements. Missing content or proofs
149 never produce fabricated blocks. Unknown gateways do not gain extra empty-value
150 compatibility from a DeepSeek model name; explicit protocol configuration applies.
151
152 Anthropic preserves initial thinking, signature fragments, signed empty text, and
153 separate signed blocks. Responses preserves full reasoning items and uses the
154 completed response's final snapshot for the same item ID. Missing, explicitly
155 empty, client-truncated, and unfinished states are distinct. Required truncated or
156 unfinished reasoning cannot use an empty fallback to release tool execution.
157
158 Compatibility conversion runs before strict recovery. Native Claude's complete,
159 unsigned thinking from a non-tool assistant turn can become ordinary assistant
160 text in the outbound view. It is not applied to client/server tool turns, mixed
161 signed/unsigned proofs, redacted data, or incomplete/truncated reasoning. Raw
162 local thinking stays intact; no signature is invented.
163
164 An unknown Anthropic gateway does not acquire Claude's signature requirement
165 from its model name or adaptive-thinking setting. With thinking replay enabled,
166 its actually received unsigned blocks are retained without adding a signature.
167 An absent block is not synthesized. Explicit `reasoning_protocol = "deepseek"`
168 continues to enforce the DeepSeek replay contract. A concrete replay rejection
169 from the server uses the existing bounded history repair, retaining completed
170 execution facts instead of repeating tools.
171
172 Native signed and DeepSeek replay prefixes stay unchanged. Gateways whose enabled
173 thinking was previously dropped now receive their actual blocks; this corrects
174 lossy serialization but can change the old prefix once. No ordinary user setting
175 or additional persisted format is introduced by these conversions.
176
177 Strict replay repair and reasoning HTTP 400 repair share one recovery and consume the current model round retry budget. Only the outbound view changes; original records and completed-tool facts remain available. New calls still pass replay validation.
178
179 Tool results are recorded in call order, crossing a durability barrier before the
180 next writer starts. Read-only parallel groups checkpoint after the group returns.
181 Persistence failure prevents later tools from starting. Recovery distinguishes
182 completed, definitely not started, and outcome unknown. Unknown calls require
183 checking filesystem or external side effects before retrying. Missing results do
184 not prove non-execution, and unfinished siblings do not hide completed writes.
185
186 The optional fields `reasoning_state`, `thinking_blocks`, `tool_run_state`, and
187 recovery fields `not_started_tools` / `unknown_tools` preserve legacy reads.
188 Older sessions infer state from existing fields; interrupted placeholders mean
189 unknown outcome. Older clients can ignore new metadata for display, but cannot be
190 guaranteed to resume sessions relying on multiple signed blocks or opaque Responses
191 items; use the current version for those sessions. Healthy histories are not
192 stripped each round. Fault repair can change the repaired prefix's cache hit;
193 newly appended healthy tool rounds stay outside the old-prefix repair.
194
195
196 ## Automatic retries and waiting
197
198 Model rounds allow three additional attempts with 2/4/8-second backoff. HTTP,
199 stream, and protocol recovery no longer reset separate budgets. Server retry
200 delays take precedence. Only the main conversation can keep waiting after
201 transient connection, throttling, or service failures, normally every 60 seconds.
202 Partial generations, protocol failures, credentials, and exhausted quota cannot
203 enter unlimited generation. Search, summaries, compaction, and subagents use
204 finite retries. Cancellation and existing task limits remain effective; restarting
205 the app does not automatically resume network activity. Request counts and known
206 usage accumulate; missing usage is marked unknown, not interpreted as free.
207
208 ## File write verification
209
210 Built-in writes and edits persist versioned `write_intents` before modification,
211 including before/after content digests, encoding, path, and execution route.
212 Failed persistence prevents writing. Metadata never enters model requests or tool
213 schemas. Recovery reads the original route and reports satisfied postconditions
214 only when every target matches; it does not invent the original execution result.
215 Conflicts, unknown versions, and unavailable or replaced transports stay unknown.
216 There is no fallback to a similarly named local file. Identical unresolved writes
217 are blocked; read-only inspection remains available. Shell/MCP effects are not
218 automatically verified. Old sessions remain readable without evidence; older
219 clients do not provide the new recovery guarantees.
220
221
222 ### Verified official recovery behavior
223
224 The 2026-09-05 official Flash/Pro probes distinguished original provider call
225 IDs from replacement IDs: omitting reasoning can succeed with the former and
226 return a protocol-specific HTTP 400 with the latter. Responses names
227 `reasoning_text`, Messages names `content[].thinking`, and Chat names
228 `reasoning_content`; all use the existing bounded recovery path. Do not infer
229 unconditional omission support from one successful request.
230
231 EOF inside an unterminated JSON event now enters finite stream recovery;
232 malformed complete events remain errors. History repair carries bounded,
233 escaped, model-visible completed tool outputs with their originating user turns.
234 It retains the original repair boundary so later turns cannot reintroduce the
235 removed protocol history. Raw/local-only output remains excluded. Only fault
236 recovery changes that prefix; healthy schema and history stay stable.
237
238 File-write intent persistence runs on the actual prepared dispatch context,
239 before the effect. Missing terminal usage remains unknown through estimation
240 and aggregation. See [the validation report](RECOVERY_VALIDATION.md) for real
241 endpoint results, observed model variability, and the distinction between API
242 acceptance and fault-injected Agent tests.
243
243 lines MARKDOWN