ai_api / src /lib /resilience /modelLockoutSettings.ts
Yogesh
initial deploy
cd8bd0a
Raw
History Blame Contribute Delete
2.97 kB
export interface ModelLockoutSettings {
enabled: boolean;
errorCodes: number[];
baseCooldownMs: number;
maxCooldownMs: number;
maxBackoffSteps: number;
useExponentialBackoff: boolean;
}
export const DEFAULT_MODEL_LOCKOUT_SETTINGS: ModelLockoutSettings = {
enabled: false,
errorCodes: [403, 404, 429, 502, 503, 504],
baseCooldownMs: 120_000,
maxCooldownMs: 1_800_000,
maxBackoffSteps: 10,
useExponentialBackoff: true,
};
type JsonRecord = Record<string, unknown>;
function asRecord(value: unknown): JsonRecord {
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {};
}
function toInteger(
value: unknown,
fallback: number,
options: { min?: number; max?: number } = {}
): number {
const min = options.min ?? 0;
const max = options.max ?? Number.MAX_SAFE_INTEGER;
const parsed =
typeof value === "number" ? value : typeof value === "string" ? Number(value) : fallback;
return Number.isFinite(parsed) ? Math.max(min, Math.min(max, Math.trunc(parsed))) : fallback;
}
function toBoolean(value: unknown, fallback: boolean): boolean {
if (typeof value === "boolean") return value;
if (typeof value === "number") return value !== 0;
if (typeof value === "string") return value === "true" || value === "1";
return fallback;
}
function toNumberArray(value: unknown, fallback: number[]): number[] {
if (Array.isArray(value)) {
const nums = value
.map((v) => (typeof v === "number" ? v : Number(v)))
.filter((n) => Number.isFinite(n) && n >= 100 && n <= 599);
return nums.length > 0 ? nums : fallback;
}
return fallback;
}
export function resolveModelLockoutSettings(
settings: Record<string, unknown> | null | undefined
): ModelLockoutSettings {
const record = asRecord(settings);
const raw = asRecord(record.modelLockout);
const isTest =
process.env.NODE_ENV === "test" ||
process.execArgv.includes("--test") ||
process.argv.some((arg) => typeof arg === "string" && arg.includes("test"));
const baseCooldownMs = toInteger(
raw.baseCooldownMs,
DEFAULT_MODEL_LOCKOUT_SETTINGS.baseCooldownMs,
{
min: isTest ? 0 : 5_000,
max: 600_000,
}
);
const maxCooldownMs = Math.max(
toInteger(raw.maxCooldownMs, DEFAULT_MODEL_LOCKOUT_SETTINGS.maxCooldownMs, {
min: isTest ? 0 : 5_000,
max: 3_600_000,
}),
baseCooldownMs // cap must be >= base or exponential backoff is meaningless
);
return {
enabled: toBoolean(raw.enabled, DEFAULT_MODEL_LOCKOUT_SETTINGS.enabled),
errorCodes: toNumberArray(raw.errorCodes, DEFAULT_MODEL_LOCKOUT_SETTINGS.errorCodes),
baseCooldownMs,
maxCooldownMs,
maxBackoffSteps: toInteger(
raw.maxBackoffSteps,
DEFAULT_MODEL_LOCKOUT_SETTINGS.maxBackoffSteps,
{ min: 0, max: 20 }
),
useExponentialBackoff: toBoolean(
raw.useExponentialBackoff,
DEFAULT_MODEL_LOCKOUT_SETTINGS.useExponentialBackoff
),
};
}