mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-21 14:22:14 +03:00
* feat(sse): learn hard request caps from 429 bodies and pace under them Providers such as TokenRouter reject bursts with prose like "Maximum 5 requests within 1 minutes" and send no rate-limit headers, so the limiter never learned the ceiling and kept racing into it. Every 429 also evicts the limiter, and the rebuilt one carried no pacing at all. updateFromResponseBody now parses that phrasing (plus "N requests per minute", "N requests per M seconds" and "N RPM") into a per-window cap, applies it to the limiter as an empty reservoir that refills N every window with calls spread window/N apart, and records it in learnedRateLimits. getLimiter reapplies a learned cap whenever the limiter is rebuilt, and loadPersistedLimits restores it after a restart, unless the connection has an explicit RPM override. Also pins the capacity-503 classification for TokenRouter's "system cpu/disk overloaded" bodies as model_capacity with backoff, and prunes the no-unused-vars allowlist entry for rateLimitManager.ts that this change made obsolete. Fixes #13594 * fix(sse): require a cap word before rpm figures and gate every cap path on the queue budget `RPM_RE` accepted any "<n> rpm" so a usage readout like "current usage: 4 rpm" pinned the connection to one request every 15s. It now shares the cap-word prefix and made/sent/used guard with the verbose pattern, so a bare usage figure is not read as a ceiling. The queue-budget refusal only guarded the learn path. A cap learned under a generous maxWaitMs was still re-applied by getLimiter on every rebuild and by loadPersistedLimits after a restart once the budget shrank, turning every request into a local queue timeout. The check now lives in capSettingsWithinBudget(), which all three paths call; a refused cap stays recorded and is retried the next time the limiter is built.