model_reasoning_effort not forwarded to third-party providers

Resolved 💬 4 comments Opened Dec 4, 2025 by teble Closed Mar 29, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex is running?

codex-cli 0.64.0

What subscription do you have?

gpt-5.1-codex via ZenMux API

Which model were you using?

gpt-5.1-codex

What platform is your computer?

Microsoft Windows NT 10.0.19045.0 x64

What issue are you seeing?

Configured model_reasoning_effort = "high" (and model_reasoning_summary = "auto"). When routing via a custom provider (base_url points to a relay that adds an openai/ prefix to model names), Codex does not forward reasoning controls:

1) Chat wire (POST /v1/chat/completions): Body never contains a top‑level "reasoning_effort" key.
2) Responses wire (POST /v1/responses): "reasoning" becomes null unless the model slug matches specific patterns (appears tied to CONTEXT_WINDOW_272K‑related checks). With the relay’s openai/ prefix, the slug check never matches, so "reasoning" stays null.

Representative (redacted) downstream log (Chat wire):
POST /chat/completions
Headers: { "authorization": "Bearer ****(masked)", "accept": "text/event-stream", ... }
JSON Body:

{
  "model": "openai/gpt-5.1-codex",
  "messages": [
    {"role":"system","content":"..."},
    {"role":"user","content":"test"}
  ]
  // No "reasoning_effort" present
}

Code pointer found while investigating:

pub(crate) fn get_model_info(model_family: &ModelFamily) -> Option<ModelInfo> {
    let slug = model_family.slug.as_str();
    match slug {
        // ...
        _ if slug.starts_with("gpt-5-codex")
            || slug.starts_with("gpt-5.1-codex")
            || slug.starts_with("gpt-5.1-codex-max") => {
            Some(ModelInfo::new(CONTEXT_WINDOW_272K))
        }
        _ if slug.starts_with("gpt-5") => Some(ModelInfo::new(CONTEXT_WINDOW_272K)),
        _ if slug.starts_with("codex-") => Some(ModelInfo::new(CONTEXT_WINDOW_272K)),
        _ if slug.starts_with("exp-") => Some(ModelInfo::new(CONTEXT_WINDOW_272K)),
        _ => None,
    }
}

Observation (correlation, not proof): In our testing, models whose names match the above prefixes tend to produce a non-null "reasoning" in responses mode, while names that do not match (relay prefixes them with openai/…) result in "reasoning": null.

What steps can reproduce the bug?

1) Start a local transparent proxy to print requests and forward to the real third‑party relay (any simple HTTP proxy that dumps JSON is fine).
2) Configure Codex to use a custom provider with that proxy’s base_url.

config.toml (redacted)

model = "openai/gpt-5.1-codex"
model_provider = "my3p"
model_reasoning_effort = "high"
model_reasoning_summary = "auto"

[model_providers.my3p]
name = "ThirdParty"
base_url = "http://127.0.0.1:8787/v1"   # points to local proxy
env_key  = "THIRDPARTY_API_KEY"
wire_api = "chat"                        # also test "responses"

3) Run Codex once (any trivial prompt, e.g., “test”).
4) Inspect the proxy logs.

Observed:

  • With wire_api="chat" → body lacks "reasoning_effort".
  • With wire_api="responses" → "reasoning" is null unless the model slug matches internal patterns; when the relay prefixes model with openai/…, it never matches, so "reasoning" stays null.

Note: The relay consistently rewrites the model name to include openai/ (e.g., openai/gpt-5.1-codex).

What is the expected behavior?

  • When model_reasoning_effort is set in config:
  • Chat wire should include a top‑level "reasoning_effort": "<low|medium|high|minimal>" in the request body.
  • Responses wire should include "reasoning": { "effort": "<…>", "summary": "<auto|…>" }".
  • Emission of these fields should not depend on model‑name heuristics or context‑window families. Providers that don’t support the field can reject it server‑side.

Additional information

_No response_

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 8 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #6375
  • #5775

Powered by Codex Action

kentyman23 · 8 months ago

+1 on this issue.

Another use case is when the model name on the deployment doesn't match at all. So maybe we want abstract deployments called default and think-harder that happen to map behind the scenes to gpt-5.1-codex and gpt-5.1-codex-max, respectively. Ideally, Codex CLI wouldn't need to interrogate my model names to try to guess whether I'm allowed to change things like model_reasoning_effort and should instead trust that I know what I'm doing. Furthermore, when gpt-5.2-codex models come out, I shouldn't necessarily have to update Codex CLI in order for it to work.

I do realize that Codex is interrogating names for good reasons, like deciding which prompts to use. Perhaps there should be a more explicit setting like model_family that can be set in config.toml to allow this?

etraut-openai contributor · 5 months ago

This bug report hasn't received any upvotes or additional posts in four months. Closing.

kdejaeger · 3 months ago

Is there a way to verify if the configured model_reasoning_effort is correctly passed to openrouter. I have configured for example this

[profiles.deepseek]
model_provider = "openrouter"
model = "deepseek/deepseek-v4-pro"
model_reasoning_effort = "xhigh"
model_context_window = 1048576

In https://openrouter.ai/logs, I see reasoning tokens are being used, but I cannot verify if the model_reasoning_effort is coming in correctly.