Repository navigation
feat(llm): cross-worker quota headroom store, persist real 429 headroom - #16483
Conversation
|
Warning Review limit reachedNext included review available in 19 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (9)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ SSOT Configuration Compliance: Passing🎉 No new hardcoded values of either class — Known backlog in |
|
Delta review at 5c194d3 (rebase onto current main, per a7): APPROVE. Verified the fix for the blocking finding from my earlier review directly: the three tests in CI: 0 fail, 34 success, 24 pending. Good to merge once green. |
Main gained a new AUTOBOT_* var since this branch's own env_registry_ai.py addition landed, so the autogenerated table (and its count line, 225 -> 226) had drifted behind pipeline-scripts/check_env_var_registry.py's live count.
|
Delta review at d466e37: APPROVE. Verified the fix directly — |
…#16480) Adding llm_shared.quota_headroom to conftest.py's real-load sequence pushed it from 1570 to 1575 lines, raising the file-size ratchet's ceiling to admit it -- against the ratchet-only-shrinks rule. Root cause wasn't the 5-line addition; it's that _make_pkg_stub and _real_load_and_bind are 74 lines of general-purpose sys.modules stubbing/real-loading with zero conftest-specific state (57 and 21 call sites respectively, all unaffected by where the functions live), sitting in conftest.py instead of testkit/module_stubs.py where the rest of this exact concern already lives (#13451). Moved both, unchanged, to testkit/module_stubs.py; conftest.py now imports and aliases them back to their original names so every call site is untouched. Net effect: conftest.py drops to 1507 lines (black-formatted, real measured count) -- the quota_headroom addition fits with room to spare, so the ceiling in both baseline files goes down to 1507, not back to 1570. Verified with the file-size ratchet suite, the quota-headroom/ rate-limit-backoff tests that exercise this exact real-load path, the module_stubs test suite, and a real-load call site outside llm_shared (tool_output_filter) as a broader sanity check -- 88 tests total, all passing; behavior-preserving move, zero logic changes.
|
Delta review at 59e37d1 (from d466e37): approve. This corrects a finding on my own earlier approval — I missed that the previous head raised the ratchet ceiling (1570→1575) rather than extracting, which a7 caught. This delta does it right: extracts instead of exempting. Verified independently (reading, not running):
CI: no FAILURE/ERROR/TIMED_OUT checks; rest still queued on the fresh push. Mergeable. |
Summary
Closes #16480 (sub-issue of #15026, covering exactly the error/429 harvest path — the success path is #16476, separately tracked and not in scope here).
Single-issue rationale: #16480 covers the quota headroom store and harvesting on the 429/error path only. No other open issue shares that scope. #16476, its sibling under #15026, covers success-path harvesting, which needs per-provider SDK raw-response access, so it is a different change and is tracked separately. #15028, #15029 and #15030 consume observed headroom (routing, account selection and the provider view) rather than record it.
Supersedes #16477, opened from the wrong branch (
gh issue develop 15026directly, which — since the default-branch switch — makes any PR from it a closing reference for #15026 regardless of body wording, even for a deliberately partial delivery). Same commits, same diff, opened from a sub-issue-linked branch instead soclosingIssuesReferencesresolves correctly. #16477 is closed with a pointer here.llm_shared/quota_headroom.py— new Redis-backedQuotaHeadroomStore, mirroringprovider_degradation.py's pattern (llm_shared: persistent cross-worker provider degradation marking (Redis TTL) #11519): in-process fallback when Redis is down, never raises into a caller, TTL from a registered env var. Key:(provider, account_id, window).account_iddefaults to"default"so llm-auth: one credential per provider per tenant — add a multi-account credential pool with headroom-based selection #15029's future multi-account pool can extend this without a schema change. Absence is a first-class state — no entry means "no signal yet," never fabricated as a zero.llm_shared/rate_limit_backoff.py—raise_if_rate_limited()now persists aremaining=0/resets_atreading on every 429, fire-and-forget (matchesbase_provider.py's existing_notify_*pattern), before raising — so a store hiccup can never affect the existing raise-and-retry behavior. All 15 pre-existing tests pass unchanged.autobot-backend/conftest.py— real-loadsllm_shared.quota_headroominto thellm_sharedtest stub's per-submodule allowlist (the same mechanismprovider_degradation/rate_limit_backoff/base_provideralready use).autobot_shared/env_registry_ai.py— registersAUTOBOT_LLM_QUOTA_HEADROOM_TTL_SECONDS.Acceptance criteria (#16480)
QuotaHeadroomStoreinstances sharing afakeredisFakeServer, mirroringtest_provider_degradation.py's verification style)rate_limit_backoff.pybehaviour is unchanged for every existing caller — this is additive#15026 itself stays open — its success-path harvesting criteria are #16476, not delivered here.
Test plan
660 tests pass in
llm_shared/(128 skipped — heavy/optional deps). New: 14 tests forQuotaHeadroomStore, 4 new async tests for the 429-persistence wiring inrate_limit_backoff_test.py. Pre-push hook ran the relevant subset and passed.Thinking Path
rate_limit_backoff.raise_if_rate_limited()already parsed a provider's 429 response, then discarded the reading instead of recording it. #15026 asks for a cross-worker quota headroom store fed from provider rate-limit signals. #16480 is the slice that can land now: the store itself, fed from the 429 path. Success-path harvesting needs per-provider SDK access and is #16476.The store follows
provider_degradation.py's existing pattern (#11519) rather than inventing a new one: Redis-backed, an in-process fallback when Redis is down, and never raising into a caller. The 429 write is fire-and-forget and happens before the existing raise, so the retry behaviour current callers depend on can't change. A missing entry means "no signal yet", never zero remaining, so a consumer can't read silence as exhaustion.What Changed
autobot-backend/llm_shared/quota_headroom.py(new, +220):QuotaHeadroomStore, keyed by(provider, account_id, window), withaccount_iddefaulting to"default". Redis-backed with an in-process fallback. A reading expires afterAUTOBOT_LLM_QUOTA_HEADROOM_TTL_SECONDS.autobot-backend/llm_shared/rate_limit_backoff.py(+33/-0):raise_if_rate_limited()recordsremaining=0andresets_aton every 429, fire-and-forget, before the existing raise.autobot_shared/env_registry_ai.py(+10/-0): registersAUTOBOT_LLM_QUOTA_HEADROOM_TTL_SECONDS(int, default 3600).docs/developer/CLAUDE_RULES.md(+2/-1) gets the matching row in its generated env-var table, whose count goes from 219 to 220.autobot-backend/conftest.py(+5/-0): real-loadsllm_shared.quota_headroomin thellm_sharedtest stub, ahead ofrate_limit_backoff, which imports it at module level.repo_tests/python_file_size_ratchet_baseline.pyandscripts/python_file_size_known_large.py(+1/-1 each): theautobot-backend/conftest.pyceiling goes from 1580 to 1585 for those five lines.changelog/unreleased/15026-quota-headroom-store.md(new): feature entry.autobot-backend/llm_shared/tests/test_quota_headroom.py(new, +239) andautobot-backend/llm_shared/rate_limit_backoff_test.py(+85/-0), listed under Verification.Verification
CI on this PR is the verification of record. Nothing was re-run locally when these template sections were added; the "Test plan" section above reports the author's earlier local run.
autobot-backend/llm_shared/tests/test_quota_headroom.py: 14 tests forQuotaHeadroomStore. They cover record and get; an absent entry returningNonerather than zero; expiry restoring the absent state; filtering by provider;account_idscoping; cross-worker visibility through two stores sharing onefakeredisFakeServer; the no-Redis fallback for record, expiry and listing; and the fraction-consumed calculation, including an unknown limit, an unknown remaining count, a zero limit and a negative remaining count.autobot-backend/llm_shared/rate_limit_backoff_test.py: 4 new async tests. A 429 records zero remaining; a 429 withRetry-Aftersetsresets_at; an OK response persists nothing; and a missing provider neither persists nor raises a scheduling error. The file's existing tests are untouched (+85/-0).Model Used
Claude (AutoBot coordinator fleet)
🤖 Generated with Claude Code