Make reasoning summaries opt-in via a flag (with per-model override) for self-hosted reasoning models
Summary
For users running self-hosted reasoning models (DeepSeek R1, Qwen QwQ, Qwen-3 thinking, etc.) on Ollama / LM Studio / llama.cpp / vLLM, codex's bundled catalog has no entry for the slug, so supports_reasoning_summaries defaults to false and the reasoning stream never reaches the UI. The only existing escape hatch is the global model_supports_reasoning_summaries knob in ~/.codex/config.toml, which is all-or-nothing for users who run more than one local model.
The result is that running a real reasoning model locally feels strictly worse than running a non-reasoning model locally — the reasoning UI just doesn't show up, even though the model is emitting reasoning content.
Proposed solution
Two new provider-scoped fields on model_providers.<name>:
local_provider = true— marks a provider as serving locally-hosted models. When set, codex applies a conservative reasoning-name heuristic that auto-enablessupports_reasoning_summariesfor unknown models whose slug tokens match a known reasoning-model designator (r1,r2,r3,qwq,thinking, orreasoning, matched on alphanumeric segment boundaries — not substrings, sowarp1doesn't tripr1).
Built-in ollama and lmstudio providers default to local_provider = true. Every other provider (including OpenAI, Azure, Bedrock) defaults to false. User-defined providers opt in explicitly.
model_overrides.<slug>.supports_reasoning_summaries— explicit per-model escape hatch for cases the heuristic doesn't catch (e.g.qwen3-30b-a3b-thinking-2507is fine, butmyorg/some-reasoning-thingisn't), or to force-disable the heuristic for a specific model on a local provider. Bidirectional — bothtrueandfalseare honored.
Example config.toml:
[model_providers.my-vllm]
name = "my vllm cluster"
base_url = "http://localhost:8000/v1"
wire_api = "responses"
local_provider = true # heuristic enabled
[model_providers.my-vllm.model_overrides."my-custom-reasoning-model"]
supports_reasoning_summaries = true # explicit override
Precedence (lowest to highest)
- Model catalog default (existing behavior preserved).
- Heuristic auto-enable on
local_provider = truefor matching slugs. - Global
model_supports_reasoning_summaries = true(only enables;Some(false)still ignored to preserve existing semantics). - Per-model
model_overrides.<slug>.supports_reasoning_summaries(always wins).
Why a heuristic at all (and why this specific one)
A blanket auto-enable on local_provider = true would be over-reaching — strict OpenAI-compatible servers fronting non-reasoning models could 4xx on the unknown reasoning.summary request param. The heuristic limits the auto-enable to slugs that are unambiguously reasoning models. The allow-list is small and self-explanatory:
r1/r2/r3— DeepSeek R-series familyqwq— Qwen QwQ familythinking— Qwen-3 reasoning variantsreasoning— generic catch-all
Models that don't match still work fine, they just don't auto-enable. The model_overrides escape hatch covers them.
What I considered and rejected
- Auto-enable for every model on a local provider: too aggressive, would break strict servers running non-reasoning models.
- Per-model overrides only (no heuristic): solves correctness but doesn't actually fix the UX — users still have to write one override block per model.
- A new global
model_supports_reasoning_summaries_per_provider = trueknob: doesn't scale to the multi-model-per-provider case.
The combined heuristic + override design lets the 90% case work with one local_provider = true line, while keeping the 10% case correct via an explicit override.
deny_unknown_fields
ModelProviderOverride uses #[serde(deny_unknown_fields)] so typos like supports_reasoning_summary (missing trailing ies) are caught at config-load time instead of silently no-oping at runtime.
Reference implementation
A working implementation with tests lives on the team-wcv fork:
- Branch: https://github.com/team-wcv/codex/tree/feat/local-provider-reasoning-summaries
- Diff: https://github.com/team-wcv/codex/compare/main...feat/local-provider-reasoning-summaries
Touches:
codex-rs/model-provider-info/src/lib.rs— newlocal_provider: boolandmodel_overrides: Option<HashMap<…, ModelProviderOverride>>fields. Built-inollama/lmstudiodefault tolocal_provider = true;openai/bedrocktofalse.codex-rs/models-manager/src/config.rs— addsactive_provider_is_local: boolandmodel_provider_overrides: HashMap<String, ActiveModelOverride>toModelsManagerConfig.codex-rs/models-manager/src/model_info.rs— newlooks_like_reasoning_model(slug)helper plus the four-layer precedence logic inwith_config_overrides.codex-rs/core/src/config/mod.rs—to_models_manager_configresolves the active provider's overrides into the manager config.codex-rs/core/config.schema.json— regenerated viajust write-config-schema.- Tests: 9 new in
model_info_tests.rscovering precedence, gating, exact-slug keying, and bidirectional override; 6 new inmodel_provider_info_tests.rscovering built-in defaults, TOML round-tripping, anddeny_unknown_fields.
cargo test -p codex-models-manager -p codex-model-provider-info -p codex-config -p codex-core --lib passes ~1900 tests. cargo clippy --tests -- -D warnings and cargo fmt clean.
Open to alternatives
This is the shape that felt cleanest after weighing user UX against scope, but I'm open to alternatives — e.g. heuristic-only with no per-model override, override-only with no heuristic, or rejecting both and treating local reasoning models as something users should keep configuring manually. Happy to rework the patch shape if there's a direction you'd prefer.