Repository navigation
Fast requests are falsely classified as downgraded from ChatGPT backend service_tier metadata #2558
Description
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, commitec51e42d745d2645bcb22cb67855fa053ba1778e - 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=fastorpriorityopts 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:
- Fast mode / priority service tier is requested, but responses complete with service_tier=default openai/codex#30413 — Fast request contains
priority, butresponse.completedcontainsdefault
Fast mode / priority service tier is requested, but responses complete with service_tier=default openai/codex#30413
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:
- Codex sends
priority. - OpenCodex preserves and forwards
priority. - The ChatGPT Codex backend returns
defaultmetadata. - OpenCodex interprets that metadata using the public Platform API contract.
- 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
- Make response-tier confirmation destination-aware.
- For
authMode: "forward"+chatgpt.com/backend-api/codex:- record
responseServiceTier: default; - classify the Fast outcome as
unknownormetadata-mismatch, not confirmeddowngraded.
- record
- Reserve
response-declinedfor destinations whose documented response contract is known to be authoritative, such as the public Platform Responses API. - Add provenance fields, for example:
tierConfirmationSource: public-api | chatgpt-backend | inferredtierMetadataAuthoritative: boolean
- Show request tier and response metadata separately in the GUI.
- Add regression tests for:
- Platform API: request
priority, responsedefault=> downgraded. - ChatGPT Codex backend: request
priority, responsedefault=> unknown/metadata mismatch.
- Platform API: request
- 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
lidge-jun commentedon Aug 25, 2026
리뷰 · 우선순위 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
devHEAD 는 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 - 업스트림 메타데이터 불일치. 프록시 없이 같은 증상이 있다
메인테이너의 판단이 필요한 지점
너의 추천
이 이슈는 연다. 구현 풀을 기다린다. 확인을 목적지별로 갈라 chatgpt.com/backend-api/codex 에서는 metadata-mismatch 로 두고, 공개 플랫폼 API 에서만 response-declined 를 확정한다. 내가 머지하지 않고 구현하지 않는다. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.
이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Aug 25, 2026
Fixed on
devin #2589. Your suspicion was correct — this was a false negative, not a real downgrade.The ChatGPT-internal Codex backend returns
service_tier: defaulteven on turns it scheduled as priority, sofastwirewas reading that echo as a verdict it cannot give.TierObservationContextnow carriesresponseTierAuthoritative; absent means assume authoritative, so the public API path (where an echoeddefaultreally does mean declined) is unchanged, andcore.tssets it false only for the canonical ChatGPT-forward destination.Worth noting the second consequence you did not mention:
fastOutcomedrives 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.feat(gui): show whether the requested service tier was actually granted
feat(gui): show whether the requested service tier was actually granted