// ─── Header Parsing ────────────────────────────────────────────────────────── /** * Standard headers used by most providers (OpenAI, Fireworks, etc.) */ export const STANDARD_HEADERS = { limit: "x-ratelimit-limit-requests", remaining: "x-ratelimit-remaining-requests", reset: "x-ratelimit-reset-requests", limitTokens: "x-ratelimit-limit-tokens", remainingTokens: "x-ratelimit-remaining-tokens", resetTokens: "x-ratelimit-reset-tokens", retryAfter: "retry-after", overLimit: "x-ratelimit-over-limit", }; /** * Anthropic uses custom headers */ export const ANTHROPIC_HEADERS = { limit: "anthropic-ratelimit-requests-limit", remaining: "anthropic-ratelimit-requests-remaining", reset: "anthropic-ratelimit-requests-reset", limitTokens: "anthropic-ratelimit-input-tokens-limit", remainingTokens: "anthropic-ratelimit-input-tokens-remaining", resetTokens: "anthropic-ratelimit-input-tokens-reset", retryAfter: "retry-after", }; /** * Parse a reset time string into milliseconds. * Formats: "1s", "1m", "1h", "1ms", "60", ISO date, Unix timestamp */ export function parseResetTime(value) { if (!value) return null; // Duration strings: "1s", "500ms", "1m30s" const durationMatch = value.match(/^(?:(\d+)h)?(?:(\d+)m(?!s))?(?:(\d+)s)?(?:(\d+)ms)?$/); if (durationMatch) { const [, h, m, s, ms] = durationMatch; return ( (parseInt(h || 0) * 3600 + parseInt(m || 0) * 60 + parseInt(s || 0)) * 1000 + parseInt(ms || 0) ); } // Pure number: assume seconds const num = parseFloat(value); if (!isNaN(num) && num > 0) { // If it looks like a Unix timestamp (> year 2025) if (num > 1700000000) { return Math.max(0, num * 1000 - Date.now()); } return num * 1000; } // ISO date string try { const date = new Date(value); if (!isNaN(date.getTime())) { return Math.max(0, date.getTime() - Date.now()); } } catch {} return null; } export function toPlainHeaders(headers: unknown): Record { if (!headers) return {}; const plain: Record = {}; const obj = headers as Record; if (typeof obj.forEach === "function") { try { (obj.forEach as (cb: (v: string, k: string) => void) => void)((v: string, k: string) => { plain[k.toLowerCase()] = v; }); return plain; } catch {} } if (typeof obj.entries === "function") { try { for (const [k, v] of (obj.entries as () => Iterable<[string, string]>)()) { plain[k.toLowerCase()] = v; } return plain; } catch {} } try { for (const [k, v] of Object.entries(obj)) { plain[k.toLowerCase()] = v == null ? "" : String(v); } } catch {} return plain; }