Feature: configurable `reasoning_effort` for OpenAI-compatible providers
Summary
Many models people run behind the OpenAI-compatible provider today are reasoning models (Qwen3, DeepSeek-R1 distills, gpt-oss, GPT-5.x). For structured batch work — auto-categorization, merchant detection, bill suggestions — they routinely spend most of their output budget "thinking" before emitting the JSON Sure asked for. The OpenAI Chat Completions API and most local runtimes (llama.cpp server, vLLM, LM Studio, Ollama's OpenAI endpoint) accept a reasoning_effort request parameter (none | minimal | low | medium | high); the Responses API takes the same thing as reasoning: { effort: … }. Sure has no way to set it.
Motivation
Observed with a local Qwen3 deployment: a 20-transaction categorization batch would exhaust the context/output budget mid-reasoning and fail JSON parsing (see the related Auto-mode fallback bug). The identical request with reasoning_effort: "none" returned complete, correct JSON in a fraction of the time. Today the only workarounds are model-specific prompt hacks (/no_think) or a proxy that injects the parameter.
Proposal
- New optional setting, ENV > Setting > unset, mirroring
LLM_JSON_MODE/openai_json_mode:OPENAI_REASONING_EFFORTenv varSetting.openai_reasoning_efforteditable in Settings → Self-hosting → OpenAI (select: Provider default,none,minimal,low,medium,high), disabled when the env var is set — same UX as JSON mode.
- Compatibility rule: when unset, the parameter is not sent at all. No behaviour change for existing installs. No global default of
none. - Applied consistently to every OpenAI-compatible request Sure builds: chat (Responses and Chat Completions paths), auto-categorizer, merchant detector, provider-merchant enhancer, bill setup suggester, bank-statement extractor, PDF processor. Not applied to the AI health probe (it should keep exercising the raw endpoint).
- Documented in
docs/hosting/ai.mdwith the caveat that the official OpenAI API rejectsreasoning_efforton non-reasoning models (e.g.gpt-4.1→ HTTP 400), so it should only be set when the configured model supports it.
Acceptance criteria
- Unset → request payloads byte-identical to today (test: no
reasoning_effort/reasoningkey). - Set via ENV or Setting → Chat Completions requests include
reasoning_effort: <value>; Responses requests includereasoning: { effort: <value> }. - Invalid values are ignored with a logged warning rather than sent.
- ENV wins over Setting; UI select disabled when ENV is set.
- Hosting docs updated.
Source: we-promise/sure