import { resolveBuiltinPacing } from './builtin-pacing'; import type { PacingRule, PlayQueueHint } from './governor/rate-state-backend'; export type ResolvedPacingPolicy = { provider: string; rules: PacingRule[]; }; type PacingPolicyQueueHint = Pick< PlayQueueHint, | 'provider' | 'ruleId' | 'requestsPerWindow' | 'windowMs' | 'adaptiveMaxRps' | 'maxConcurrency' >; function isFinitePositiveNumber(value: unknown): value is number { return typeof value === 'number' && Number.isFinite(value) && value > 0; } export function pacingPolicyFromQueueHints( hints: readonly PacingPolicyQueueHint[], ): ResolvedPacingPolicy | null { if (hints.length === 0) return null; const provider = hints[0]?.provider?.trim(); if (!provider) return null; const rules = hints.flatMap((hint) => { if ( typeof hint.ruleId !== 'string' || !hint.ruleId.trim() || !isFinitePositiveNumber(hint.requestsPerWindow) || !isFinitePositiveNumber(hint.windowMs) ) { return []; } return [ { ruleId: hint.ruleId, requestsPerWindow: hint.requestsPerWindow, windowMs: hint.windowMs, adaptiveMaxRps: isFinitePositiveNumber(hint.adaptiveMaxRps) ? hint.adaptiveMaxRps : null, maxConcurrency: typeof hint.maxConcurrency === 'number' && Number.isFinite(hint.maxConcurrency) && hint.maxConcurrency > 0 ? hint.maxConcurrency : null, } satisfies PacingRule, ]; }); return rules.length > 0 ? { provider, rules } : null; } export function pacingPolicyFromUnknownQueueHints( value: unknown, ): ResolvedPacingPolicy | null { if (!Array.isArray(value)) return null; return pacingPolicyFromQueueHints( value .filter((hint): hint is Record => Boolean(hint && typeof hint === 'object' && !Array.isArray(hint)), ) .map((hint) => ({ provider: typeof hint.provider === 'string' ? hint.provider : '', ruleId: typeof hint.ruleId === 'string' ? hint.ruleId : '', requestsPerWindow: typeof hint.requestsPerWindow === 'number' ? hint.requestsPerWindow : Number.NaN, windowMs: typeof hint.windowMs === 'number' ? hint.windowMs : Number.NaN, adaptiveMaxRps: typeof hint.adaptiveMaxRps === 'number' ? hint.adaptiveMaxRps : null, maxConcurrency: typeof hint.maxConcurrency === 'number' ? hint.maxConcurrency : null, })), ); } export function pacingPolicyForTool( toolId: string, hints: readonly PlayQueueHint[], ): ResolvedPacingPolicy | null { return resolveBuiltinPacing(toolId) ?? pacingPolicyFromQueueHints(hints); }