model_reasoning_effort not forwarded to third-party providers

Resolved 💬 4 comments Opened Dec 4, 2025 by teble Closed Mar 29, 2026

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 ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗