aegida-console / hooks / use-models.ts
use-models.ts
Raw
"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 };
}