import catalog from "./provider-catalog.json"; export type User = { id: string; name: string; email: string; is_admin?: boolean; }; export type Health = { status?: string; latency_ms?: number; retry_at?: number; http_status?: number; }; export type Provider = { id: string; kind: string; name: string; base_url: string; enabled: boolean; priority: number; pinned: boolean; preferred_models?: string[]; preferred_only?: boolean; auth_type?: "api_key" | "oauth"; models_count: number; discovered_at: number | null; discovery_error: string | null; health: Health; }; export type Model = { shared?: boolean; owned?: boolean; id: string; model_id: string; name: string; provider: string; provider_name: string; provider_id: string; route_id: string; capabilities: Record; context_window: number | null; input_price: number | null; output_price: number | null; available: boolean; enabled: boolean; favorite: boolean; manual: boolean; source: string; health: Health; }; export type GatewayKey = { id: string; name: string; prefix: string; created_at: number; last_used_at: number | null; expires_at: number | null; revoked: boolean; }; export type Attempt = { provider: string; provider_name: string; model: string; status: number; latency_ms: number; error?: string; }; export type Log = { id: string; provider_name: string | null; requested_model: string; resolved_model: string | null; endpoint: string; status: number; latency_ms: number; input_tokens: number | null; output_tokens: number | null; estimated_cost: number | null; attempts: Attempt[]; error_code: string | null; created_at: number; }; export type Metrics = { requests: number; successes: number | null; avg_latency_ms: number | null; input_tokens: number | null; output_tokens: number | null; estimated_cost: number | null; priced_requests: number; }; export type Usage = { summary: Metrics; series: (Metrics & { day: number })[]; providers: (Metrics & { provider_name: string })[]; days: number; }; export type Page = { data: T[]; total: number }; export type ProviderDirectoryEntry = { id: string; name: string; symbol: string; color: string; url: string; website: string; key_url?: string; integration: string; protocol: string; auth: string[]; category: string; note?: string; }; export const providerDirectory: ProviderDirectoryEntry[] = catalog; export const providerInfo: Record = Object.fromEntries( providerDirectory .filter((p) => p.integration === "direct") .map((p) => [p.id, p]), ); export function csrf(): string { return typeof document === "undefined" ? "" : decodeURIComponent( document.cookie .split("; ") .find((x) => x.startsWith("gw_csrf=")) ?.split("=")[1] || "", ); } export class ApiError extends Error { constructor( message: string, public status: number, public code?: string, ) { super(message); } } export async function api(path: string, init: RequestInit = {}): Promise { const response = await fetch("/api" + path, { ...init, credentials: "same-origin", headers: { "Content-Type": "application/json", "X-CSRF-Token": csrf(), ...init.headers, }, }); if (!response.ok) { const error = await response.json().catch(() => null); throw new ApiError( error?.error?.message || `Request failed (${response.status})`, response.status, error?.error?.code, ); } return response.status === 204 ? (undefined as T) : ((await response.json()) as T); } export function num(value: number | null | undefined): string { return value == null ? "—" : Intl.NumberFormat("en", { notation: value >= 10000 ? "compact" : "standard", maximumFractionDigits: 1, }).format(value); } export function money(value: number | null | undefined): string { return value == null ? "Unknown" : `$${value.toFixed(value > 0 && value < 0.01 ? 5 : 2)}`; } export function duration(value: number | null | undefined): string { if (value == null) return "—"; return value >= 1000 ? `${(value / 1000).toFixed(value >= 10000 ? 1 : 2)} s` : `${Math.round(value)} ms`; } export async function loadAllModels( signal?: AbortSignal, ownedOnly = false, ): Promise { const models = new Map(); let offset = 0; while (true) { const page = await api>( `/models?limit=1000&offset=${offset}${ownedOnly ? "&owned_only=true" : ""}`, { signal }, ); page.data.forEach((model) => models.set(model.id, model)); offset += page.data.length; if (offset >= page.total) { if (models.size < page.total) throw new Error( "The catalog changed while loading. Refresh models to try again.", ); return [...models.values()]; } if (!page.data.length) throw new Error( "The complete catalog could not be loaded. Refresh models to try again.", ); } } export function stamp(value: number | null | undefined): string { return value ? new Date(value * 1000).toLocaleString("en-US", { timeZone: "UTC", timeZoneName: "short", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }) : "Never"; }