Skip to content

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

Closed

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.

Activity

  1. lidge-jun commented on Aug 25, 2026

    @lidge-jun
    OwnerAuthor

    리뷰 · 우선순위 53 / 80

    설명: 이 이슈는 코덱스 Fast 요청을 프록시가 항상 downgraded / response-declined 로 분류한다고 적는다. 요청 wire 는 service_tier=priority 로 맞고, 관측 처리량은 Fast 와 no-tier 가 다르다. 작성자는 lidge-jun 이다. 라벨은 없다. 2557 에서 갈라졌다. 보고 버전은 2.33.0 이고 검사 커밋은 origin/main ec51e42 이다. 모델은 gpt-5.6-sol 이다. 인증은 챗지피티 전달이다. 목적지는 chatgpt.com/backend-api/codex 다. 어댑터는 openai-responses 다. 웹소켓은 꺼져 있다. 지금 CURRENT dev HEAD 는 e1fb675 이다. 이번 시간에 SHA 는 안 움직였다. 같은 분류기가 HEAD 에도 있다. 이슈를 연다. 닫지 말 것. 라벨은 바꾸지 말 것. 내가 머지하지 않는다.

    HEAD 에서 재현 길을 읽었다. src/providers/fastwire.ts 363-372줄 observeResponseServiceTier 는 응답 값이 canonicalFromWire 로 priority 가 아니면 바로 fastOutcome=downgraded, fastDowngradeReason=response-declined, confirmation=downgraded 다. 목적지를 보지 않는다. src/server/request-log.ts 607-625줄 applyResponseLogMetadata 는 페이로드에 service_tier 가 있으면 그 관찰자를 매 응답과 SSE 에 호출한다. 공개 플랫폼 Responses 계약은 Fast 가 실제로 쓰이면 응답도 priority 라고 적는다. 챗지피티 내부 코덱스 엔드포인트는 그 계약이 문서화되어 있지 않다. 본문이 적은 openai/codex 30413 도 같은 메타데이터 불일치를 프록시 없이 보고한다.

    시험 tests/fastwire-observability.test.ts 155-167줄이 그 가정을 고정한다. 요청 priority, 응답 default 이면 response-declined 가 맞다고 단언한다. 플랫폼 API 에는 그 시험이 맞다. chatgpt-backend 분기는 없다. 그래서 고치려면 시험도 목적지를 갈라야 한다. 본문 제안 1-3 이 그 길이다. 확인 출처를 public-api 와 chatgpt-backend 와 inferred 로 나누고, 내부 엔드포인트에서는 unknown 또는 metadata-mismatch 로 둔다. tok/s 분포는 요청 단위 증거가 될 수 없다고 본문이 이미 적는다. 그 점은 맞다.

    이건 라우팅 구멍이 아니다. 직렬화가 kind 가 set 일 때 next.service_tier 에 decision.value 를 넣는 길은 본문대로 맞다. 로그의 callerServiceTier, requestedServiceTier, wireValue 가 모두 priority 다. 잘못된 것은 응답 메타데이터를 공개 API 권위로 읽은 관찰기다. 2557 의 파워셸 join 과는 다른 단이다. 다시 합치지 말 것. 2455 는 대기열 지연과 granted tier 를 표면에 보여 달라는 기능 이슈다. 이 이슈로 닫지 말 것. 2548 의 솔 테라 루나 게이트와도 다르다. 2550 이 그 게이트다. 서로 닫지 말 것.

    origin/dev 패키지는 2.32.1-preview.20260825 다. 보고 버전 2.33.0 은 origin/main 이다. fastwire.ts 관찰기는 두 트리에 같다. 이 이슈를 메인 전용으로 닫지 말 것. src/runtime 은 없다. default-aliases.ts 와 model-presets.ts 도 없다. 2463 2464 2465 를 닫지 말 것. types.ts 와 config.ts 가르기와는 무관하다. 프리뷰 배포가 아니다. 구현 풀은 아직 없다. 내가 구현하지 않는다.

    src/providers/fastwire.ts 363-372줄 HEAD - 응답이 priority 가 아니면 목적지를 보지 않고 response-declined 로 확정한다
    src/providers/fastwire.ts 259-267줄 HEAD - canonicalFromWire 는 선언 매핑만 본다. 엔드포인트 권위가 없다
    src/server/request-log.ts 619-625줄 HEAD - service_tier 가 있는 응답마다 그 관찰자를 호출한다
    tests/fastwire-observability.test.ts 155-167줄 HEAD - 응답 default 를 플랫폼 계약의 거절로 고정한다. chatgpt-backend 분기가 없다
    2557 - 윈도우 데스크톱 재시작이다. 이 이슈와 다시 합치지 말 것
    2455 - 대기열 지연 표면이다. 이 이슈로 닫지 말 것
    openai/codex 30413 - 업스트림 메타데이터 불일치. 프록시 없이 같은 증상이 있다

    메인테이너의 판단이 필요한 지점

    • 이 이슈를 지금 닫을지. 닫지 말 것. HEAD 분류기가 목적지를 안 본다
    • 내부 코덱스 엔드포인트에서 응답 default 를 unknown 으로 둘지. 그 편이 맞다. 확정 downgraded 는 공개 API 에만 둔다
    • tok/s 를 요청 단위 증거로 쓸지. 쓰지 말 것. 본문이 이미 노이즈라고 적는다
    • 2558 과 2557 을 다시 합칠지. 합치지 말 것
    • 2455 를 같이 닫을지. 닫지 말 것

    너의 추천
    이 이슈는 연다. 구현 풀을 기다린다. 확인을 목적지별로 갈라 chatgpt.com/backend-api/codex 에서는 metadata-mismatch 로 두고, 공개 플랫폼 API 에서만 response-declined 를 확정한다. 내가 머지하지 않고 구현하지 않는다. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.

    이 댓글은 grok-bot이 작성했습니다

  2. lidge-jun commented on Aug 25, 2026

    @lidge-jun
    OwnerAuthor

    Fixed on dev in #2589. Your suspicion was correct — this was a false negative, not a real downgrade.

    The ChatGPT-internal Codex backend returns service_tier: default even on turns it scheduled as priority, so fastwire was reading that echo as a verdict it cannot give. TierObservationContext now carries responseTierAuthoritative; absent means assume authoritative, so the public API path (where an echoed default really does mean declined) is unchanged, and core.ts sets it false only for the canonical ChatGPT-forward destination.

    Worth noting the second consequence you did not mention: fastOutcome drives priority cost attribution, so this was skewing pricing as well as diagnostics.

    The raw echo is still recorded in responseServiceTier — this suppresses a verdict, not the evidence. Driven red first: removing the guard fails exactly the new case; 32 pass with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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