feat(server): sticky provider-reported request size skips subsequent low-TPM models - #507
Open
jasnoorgill wants to merge 1 commit into
Open
Conversation
Owner
|
Solid work, and I like that the sizing decision is driven by what the provider actually reports rather than a guess. Two things before I go through it properly: it needs a rebase on current main (it's conflicting now), and Ping me once that's pushed and I'll review right after. |
…low-TPM models (Re-applied onto current main with review feedback addressed.) Rebased onto current origin/main. Resolved conflicts in fallback-loop.ts, client-context.ts, services/router.ts, and the matching test — additive in every case (HEAD's superset imports + new observedRequestTokens field/methods). Review feedback addressed: - Platform type is now imported from @freellmapi/shared/types.ts instead of being redeclared in provider-size-parser.ts. One canonical definition; the local copy was never referenced anywhere else in the codebase. Tests: 41/41 passing in the three test files affected (provider-size-parser, client-context, router).
jasnoorgill
force-pushed
the
feat/provider-reported-request-size-skip
branch
from
August 1, 2026 18:01
9e20b0a to
b3948f2
Compare
Contributor
Author
|
Rebased onto current main and addressed the What's in this push:
Head SHA on this branch is now |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a provider rejects a chat request with a size error (Groq 413 'Limit N, Requested M', OpenRouter 400 context-length, Cloudflare 400/413), the same 36K-token prompt currently hits 4–5 models in the fallback chain sequentially. Each one rejects independently — wasting latency, burning per-key request budget on doomed attempts, and inflating the upstream 429 rate.
Live data (last 30 days,
requeststable wherestatus='error'):Requested NThe local token estimator (
text.length / 4) undercounts tool-heavy / code-heavy prompts by 3–10×. A request the estimator scores at 3,700 tokens is reported as 36,532 by Groq — themodels.tpm_limitpre-check passes, the call fires, and Groq 413s.Solution
Once one upstream names the real token count for THIS request, propagate it as a sticky per-request value. Subsequent attempts in the same chain then pre-skip models whose TPM ceiling can't fit it — distinct from the existing
canUseTokensheadroom check (current usage + estimate vs limit) and surfaced as a new skip reasonrequest-too-large-for-tpmso the exhaustion diagnostic tells the operator exactly why the model was skipped.Implementation
lib/provider-size-parser.ts(new)parseProviderReportedSize(platform, message)→ number | null. One regex per supported provider, all derived from the live error corpus.lib/client-context.tsobservedRequestTokensto the AsyncLocalStorage context +setObservedRequestTokens/getObservedRequestTokenshelpers. Sticky: never decreases (max wins).lib/fallback-loop.tsrecordRetryableFailure: call the parser; if it returns a number, write it viasetObservedRequestTokens. Single site covers all three chat surfaces (proxy, responses, anthropic).services/router.tsselectKeyForModel: new gate beforecanUseTokens— if observed > model's tpm_limit, skip with reasonrequest-too-large-for-tpm.__tests__/lib/provider-size-parser.test.ts(new)__tests__/lib/client-context.test.tssetObservedRequestTokens/getObservedRequestTokens(no-op outside scope, sticky max, per-request isolation).__tests__/services/router.test.tsWhy GitHub Models is intentionally NOT parsed
GitHub's body is
Request body too large for gpt-4.1 model. Max size: 8000 tokens.The 8000 is the LIMIT, not the rejected request's size. Returning it would cause every subsequent model with TPM < 8000 to be skipped for the rest of the request — wildly wrong since most free models have TPM ~6000. The parser returns null andlearnLimitFromErrorkeeps doing what it already does (loweringmodels.tpm_limitto 8000 for github/gpt-4.1).Why AsyncLocalStorage and not a
FallbackStatefieldThe sticky value is request-scoped, not loop-scoped — the same store already carries IP/UA and is reset by
clientContextMiddlewareper request. Avoids threading a new arg through 7 function signatures (routeRequest,selectKeyForModel,runFallbackLoop, etc.). The router reads it on every iteration; the loop writes it once when a 4xx lands.Backwards compatibility
canUseTokensheadroom check still runs after the new gate.Verification
Expected impact
request-too-large-for-tpmsurfaces in/v1/docsexhaustion traces, so operators can see when a request was too big for the entire pool vs when it was just one model's 429.Out of scope (follow-up ideas)