import { NextResponse } from "next/server"; import { flattenModelsDevCatalog, recommendModelCatalogPreset, searchModelCatalog, type ModelCatalogEntry, } from "@/lib/model-catalog"; export const dynamic = "force-dynamic"; const MODELS_DEV_URL = "https://models.dev/api.json"; const CATALOG_TTL_MS = 60 * 60 * 1000; const FETCH_TIMEOUT_MS = 15_000; interface CatalogCache { entries: ModelCatalogEntry[]; expiresAt: number; inFlight?: Promise; } declare global { var __piModelsDevCatalogCache: CatalogCache | undefined; } function getCache(): CatalogCache { return globalThis.__piModelsDevCatalogCache ??= { entries: [], expiresAt: 0 }; } async function fetchCatalog(): Promise { const response = await fetch(MODELS_DEV_URL, { cache: "no-store", headers: { Accept: "application/json" }, signal: AbortSignal.timeout(FETCH_TIMEOUT_MS), }); if (!response.ok) throw new Error(`models.dev returned HTTP ${response.status}`); const entries = flattenModelsDevCatalog(await response.json()); if (entries.length === 0) throw new Error("models.dev returned an empty catalog"); return entries; } async function loadCatalog(): Promise { const cache = getCache(); if (cache.entries.length > 0 && cache.expiresAt > Date.now()) return cache.entries; if (!cache.inFlight) { cache.inFlight = fetchCatalog().then((entries) => { cache.entries = entries; cache.expiresAt = Date.now() + CATALOG_TTL_MS; return entries; }).finally(() => { cache.inFlight = undefined; }); } try { return await cache.inFlight; } catch (error) { if (cache.entries.length > 0) return cache.entries; throw error; } } export async function GET(req: Request) { const { searchParams } = new URL(req.url); const query = (searchParams.get("q") ?? "").slice(0, 120); const provider = (searchParams.get("provider") ?? "").slice(0, 120); const baseUrl = (searchParams.get("baseUrl") ?? "").slice(0, 500); const parsedLimit = Number.parseInt(searchParams.get("limit") ?? "50", 10); const limit = Number.isFinite(parsedLimit) ? parsedLimit : 50; try { const entries = await loadCatalog(); const models = searchModelCatalog(entries, query, provider, limit); const recommendation = recommendModelCatalogPreset(entries, query, provider, baseUrl); return NextResponse.json({ models, recommendation, source: MODELS_DEV_URL }); } catch (error) { return NextResponse.json({ error: error instanceof Error ? error.message : String(error) }, { status: 502 }); } }