codex icon indicating copy to clipboard operation
codex copied to clipboard

model_reasoning_effort not forwarded to third-party providers (Chat never sends `reasoning_effort`; Responses gated by model slug)

Open teble opened this issue 2 months ago • 1 comments

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"
  1. Run Codex once (any trivial prompt, e.g., “test”).
  2. 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

teble avatar Dec 04 '25 14:12 teble

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

  • #6375
  • #5775

Powered by Codex Action

github-actions[bot] avatar Dec 04 '25 14:12 github-actions[bot]

+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?

kentyman23 avatar Dec 07 '25 17:12 kentyman23