feat: context-length tier routing (262k fleet vs 1M tier, best-effort Chutes overflow) - #860
Conversation
… Chutes)
A NEAR-served model can now run two capacity tiers behind one canonical id
(glm-5.2: 262k 2xTP4 fleet + 1M TP8 host on glm-5-2-long.completions.near.ai,
cvm-compose-files#129). The model row's provider_config declares the tier:
{"long_context": {"inference_url": "https://glm-5-2-long.completions.near.ai",
"max_context_tokens": 1048576,
"base_max_context_tokens": 262144}}
and the pool registers a second nearai provider under the same id via the
normal inference-url flow (expand_inference_endpoints — identity expansion
for every other model).
Routing (builds on #838's context_overflow sort key):
- Best-fit capacity ordering within a tier: short requests stay on the base
fleet (the single long host is their 5xx failover, not a round-robin peer);
requests that don't fit the base window sort it last, putting the long
tier first with the Chutes fallback (tier Attested3p) behind it — so a
saturated long tier (SGLang --max-queued-requests 503) falls back per the
existing retry chain, best-effort.
- The estimated_tokens hint is refined into a real context requirement
(ceil(input × factor) + max_tokens reserve) for multi-capacity models
only; near a tier boundary the pool asks the backend for an exact count
via the attested POST /v1/tokenize passthrough (best-effort, 5s budget,
falls back to the bytes/4 heuristic). Single-capacity models keep raw
PR #838 semantics — routing is byte-identical for them.
- Context-length 400s now fall through to a bigger-window sibling (same
mechanism as the #797 model-not-found fall-through; not retryable, no
failure-counter bump) so an under-estimated request self-heals.
- Non-streaming chat now passes routing hints too (was default-only).
- CHUTES_MODELS slug takes an optional @<max_context_tokens> suffix so the
Chutes tier declares its window; estimator counts tools + media parts.
- METRIC_PROVIDER_REQUESTS gains provider_ctx/context_tier tags for
long-tier share dashboards.
Catalog contextLength stays 1048576 (the long tier's window): /v1/models is
unchanged, no new model rows, requests keep the canonical id end-to-end.
There was a problem hiding this comment.
Code Review
This pull request implements context-length tier routing, allowing models to declare multiple capacity tiers (e.g., base and long-context) via provider_config. The system estimates request context requirements using a byte-based heuristic, which can be refined with an exact token count via /v1/tokenize near tier boundaries, and routes requests to the best-fit provider. Feedback on these changes suggests lowering the tokenize request timeout from 5 seconds to 2 seconds to avoid latency issues on slow backends, and replacing try_from().unwrap_or() with an explicit saturating cast when converting the required token count to u32.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Review — context-length tier routingReviewed the full diff focusing on correctness, production-safety (rolling updates / multi-tier state), and Rust concerns. No critical/blocking issues found. This is a notably careful change — the design fails toward existing behavior at every branch and the test coverage matches the surface area. Notes below are non-blocking. Verified safe
Minor observations (non-blocking)
Nice touch gating the all-overflow ordering inversion (biggest-window-first) and building admin re-registration from the merged post-upsert rows. ✅ Approved |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 01aca41e23
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Include the max_tokens reserve in the tokenize-band decision: a large reserve shrinks the input room to cap - reserve, so a mid-size prompt can sit at the tier boundary even when the input alone looks below it (codex P2, also flagged by claude-review). - A context-length 400 falling through from a smaller-window provider no longer clobbers an earlier RETRYABLE error: a long request whose 1M tier 503s and whose base fleet then 400s now terminates with the retryable 503 (round retries the capable tier) instead of a misleading 'maximum context length' client error (codex P2). Regression test. - Lower the /v1/tokenize timeout 5s -> 2s: best-effort precision upgrade on the critical path should fail-open fast (gemini). - Saturating cast instead of try_from().unwrap_or() (gemini).
|
Review disposition (all threads resolved, fixes in d4e5a63):
Deferred by design → #861: E2EE long-context single-tier limitation, absurd-max_tokens pre-flight, SGLang tokenize-shape verification on staging, long-context pricing. |
Nine e2e tests driving the FULL app (real DB, real provider pool, real nearai providers registered via the real admin-PATCH path) against two HTTP backends with different declared context windows (1k base / 10k long). Works without an attestation stack because non-rotation IP-literal URLs serve via the provider's plain one-shot fallback client and fingerprint blocking is TLS-only. Covered end to end: small->base (long tier sees zero traffic), oversize->long, exact /v1/tokenize count overriding the byte heuristic in BOTH directions at the band boundary, engine context-400 fall-through to the bigger tier, saturated-long 503 surfacing as a retryable 429/5xx (not the base fleet's misleading context-400), streaming tier selection over SSE, catalog transparency (one entry, full window), and the full base->long->pinned-attested-fallback chain (Chutes stand-in at the pool boundary; its wire client is ML-KEM+TDX and not HTTP-mockable by design). Adds wiremock as an api dev-dependency.
lloydmak99
left a comment
There was a problem hiding this comment.
Approve. Reviewed the full diff and traced the high-complexity paths against the source.
Verified
expand_inference_endpoints: identity by default; invalidlong_contextblocks dropped loudly without touching the base entry (tiers can't collapse into a round-robin onto the single long host). Used on both the DB-load (flat_map) and admin-PATCH paths, built from merged post-upsert rows so a partial PATCH can't tear down the tier.- Sort
capacity_rank: traced all four cases (short→base first; overflow→long/Chutes/base; all-overflow→biggest-first viau32::MAX-capacity; no-hint→unchanged). Matches the tests; single-capacity models order byte-identically. - Context-400 fall-through: gated on a strictly-larger declared sibling, non-retryable, no failure-counter bump, and
keep_prior_retryablecorrectly stops a base-tier 400 from clobbering the long-tier retryable 503. count_tokens/refinement: best-effort, 2s timeout, semaphore(4), skipped for encrypted payloads, text only over the pinned attested transport, numbers-only logging; gated on ≥2 distinct capacities so all other models are untouched.- Both provider paths (create + reuse) set
max_context_tokens; both expanded URLs accumulate under one id.
The four bot P2s (timeout, saturating cast, output-reserve in the band check, retryable-clobber) match the areas I independently flagged and are all fixed in d4e5a63. CI green.
Rollout note (already documented): routing + catalog PATCH + cvm-compose-files#129 must all be live before any host flips to 2xTP4.
|
End-to-end validation (full dev environment, per review request): 1. New e2e suite ( 2. Real-engine stack (#862): the same behavior verified with a REAL model (Qwen2.5-0.5B on CPU via llama.cpp) and REAL tokenizer counts — demo 8/8, including the boundary case where the true count (801 tokens) kept a request on base that the byte heuristic (~1,201) would have mis-routed, and a curl-visible saturation drill returning 429. Rollout invariant unchanged: this + the catalog PATCH + cvm-compose-files#129 must be live before any host flips to 2xTP4. |
What
Lets a NEAR-served model run two capacity tiers behind one canonical id — built for GLM-5.2 after cvm-compose-files#128 (2xTP4 fleet at 262k ctx) + cvm-compose-files#129 (TP8 host on
glm-5-2-long.completions.near.aiat 1M ctx). Requests whose estimated context requirement doesn't fit the base fleet route to the long tier; a saturated long tier (SGLang--max-queued-requests→ 503) falls back to Chutes via the existing retry chain, best-effort.The tier is data, not code: one PATCH on the existing model row —
{"long_context": {"inference_url": "https://glm-5-2-long.completions.near.ai", "max_context_tokens": 1048576, "base_max_context_tokens": 262144}}expand_inference_endpointsturns the row into two inference-url entries under the same canonical id, flowing through the normalload_inference_url_modelslifecycle (attestation discovery, fingerprint pinning, fail-closed, teardown on removal). Invalid blocks are dropped loudly (identity expansion). No catalog change:/v1/modelsis untouched,contextLengthstays 1048576 (Phala requirement), clients sendz-ai/glm-5.2for every size, same pricing row — the tiering is invisible to users.Routing (builds on #838)
refine_context_requirement): gated on ≥2 distinct declared capacities — every single-capacity model routes byte-identically to main (the service-side estimator and both hint call sites are untouched from feat: adaptive load balancing (TTFT EMA + prefix-hash cache routing + context-length routing) #838). For multi-tier models:ceil(countable_bytes/4 × 1.2) + media/template overhead + max_tokens reserve; near a tier boundary ([0.7, 1.3]×cap) it asks the base fleet for an exact count via the attestedPOST /v1/tokenizepassthrough (new defaultedcount_tokenstrait method, implemented on the nearai provider over its fingerprint-pinned client; best-effort, 5s budget, concurrency-capped at 4, skipped for encrypted payloads).CHUTES_MODELSslug takes an optional@<max_context_tokens>suffix (e.g.z-ai/glm-5.2=zai-org/GLM-5.2-TEE@1048576— verified: that chute serves the full 1M window).METRIC_PROVIDER_REQUESTSgainsprovider_ctx:{capacity|unbounded}andcontext_tier:{long|default}tags for long-tier-share dashboards.Review
Adversarially reviewed (4 dimensions, per-finding verification); all confirmed findings fixed: estimator/hint changes reverted to keep single-capacity models byte-identical, all-overflow ordering inverted, 400 fall-through gated on a larger sibling, invalid long_context blocks rejected, tokenize amplification bounded (semaphore + narrowed band), admin PATCH source-of-truth fixed. Privacy: tokenize text travels only over the pinned attested transport; all new logs carry numbers/ids only.
Testing
cargo test --lib --bins(1,067 tests),cargo fmt --check,cargo clippy --all-targets --all-features -- -D warnings— clean.@max_ctxparsing, pinned-capacity recording, metric tags.base_max_context_tokensto exercise tier selection cheaply, one real ~250k-token request end-to-end, dead-SNI drill for the Chutes leg,/v1/tokenizeshape verification (fail-open to the heuristic if it differs).Rollout ordering (hard invariant)
Deploys inert (no
long_contextin any row ⇒ single provider ⇒ behavior identical). Routing + catalog PATCH + cvm-compose-files#129 must ALL be live before any host flips to 2xTP4, else >262k inputs (p99=318k) 400 on the TP4 fleet. Sequencing details in cvm-compose-files#129.