Files
2026-07-13 13:39:12 +08:00

95 lines
2.8 KiB
TypeScript

// ─── 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<string, string> {
if (!headers) return {};
const plain: Record<string, string> = {};
const obj = headers as Record<string, unknown>;
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;
}