Surface server-declined service tiers (Fast mode silently falls back to standard in codex exec)

Open 💬 2 comments Opened Jul 10, 2026 by colindotfun

What variant of Codex are you using?

CLI (codex-cli 0.144.0, macOS, ChatGPT auth)

What feature would you like to see?

Two related asks around Fast mode in non-interactive use:

1. Warn when the serviced tier differs from the requested tier. #31284 added a client-side warning when the configured service tier isn't advertised by the model catalog. The silent case remains: the request goes out with the tier and the server declines it. response.completed already reports the tier that actually serviced the request, so the client has what it needs to surface "Fast mode was requested but this turn ran on standard."

2. Clarify (and ideally support) Fast mode for codex exec. Related: #22622. Verified on 0.144.0 and an instrumented build of main (6138909d6e): with service_tier = "priority" in config.toml (what /fast on persists) and the fast_mode feature enabled, codex exec turns DO transmit "service_tier":"priority" in the websocket response.create payload — but the backend echoes service_tier:"auto" and response.completed reports "default". Same result on gpt-5.6-sol and gpt-5.5, so it is not model-specific. If Fast mode is intentionally unavailable for headless traffic, documenting that (plus the warning from ask 1) would save users the investigation; if it's meant to work, honoring the transmitted tier would be even better.

Additional information

Repro:

# config.toml: service_tier = "priority"; features fast_mode enabled
RUST_LOG=trace codex exec -m gpt-5.6-sol -c 'service_tier="priority"' "Reply with exactly one word: ok"
# trace shows the outbound response.create payload carrying "service_tier":"priority",
# response.created echoes "auto", response.completed reports "default"

Why headless matters: the Codex plugin for Claude Code (https://github.com/openai/codex-plugin-cc) delegates review/rescue work through the local Codex CLI and app server, so codex exec-style non-interactive runs are becoming a primary surface. Plugin users who configure Fast mode currently get the standard tier with no signal anywhere that their setting isn't taking effect.

View original on GitHub ↗

2 Comments

oleksoleksoleks · 1 month ago

Thanks for the existing report. I reproduced and extended this on macOS with the latest standalone CLI available during the investigation (codex-cli 0.144.5, ChatGPT auth, fast_mode enabled, Responses WebSocket handshake HTTP 101).

This appears closely related to #30413 and #32191, so I am adding evidence here rather than opening a duplicate issue.

Additional environment

  • OS: macOS Apple Silicon
  • Codex: 0.144.5 (updated with codex update from 0.144.1)
  • Auth: ChatGPT refresh-token auth, not API key
  • Transport: Responses WebSocket (transport="responses_websocket", wire_api=responses)
  • Default configured model: gpt-5.6-terra
  • Models tested: gpt-5.5, gpt-5.6-luna, gpt-5.6-terra, explicit gpt-5.3-codex-spark

codex doctor --json reported overallStatus: "ok", fast_mode enabled, ChatGPT auth configured, and a successful Responses WebSocket handshake.

Catalog result

I queried the live app-server model/list response on 0.144.5:

models returned: 4
models with non-empty serviceTiers/additionalSpeedTiers: 0

The four returned models were:

gpt-5.6-terra
  serviceTiers=[] additionalSpeedTiers=[] defaultServiceTier=null
gpt-5.6-luna
  serviceTiers=[] additionalSpeedTiers=[] defaultServiceTier=null
gpt-5.5
  serviceTiers=[] additionalSpeedTiers=[] defaultServiceTier=null
gpt-5.4-mini
  serviceTiers=[] additionalSpeedTiers=[] defaultServiceTier=null

This differs from #30413, which reports a local model cache advertising priority/Fast for gpt-5.5. That cache-vs-app-server discrepancy may itself be relevant.

Effective-tier probe

I drove app-server JSON-RPC directly with fresh ephemeral threads.

GPT-5.5 without a tier:

{"model":"gpt-5.5","ephemeral":true}

GPT-5.5 with an explicit priority tier:

{"model":"gpt-5.5","ephemeral":true,"serviceTier":"priority"}

Both thread/start responses returned:

{"model":"gpt-5.5","serviceTier":null}

The same normalization to null occurred for Spark with both serviceTier: "priority" and serviceTier: "fast". Spark is not present in model/list, but explicit model selection is accepted.

This indicates that this app-server/model-resolution path removes the tier before the model request. It also means a timing comparison using these requests cannot establish backend priority behavior.

Streaming measurements

I timestamped live agent-message delta notifications over app-server stdio JSON-RPC and captured thread/tokenUsage/updated output-token counts. The prompt was the same approximate-10,000-word Unix process architecture prompt for each run.

GPT-5.5 result:

                         default       priority request
output tokens             15,467        25,631
stream duration            280.660 s      464.105 s
mean delta gap               18.0 ms        18.0 ms
output tok/s                 55.11          55.23
output tok/s after TTFT      ~55.96         ~55.83

The output lengths differed, and the priority request was not confirmed as effective (thread/start resolved it to null). The useful observation is that observed generation cadence was the same: approximately 55 output tokens/second and 18 ms per streamed delta. These runs should not be interpreted as proof that the backend ignored a valid priority request.

Relevant source behavior

In codex-rs, the lower-level request format supports the field:

pub service_tier: Option<String>,

The WebSocket conversion copies it into response.create, and there is a serialization test using service_tier: Some("priority").

Separately, the TUI service-tier resolver only retains a configured tier when the selected model advertises it in service_tiers; otherwise it returns None. The live model/list result above advertises no tiers.

Requested clarification/fix

Could the team clarify which of these is expected?

  1. Fast/priority is unavailable for this ChatGPT account and these models. If so, please expose that clearly and avoid presenting /fast as active.
  2. Fast/priority is available, but model/list is stale/incomplete. If so, please publish the supported tiers and effective defaults in the app-server catalog.
  3. Fast/priority is sent but can be declined by the backend. If so, please preserve the raw response.created/response.completed service-tier metadata and surface a warning when the requested tier differs from the serviced tier.

For a definitive client/backend diagnostic, it would help if app-server exposed:

requested service tier
outgoing response.create service_tier
backend response.created response.service_tier
backend response.completed response.service_tier

The key issue is that a user-visible Fast setting can currently collapse to null before the request, or potentially be downgraded by the backend, without an observable reason. That makes it impossible to tell whether Fast is active or to evaluate its performance impact.

Related: #30413, #32191.

limichange · 5 days ago

I reproduced this on the current CLI with a controlled output-length benchmark. This adds one important data point beyond the earlier reports: the request includes the newer x-codex-routing-hint tier signal, but Fast still has no measurable effect on generation speed.

Environment

  • Codex CLI: 0.149.0
  • Platform: Darwin 25.5.0 arm64 arm
  • Auth: ChatGPT sign-in (not an API key)
  • Subscription: Plus
  • Model: gpt-5.6-sol
  • Reasoning effort: xhigh
  • Upstream: ChatGPT Codex backend directly; no custom model provider or third-party gateway
  • Transport: HTTP/SSE, captured by a local transparent recorder

The live model catalog advertised Fast for gpt-5.6-sol with service_tiers=[{id: "priority", name: "Fast"}] and additional_speed_tiers=["fast"].

Wire-level verification

For every Fast run, CLI 0.149.0 sent all of the expected signals:

request body service_tier: priority
x-codex-routing-hint: model=gpt-5.6-sol;tier=priority
x-openai-internal-codex-responses-lite: true
user-agent: codex_exec/0.149.0 (...)
reasoning.effort: xhigh

All requests returned HTTP 200. response.completed.response.service_tier was default for both Standard and Fast.

Controlled A/B result

I ran three Standard requests and three Fast requests in a balanced order, using the same account, model, effort, machine, prompt, and transport. The prompt was:

Output exactly the integers 0001 through 1000 in ascending order, separated by single spaces. No prose, no code fence, no omissions.

Every response produced exactly 3,005 non-reasoning output tokens, so the durations are directly comparable.

| Mode | Durations | Mean | Aggregate visible output rate |
|---|---:|---:|---:|
| Standard | 57.232s, 56.915s, 58.142s | 57.430s | 52.32 tok/s |
| Fast (priority on wire) | 56.797s, 57.864s, 57.861s | 57.507s | 52.25 tok/s |

Fast was 0.14% slower on the mean, which is effectively identical. This is far from the documented 1.5x speed increase. At the observed Standard rate, a 1.5x Fast run would be expected to finish in roughly 38 seconds rather than 57.5 seconds.

This conclusion does not rely on response.service_tier=default alone: the measured output cadence itself is the same across both modes.

Why this seems actionable

Earlier reports predated the model/tier routing hint added in #37345. On 0.149.0, the catalog advertises Fast and the client sends both service_tier=priority and the tier=priority routing hint, yet the backend behavior is still indistinguishable from Standard.

Could the CLI/backend expose whether Fast was accepted, rejected, or downgraded, and why? If Fast cannot be applied for a turn, /fast status should not present it as effectively active without a warning.

Related: #30413. Official documentation: https://learn.chatgpt.com/docs/agent-configuration/speed#fast-mode