Skip to content

Chained clients (provider hub) drop per-model context windows the hub already serves — every routed row materializes at the 128k fallback #4032

Closed

Description

@tizerluo

Client or integration

Other

Area

Catalog / models

Summary

A chained client (single provider hub pointing at a remote hub data plane, liveModels: true — the provider-based chained setup, not ocx connect) materializes every hub-routed row with context_window: 128000 — the parser's compatibility fallback — even though the hub already serves correct per-model windows on two surfaces. Effect: Codex compacts at ~121.6k effective tokens on 1M-class models (cursor GPT-5.6, zai GLM-5.3), silently discarding ~87% of usable context on chained machines.

Reproduction

  1. Hub 2.48.0, runtimeRole: hub, per-provider modelContextWindows configured. The hub's own surfaces carry the truth:
    • GET /v1/catalog (the shared serializer used by the dashboard and the data-plane route): all 436 entries have context_window > 0, e.g. "slug":"cursor/gpt-5.6-sol","context_window":1000000, "slug":"zai/glm-5.3-flash","context_window":1000000, "slug":"alibaba-token-plan-intl/qwen3.8-max","context_window":983616.
    • GET /v1/models: "id":"cursor/gpt-5.6-sol","capabilities":{"context_length":922000,"max_output_tokens":128000,...} — per-model metadata is present here too.
  2. Client 2.48.0: single provider hub (baseUrl → hub data plane /v1, liveModels: true, allowPrivateNetwork: true), ocx service install + ocx sync.
  3. Client catalog ~/.codex/opencodex-catalog.json: every hub/* row = "context_window": 128000, "max_context_window": 128000. hub/cursor-gpt-5.6-sol 128000 (real 1,000,000), hub/zai-glm-5.3-flash 128000 (real 1,000,000), hub/alibaba-token-plan-intl-qwen3.8-max 128000 (real 983,616). Only rows backed by the client's own local openai forward provider keep real roster values (272000) — the loss is specific to routed rows.
  4. Where it drops: routed-row assembly never maps the hub-served metadata into the discovered window (applyProviderConfigHints in src/codex/catalog/provider-fetch.ts sees no discovered value), so src/codex/catalog/parsing.ts applies its 128000 compatibility fallback at materialization.
  5. Workaround we run today (effective, but per-client and manual): set providers.hub.modelContextWindows on each client, keyed by the /v1/models id space, plus modelMaxInputTokens. configuredContextWindow() fills the value because discovery is absent, and the gather fingerprint (providerCatalogFingerprint includes ctxW) invalidates the cache so the next ocx sync picks it up. Result: 227/449 rows corrected on one client, 220/441 on another; a streamed call through hub/gpt-5.6-luna completes.

Papercut discovered while keying that map: the two hub surfaces disagree on id shape for nested vendors — catalog slugs are single-slash (zenmux/openai-gpt-5.6-sol) while /v1/models ids are multi-slash (zenmux/openai/gpt-5.6-sol), and the client flattens all slashes to -. Any cross-referencing code (or operator) has to collapse both sides to match.

Version

2.48.0 (hub and clients)

Operating system

Hub: Ubuntu 24.04 (Contabo VPS); clients: macOS 26 and Ubuntu VPS

Provider and model

Any hub-routed provider. Observed: cursor/gpt-5.6-sol (1,000,000 → 128,000), zai/glm-5.3-flash (1,000,000 → 128,000), alibaba-token-plan-intl/qwen3.8-max (983,616 → 128,000)

Logs or error output

No error — silent metadata loss. Verified comparison:

model                                        hub /v1/catalog   hub /v1/models capabilities   client catalog after sync
cursor/gpt-5.6-sol                           1,000,000         context_length 922,000          128,000
zai/glm-5.3-flash                            1,000,000         —                              128,000
alibaba-token-plan-intl/qwen3.8-max          983,616           —                              128,000
gpt-5.6-sol (hub's own openai roster row)    272,000           context_length 922,000          128,000 (routed) vs 272,000 (local native row — correct)

Screenshots and supporting files

No response

Redacted configuration

Client (chained setup):

{
  "providers": {
    "hub": {
      "adapter": "openai-responses",
      "baseUrl": "http://<hub-tailnet-ip>:10100/v1",
      "allowPrivateNetwork": true,
      "authMode": "key",
      "liveModels": true
    }
  },
  "defaultProvider": "hub"
}

Workaround added per client (keys in /v1/models id space):

{
  "providers": {
    "hub": {
      "modelContextWindows": {
        "cursor/gpt-5.6-sol": 1050000,
        "zenmux/openai/gpt-5.6-sol": 1050000,
        "zai/glm-5.3-flash": 1000000
      },
      "modelMaxInputTokens": { "cursor/gpt-5.6-sol": 922000 }
    }
  }
}

Suggested fix

  1. When assembling routed rows for a provider whose listing carries per-model metadata, map capabilities.context_length / max_output_tokens from GET /v1/models into the discovered window instead of dropping it — that alone fixes provider-based chained clients with zero operator config.
  2. Alternatively (or additionally), have the provider-based sync path consume the same GET /v1/catalog payload the connect flow already downloads — it carries the operator-curated modelContextWindows truth byte-for-byte.
  3. Worth preserving while fixing: bare native rows correctly keep the roster's plan-effective window today (e.g. 272000 for GPT-5.6 via a ChatGPT plan, where pricing.overrides.min_prompt_tokens: 272000 applies); routed API-billed rows should get the API truth instead of the 128k floor.
  4. Papercut: unify catalog-slug vs /v1/models-id shape for nested vendors (single- vs multi-slash) so the two surfaces can be cross-referenced without collapsing separators.

Activity

  1. added
    bugSomething isn't working
    catalogModel catalog, slugs, visibility, routed entries
    platformOS/service/tray/ACL (Windows-heavy, not Windows-only)
    serviceService lifecycle (WinSW/launchd/scheduler)
    on Sep 8, 2026
  2. lidge-jun commented on Sep 8, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 70 / 80

    이 이슈는 tizerluo가 보고한 허브 체이닝 메타데이터 유실이다. 허브(runtimeRole: hub)는 GET /v1/catalog와 GET /v1/models에 모델별 context window를 이미 실어 보낸다. 예: cursor/gpt-5.6-sol catalog 1,000,000, models API capabilities.context_length 922,000. 그런데 클라이언트가 provider 하나(hub, liveModels: true, data-plane /v1)로만 붙으면, ocx sync 뒤 ~/.codex/opencodex-catalog.json의 모든 hub/* 행이 context_window: 128000으로 떨어진다. Codex는 대략 121.6k에서 컴팩트한다. 1M급 모델에서 쓸 수 있는 문맥의 대부분을 에러 없이 버린다. 로컬 openai forward 행만 정상(272000)이고, 유실은 routed 행에만 난다.

    지금 dev HEAD 29bb221c3의 코드를 보면 원인 후보가 바로 보인다. src/codex/catalog/provider-fetch.ts의 catalogHintsFromModelsApiItem은 context window를 limits.max_context_length, capabilities.limits.max_context_window_tokens, metadata.context_length, 최상위 item.context_length 등에서 읽는다. 그런데 허브 /v1/models가 쓰는 capabilities.context_length는 읽지 않는다. 같은 함수는 capabilityRecord?.max_output_tokens는 이미 읽는다. 즉 capabilities 객체의 출력 한도는 먹고, context_length는 안 먹는 비대칭이다. discovered window가 비면 applyProviderConfigHints가 채워 줄 값이 없고, src/codex/catalog/parsing.ts 물질화 단계에서 호환 폴백 128000이 들어간다. 작성자가 쓴 워크어라운드(providers.hub.modelContextWindows를 /v1/models id 공간으로 수동 기입)는 configuredContextWindow() 경로라서 동작한다. 다만 클라이언트마다 손으로 맞춰야 한다.

    점수는 70이다. 재현이 구체적이고, HEAD에서 경로가 확인되며, hub 운영자에게 침묵 손실이 크다. tip(#4002)과는 다른 축이지만 catalog/platform 버그 우선순위는 높다. 부가로 catalog slug(zenmux/openai-gpt-5.6-sol)와 /v1/models id(zenmux/openai/gpt-5.6-sol)의 슬래시 모양이 달라 교차 참조가 깨지는 papercut도 같이 적혀 있다. 이건 본버그와 묶어도 되고, 따로 잘라도 된다.

    라인 / 경로로 보면 문제·확인할 지점은 이렇다.

    src/codex/catalog/provider-fetch.ts catalogHintsFromModelsApiItem - capabilityRecord?.context_length를 positiveSafeInteger 후보에 넣지 않는다. 허브가 내려주는 바로 그 필드를 놓친다
    src/codex/catalog/provider-fetch.ts 같은 함수의 maxOutputTokens - capabilityRecord?.max_output_tokens는 이미 읽는다. context만 빠진 비대칭이 버그의 핵심 증거다
    src/codex/catalog/parsing.ts 물질화 폴백 128000 - discovered/configured가 없을 때의 호환 기본값이다. 폴백 자체를 없애기보다, discovery가 hub capabilities를 먹게 고치는 쪽이 맞다
    허브 GET /v1/catalog vs 클라이언트 liveModels 경로 - connect 플로우는 catalog payload를 쓰지만, provider 기반 체이닝은 models API discovery에 기대는 것으로 보인다. catalog의 operator-curated modelContextWindows를 sync에 태울지도 설계 선택이다
    id 모양 papercut - nested vendor에서 catalog 단일 슬래시 vs models 다중 슬래시. modelContextWindows 키와 교차 참조를 어렵게 만든다. 본수정과 분리 가능

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

    • 최소 수정(capabilities.context_length 읽기)만 할지, provider sync가 /v1/catalog도 소비하게 할지
    • hub models의 922k와 catalog의 1M 중 체이닝 클라이언트가 권위로 삼을 표면
    • id 정규화(슬래시 접기)를 이번 수정에 묶을지 후속으로 미룰지
    • 2.48.0 사용자 워크어라운드 문서화만 먼저 내보낼지

    너의 추천
    도움 환영 / 작은 패치로 고치세요. 1순위는 catalogHintsFromModelsApiItem에 capabilityRecord?.context_length를 넣고, hub /v1/models fixture로 회귀 테스트를 고정하는 것이다. 그보다 크면 liveModels sync가 hub catalog의 per-model window를 함께 먹게 하면 작성자 제안 2번과 맞는다. 워크어라운드는 임시로 두고, 랜딩 후 릴리즈 노트에 “체이닝 클라이언트 128k 폴백”을 명시하세요. types/config 대분할과 무관하다. 중복 이슈로 당장 닫을 근거는 없다.

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

  3. added a commit that references this issue on Sep 8, 2026
    ff524ae
  4. lidge-jun commented on Sep 8, 2026

    @lidge-jun
    Owner

    Fixed on dev in ff524ae (via #4058).

    catalogHintsFromModelsApiItem already read the hub's capability record for max_output_tokens but never for the window, so a hub serving capabilities.context_length: 922000 produced a window-less row and materialization applied the 128k floor. One argument is appended last to the existing positiveSafeInteger chain, so any provider that already resolves a window — Command Code and Featherless publish a top-level context_length, which still wins — is unaffected. A hub row that previously materialized at 128000 now materializes at its real value.

    Regression test tests/codex-integration/catalog-hub-context-window.test.ts covers both shapes the hub can send, pins the ordering contract, and rejects zero, negative, and string values. It fails on dev without the fix (4 pass / 2 fail).

    Closing manually because pull requests here target dev, and GitHub only auto-closes on merge into main.

  5. added a commit that references this issue on Sep 17, 2026
    a34ec9d
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

    bugSomething isn't workingcatalogModel catalog, slugs, visibility, routed entriesplatformOS/service/tray/ACL (Windows-heavy, not Windows-only)serviceService lifecycle (WinSW/launchd/scheduler)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions