import { z } from "zod"; import { withUser, parseBody, json } from "@/lib/api"; import { listRegistryModels, favoriteModels, recentModels, toggleFavorite } from "@/lib/ai/registry"; import { modelLabels, setModelLabel } from "@/lib/ai/registry/user-models"; import { listConnections } from "@/lib/providers/keys"; import { listCustomModels, countValidEndpoints } from "@/lib/endpoints/service"; export const dynamic = "force-dynamic"; /** GET /api/models — the normalized registry plus the caller's favorites/recents/labels and connected providers. */ export const GET = withUser(async ({ req, user }) => { const includeDeprecated = new URL(req.url).searchParams.get("deprecated") === "1"; const [models, favorites, recents, labels, connections, customModels, validEndpoints] = await Promise.all([listRegistryModels({ includeDeprecated }), favoriteModels(user.id), recentModels(user.id), modelLabels(user.id), listConnections(user.id), listCustomModels(user.id), countValidEndpoints(user.id)]); const connectedProviders = connections.filter((c) => c.status !== "invalid").map((c) => c.provider); // Custom OpenAI-compatible endpoints (Settings → Endpoints) appear as provider "custom", keyed `custom/:`. if (validEndpoints > 0) connectedProviders.push("custom"); return json({ models: [...models, ...customModels], favorites, recents, labels, connectedProviders }); }); const bodySchema = z.discriminatedUnion("action", [ z.object({ action: z.literal("toggle-favorite"), modelKey: z.string().max(120) }), z.object({ action: z.literal("set-label"), modelKey: z.string().max(120), label: z.string().max(60).nullable() }), ]); export const POST = withUser(async ({ req, user }) => { const body = await parseBody(req, bodySchema); if (body.action === "toggle-favorite") { const favorite = await toggleFavorite(user.id, body.modelKey); return json({ favorite }); } await setModelLabel(user.id, body.modelKey, body.label); return json({ ok: true }); });