mirror of
https://github.com/luckyyzh/pi-agent-integrated.git
synced 2026-10-03 11:09:34 +00:00
feat: integrate Pi backend and Pi Web
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
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<ModelCatalogEntry[]>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
var __piModelsDevCatalogCache: CatalogCache | undefined;
|
||||
}
|
||||
|
||||
function getCache(): CatalogCache {
|
||||
return globalThis.__piModelsDevCatalogCache ??= { entries: [], expiresAt: 0 };
|
||||
}
|
||||
|
||||
async function fetchCatalog(): Promise<ModelCatalogEntry[]> {
|
||||
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<ModelCatalogEntry[]> {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user