-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(combos): add random, least-used, and reset-window routing strategies #2050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b54b731
944bd21
8dfce70
c2a110d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import type { ProviderQuota } from "../providers/quota"; | ||
|
|
||
| function collectResetCandidates(quota: ProviderQuota): number[] { | ||
| const candidates: number[] = []; | ||
| const push = (value: number | undefined): void => { | ||
| if (value !== undefined && Number.isFinite(value)) candidates.push(value); | ||
| }; | ||
| push(quota.fiveHourResetAt); | ||
| push(quota.weeklyResetAt); | ||
| push(quota.monthlyResetAt); | ||
| if (quota.customWindows) { | ||
| for (const w of quota.customWindows) { | ||
| push(w.resetAt); | ||
| } | ||
| } | ||
| return candidates; | ||
| } | ||
|
|
||
| /** | ||
| * Earliest future reset timestamp from a cached provider quota snapshot, | ||
| * or null when no fresh quota data exists or all resets have elapsed. | ||
| */ | ||
| export function earliestQuotaResetAt( | ||
| quota: ProviderQuota | null, | ||
| now: number, | ||
| ): number | null { | ||
| if (!quota) return null; | ||
| const future = collectResetCandidates(quota).filter(ts => ts > now); | ||
| if (future.length > 0) return Math.min(...future); | ||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * Milliseconds until the soonest known quota-window reset. | ||
| * Returns Infinity when no quota data exists, quota is stale, or all known | ||
| * reset timestamps have elapsed. An elapsed reset is stale evidence — it | ||
| * does not prove the next request has fresh capacity. | ||
| */ | ||
| export function quotaResetRemainingMs( | ||
| quota: ProviderQuota | null, | ||
| now: number, | ||
| ): number { | ||
| const nearest = earliestQuotaResetAt(quota, now); | ||
| if (nearest === null) return Number.POSITIVE_INFINITY; | ||
| return nearest - now; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import type { ProviderQuota, ProviderQuotaReport } from "./quota"; | ||
|
|
||
| const quotaCache = new Map<string, ProviderQuota>(); | ||
|
|
||
| export function clearCachedProviderQuotas(): void { | ||
| quotaCache.clear(); | ||
| } | ||
|
|
||
| export function replaceCachedProviderQuotas(reports: ProviderQuotaReport[]): void { | ||
| quotaCache.clear(); | ||
| for (const report of reports) { | ||
| quotaCache.set(report.provider, report.quota); | ||
| } | ||
| } | ||
|
|
||
| export function getCachedProviderQuota( | ||
| provider: string, | ||
| now: number, | ||
| maxAgeMs = 30 * 60_000, | ||
| ): ProviderQuota | null { | ||
| const quota = quotaCache.get(provider); | ||
| if (!quota) return null; | ||
| if (now - quota.updatedAt > maxAgeMs) return null; | ||
| return quota; | ||
| } | ||
|
|
||
| export function setCachedProviderQuotaForTests( | ||
| provider: string, | ||
| quota: ProviderQuota, | ||
| ): void { | ||
| quotaCache.set(provider, quota); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -654,19 +654,19 @@ export interface OcxConfig { | |
|
|
||
| export type OcxAccountPoolRotationStrategy = "quota" | "round-robin" | "fill-first"; | ||
|
|
||
| export type OcxComboStrategy = "failover" | "round-robin"; | ||
| export type OcxComboStrategy = "failover" | "round-robin" | "random" | "least-used" | "reset-window"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a combo configured through the CLI or API uses Useful? React with 👍 / 👎. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Users consulting the canonical documentation are still told that AGENTS.md reference: AGENTS.md:L340-L341 Useful? React with 👍 / 👎. |
||
| export type OcxComboDefaultEffort = "low" | "medium" | "high" | "xhigh" | "max" | "ultra"; | ||
|
|
||
| export interface OcxComboTarget { | ||
| provider: string; | ||
| model: string; | ||
| /** Relative SWRR batch weight. Default 1; valid range 1..10000. */ | ||
| /** Relative target weight for round-robin batches and random selection. Default 1; valid range 1..10000. */ | ||
| weight?: number; | ||
| } | ||
|
|
||
| export interface OcxComboConfig { | ||
| targets: OcxComboTarget[]; | ||
| /** Ordered failover (default) or deterministic smooth weighted round-robin. */ | ||
| /** Ordered failover (default), round-robin, weighted random, least-used, or quota reset-window selection. */ | ||
| strategy?: OcxComboStrategy; | ||
| /** Successful requests retained on one RR selection batch. Default 1; range 1..100. */ | ||
| stickyLimit?: number; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.