import { NextRequest, NextResponse } from "next/server"; interface HuggingFaceModel { id?: string; modelId?: string; tags?: string[]; } type ModelOption = { id: string; name: string }; type CacheEntry = { timestamp: number; data: ModelOption[]; }; const CACHE_DURATION = 1000 * 60 * 5; // 5 minutes const cache = new Map(); function getCacheKey(query: string, limit: number) { return `${query}::${limit}`; } function normalizeModelId(model: HuggingFaceModel) { return model.id || model.modelId || ""; } function hasTag(model: HuggingFaceModel, tag: string) { return Array.isArray(model.tags) && model.tags.includes(tag); } function isExcludedFormat(model: HuggingFaceModel) { if (!Array.isArray(model.tags)) return false; const tags = new Set(model.tags); if (tags.has("gguf")) return true; if (tags.has("mlx")) return true; if (tags.has("ggml")) return true; return false; } export async function GET(request: NextRequest) { const query = request.nextUrl.searchParams.get("q")?.trim() ?? ""; const limitParam = Number(request.nextUrl.searchParams.get("limit") ?? "20"); const limit = Number.isFinite(limitParam) ? Math.max(1, Math.min(50, limitParam)) : 20; const cacheKey = getCacheKey(query.toLowerCase(), limit); const now = Date.now(); const cached = cache.get(cacheKey); if (cached && now - cached.timestamp < CACHE_DURATION) { return NextResponse.json(cached.data); } try { const url = new URL("https://huggingface.co/api/models"); if (query) { url.searchParams.set("search", query); } else { url.searchParams.set("sort", "downloads"); url.searchParams.set("direction", "-1"); } // Restrict to models that have the `safetensors` tag. url.searchParams.append("filter", "safetensors"); url.searchParams.set("limit", String(limit)); const response = await fetch(url.toString(), { headers: { Accept: "application/json", }, next: { revalidate: 300 }, }); if (!response.ok) { throw new Error(`Hugging Face API error: ${response.status}`); } const data = (await response.json()) as HuggingFaceModel[]; const seen = new Set(); const models: ModelOption[] = Array.isArray(data) ? data .filter((m) => hasTag(m, "safetensors") && !isExcludedFormat(m)) .map((m) => normalizeModelId(m)) .filter(Boolean) .filter((id) => { if (seen.has(id)) return false; seen.add(id); return true; }) .map((id) => ({ id, name: id, })) : []; cache.set(cacheKey, { timestamp: now, data: models }); return NextResponse.json(models); } catch (error) { console.error("Error fetching Hugging Face models:", error); if (cached) { return NextResponse.json(cached.data); } return NextResponse.json([], { status: 500 }); } }