Skip to content

Fast requests are falsely classified as downgraded from ChatGPT backend service_tier metadata #2558

Description

@lidge-jun

Summary

OpenCodex reports every Codex Fast request as:

fastOutcome: downgraded
fastDowngradeReason: response-declined
responseServiceTier: default

However, the request is correctly sent as service_tier: "priority", and observed throughput differs between Fast and no-tier requests. This suggests the current diagnostic may be a false negative: the ChatGPT-internal Codex backend may return service_tier: "default" even when Fast scheduling is active.

This issue is split from #2557, which now tracks only the unrelated Windows --restart-desktop-app bug.

Environment

  • OpenCodex: 2.33.0, commit ec51e42d745d2645bcb22cb67855fa053ba1778e
  • Codex Desktop: 26.818.8289.0
  • Windows 11 x64
  • Model: gpt-5.6-sol
  • Auth mode: ChatGPT forwarded auth
  • Destination: https://chatgpt.com/backend-api/codex
  • Adapter: openai-responses
  • OpenCodex WebSockets: false

Evidence

Codex config:

service_tier = "priority"
openai_base_url = "http://127.0.0.1:10100/v1"

OpenCodex logs show:

callerServiceTier: priority
requestedServiceTier: priority
wireKind: service-tier
wireValue: priority
responseServiceTier: default
fastOutcome: downgraded
fastDowngradeReason: response-declined
confirmation: downgraded

There are 33 priority-request samples and 17 no-tier samples in the local usage log. A rough generation-rate estimate (output tokens divided by post-first-output duration where available) averaged approximately:

  • Priority requested: 116 tok/s
  • No tier: 98 tok/s

This estimate is noisy because many responses are short, so it is not proof that every request received Fast scheduling. It does show that responseServiceTier: default alone is insufficient evidence that no acceleration occurred.

OpenAI documentation

The public Responses API documentation says:

  • service_tier=fast or priority opts into Fast mode.
  • A Fast-served response should report service_tier=priority.
  • The response tier may differ from the requested tier because the response field reflects the processing mode actually used.

Reference:
https://developers.openai.com/api/reference/cli/resources/responses/methods/create

That contract is for the public Platform Responses API. OpenCodex is sending to the private ChatGPT Codex endpoint, chatgpt.com/backend-api/codex, whose response metadata may not have identical semantics.

Codex-rs analysis

Latest openai/codex main inspected at:

70b5cfc73b25458a7af225d24b16ef4794f8f380

Codex-rs normalizes Fast as follows:

ServiceTier::Fast => "priority"

Relevant file:

codex-rs/protocol/src/config_types.rs

The Responses request structure includes service_tier, and the WS conversion preserves it:

codex-rs/codex-api/src/common.rs

Codex-rs tests explicitly assert that Fast produces:

"service_tier": "priority"

for both ordinary and WebSocket requests. I did not find client logic that treats a completed response's service_tier=default as an authoritative Fast failure.

The upstream Codex repository already has an exact matching report:

This shows the metadata mismatch exists without OpenCodex and is likely produced by the ChatGPT Codex backend.

OpenCodex analysis

The outbound serializer is correct:

if (decision.kind === "set") next.service_tier = decision.value;

The questionable logic is in src/providers/fastwire.ts. Any observed non-priority response tier immediately becomes a confirmed downgrade:

if (canonicalFromWire(context.fastWire, value) === "priority") {
  outcome.fastOutcome = "applied";
  outcome.confirmation = "confirmed";
} else {
  outcome.fastOutcome = "downgraded";
  outcome.fastDowngradeReason = "response-declined";
  outcome.confirmation = "downgraded";
}

src/server/request-log.ts::applyResponseLogMetadata() applies this observer to every response/SSE payload containing service_tier.

This logic assumes the public Platform API response contract also applies to the ChatGPT-internal Codex endpoint. Existing Codex issue #30413 and the throughput observations make that assumption unsafe.

Likely error

This appears to be primarily an OpenCodex observability/classification error, not a request-routing error:

  1. Codex sends priority.
  2. OpenCodex preserves and forwards priority.
  3. The ChatGPT Codex backend returns default metadata.
  4. OpenCodex interprets that metadata using the public Platform API contract.
  5. The GUI/log labels it a confirmed downgrade even though the private endpoint's metadata is not proven authoritative.

Whether the backend metadata itself is wrong remains an upstream Codex/backend question tracked by openai/codex#30413.

Suggested fix

  1. Make response-tier confirmation destination-aware.
  2. For authMode: "forward" + chatgpt.com/backend-api/codex:
    • record responseServiceTier: default;
    • classify the Fast outcome as unknown or metadata-mismatch, not confirmed downgraded.
  3. Reserve response-declined for destinations whose documented response contract is known to be authoritative, such as the public Platform Responses API.
  4. Add provenance fields, for example:
    • tierConfirmationSource: public-api | chatgpt-backend | inferred
    • tierMetadataAuthoritative: boolean
  5. Show request tier and response metadata separately in the GUI.
  6. Add regression tests for:
    • Platform API: request priority, response default => downgraded.
    • ChatGPT Codex backend: request priority, response default => unknown/metadata mismatch.
  7. Optionally report measured TTFT/tok/s distributions, but do not use them as per-request proof of tier.

Expected behavior

OpenCodex should accurately distinguish:

  • what Codex requested;
  • what OpenCodex serialized;
  • what the destination reported;
  • whether that report is authoritative enough to confirm a downgrade.

It should not state that Fast was definitely downgraded when the evidence only establishes a metadata mismatch on an undocumented private endpoint.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions