feat(llm): generic per-request thinking/effort control (provider-native mapping incl. DeepSeek chat_template_kwargs)
Summary
Add a generic thinking/effort control to the LLM request path — a per-request (and per-model default) thinking level that each provider adapter maps to its native parameter. DeepSeek V4 Flash via NEAR AI is the trigger case (0731 checkpoint got verbose), but the control must be provider-agnostic, matching the thinking vocabulary OpenClaw already exposes (off/minimal/low/medium/high/xhigh/adaptive/max).
Trigger
NEAR AI upgraded deepseek-ai/DeepSeek-V4-Flash to the 0731 checkpoint. Users report responses that are often very chatty, especially mid-conversation pieces (each interim model call in the tool loop streams as its own visible message). Proposed test configs need client-side levers that do not exist yet: chat_template_kwargs: {"thinking": false} for normal chats, and reasoning-effort levels for deep agentic work.
Current state (verified in code)
- No generic control exists.
CompletionRequest/ToolCompletionRequest(crates/domains/ironclaw_llm/src/provider.rs:323-341, 575-590) have no thinking/effort field; the loop host never sets temperature/max_tokens either, so generation behavior is server defaults for every provider that lacks a hardcoded shim. - Provider-specific, hardcoded, non-user-controlled today:
- Gemini:
thinkingConfigauto-injected atthinkingLevel: HIGH/thinkingBudget: 8192when the model name matches (gemini_oauth.rs:1646-1655). - Anthropic:
thinking_for_requestauto-enables adaptive/enabled with a fixed 1024 budget (anthropic_thinking.rs), disabled when temperature or tools are set. - OpenAI Codex:
{"effort": "medium"}hardcoded (responses_reasoning.rs:5-7). - DeepSeek/nearai: nothing sent — no
chat_template_kwargs, noreasoning_effort(nearai_chat.rs:1111-1127).
- Gemini:
- Reasoning separation works:
reasoning_content/reasoningparse intoCompletionResponse.reasoning, surfaced as dedicated UI reasoning blocks viasafe_reasoning_deltas. Caveat: whencontentis null/empty the provider substitutes the reasoning text as the answer (nearai_chat.rs:780-793, 852-856, 952-956, 1025-1033), which makes a verbose default user-visible. - No thinking/effort picker in the WebUI; FEATURE_PARITY.md pins per-level thinking control as missing for IronClaw (OpenClaw has
off/minimal/low/medium/high/xhigh/adaptive/max).
Proposed scope
- Add
thinking_effort(or equivalent) toCompletionRequest/ToolCompletionRequest:off | minimal | low | medium | high | xhigh | adaptive | max(or the subset each provider supports), defaulting to provider behavior when unset. - Provider adapters map it to native params:
- nearai / OpenAI-compat (DeepSeek V4, vLLM/SGLang):
chat_template_kwargs: {"thinking": false}andreasoning_effort/chat_template_kwargs.thinkinglevels - Gemini:
thinkingConfig.thinkingLevel/thinkingBudget - Anthropic:
thinking: {type: adaptive|enabled, budget_tokens} - OpenAI Codex:
reasoning.effort(replace hardcodedmedium)
- nearai / OpenAI-compat (DeepSeek V4, vLLM/SGLang):
- Thread a per-model
thinkingDefaultoverride through config (FEATURE_PARITY.md already tracks this as missing), plus a per-request override (UI effort picker is net-new). - Revisit the
content: null -> reasoning_contentfallback gating per provider/model while touching this surface.
Related
- #3673 — same file: openai_compatible/nearai drops
reasoning_contenton outgoing requests, breaks DeepSeek v4-pro multi-turn tool calls - #3436 — DeepSeek
reasoning_contentecho requirement (400 in thinking mode) - #3327 — surface and persist LLM reasoning content
- #84 — umbrella: agent advanced features incl. thinking modes
Source: nearai/ironclaw