"use client"; import { useEffect, useMemo, useState } from "react"; import { authFetch } from "@/lib/auth/client"; import { AUTO_MODEL, parseChatModelsResponse, type ChatModel, } from "@/lib/chat/models"; import type { ModelId } from "@/lib/chat/types"; export type ModelsStatus = "loading" | "ready" | "unavailable"; export type UseModelsResult = { status: ModelsStatus; models: ChatModel[]; effectiveModelId: ModelId; }; const FALLBACK_MODELS: ChatModel[] = [AUTO_MODEL]; export function useModels( userId: string, persistedModelId: ModelId, onUnauthorized?: () => void, ): UseModelsResult { const [state, setState] = useState<{ userId: string; status: ModelsStatus; models: ChatModel[]; }>({ userId, status: "loading", models: FALLBACK_MODELS, }); useEffect(() => { const controller = new AbortController(); let active = true; void authFetch("/api/models", { cache: "no-store", signal: controller.signal }) .then(async (response) => { if (!active) return; if (response.status === 401) { onUnauthorized?.(); setState({ userId, status: "unavailable", models: FALLBACK_MODELS }); return; } if (!response.ok) throw new Error("catalog unavailable"); const models = parseChatModelsResponse(await response.json()); if (!models) throw new Error("invalid catalog"); if (active) setState({ userId, status: "ready", models }); }) .catch(() => { if (active && !controller.signal.aborted) { setState({ userId, status: "unavailable", models: FALLBACK_MODELS }); } }); return () => { active = false; controller.abort(); }; }, [onUnauthorized, userId]); const current = state.userId === userId ? state : { userId, status: "loading" as const, models: FALLBACK_MODELS }; const effectiveModelId = useMemo( () => current.models.some((model) => model.id === persistedModelId) ? persistedModelId : "auto", [current.models, persistedModelId], ); return { status: current.status, models: current.models, effectiveModelId }; }