mirror of
https://github.com/luckyyzh/pi-agent-integrated.git
synced 2026-10-03 11:09:34 +00:00
89 lines
3.6 KiB
TypeScript
89 lines
3.6 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { resolveModelDiscoveryAuth } from "@/lib/model-discovery-auth";
|
|
import { buildModelsListUrl, parseDiscoveredModels } from "@/lib/model-discovery";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const DISCOVERY_TIMEOUT_MS = 20_000;
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
}
|
|
|
|
function hasHeader(headers: Headers, name: string): boolean {
|
|
return headers.has(name);
|
|
}
|
|
|
|
function buildHeaders(api: string, apiKey: string | undefined, configured: Record<string, string>): Headers {
|
|
const headers = new Headers(configured);
|
|
if (!hasHeader(headers, "accept")) headers.set("Accept", "application/json");
|
|
if (!apiKey) return headers;
|
|
|
|
if (api === "anthropic-messages") {
|
|
if (!hasHeader(headers, "x-api-key")) headers.set("x-api-key", apiKey);
|
|
if (!hasHeader(headers, "anthropic-version")) headers.set("anthropic-version", "2023-06-01");
|
|
} else if (api === "google-generative-ai") {
|
|
if (!hasHeader(headers, "x-goog-api-key")) headers.set("x-goog-api-key", apiKey);
|
|
} else if (!hasHeader(headers, "authorization")) {
|
|
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
}
|
|
return headers;
|
|
}
|
|
|
|
export async function POST(req: Request) {
|
|
try {
|
|
const body = await req.json() as { providerName?: unknown; provider?: unknown };
|
|
const providerName = typeof body.providerName === "string" ? body.providerName.trim() : "";
|
|
if (!providerName) return NextResponse.json({ error: "providerName is required" }, { status: 400 });
|
|
if (!isRecord(body.provider)) return NextResponse.json({ error: "provider is required" }, { status: 400 });
|
|
|
|
const baseUrl = typeof body.provider.baseUrl === "string" ? body.provider.baseUrl.trim() : "";
|
|
if (!baseUrl) return NextResponse.json({ error: "Base URL is required" }, { status: 400 });
|
|
const api = typeof body.provider.api === "string" && body.provider.api
|
|
? body.provider.api
|
|
: "openai-completions";
|
|
|
|
let endpoint: URL;
|
|
try {
|
|
endpoint = buildModelsListUrl(baseUrl, api);
|
|
} catch {
|
|
return NextResponse.json({ error: "Base URL is invalid" }, { status: 400 });
|
|
}
|
|
|
|
const auth = await resolveModelDiscoveryAuth(providerName, body.provider);
|
|
if (typeof body.provider.apiKey === "string" && body.provider.apiKey.trim() && !auth.apiKey) {
|
|
return NextResponse.json({ error: `No API key found for "${providerName}"` }, { status: 400 });
|
|
}
|
|
|
|
const response = await fetch(endpoint, {
|
|
cache: "no-store",
|
|
headers: buildHeaders(api, auth.apiKey, auth.headers),
|
|
signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),
|
|
});
|
|
const responseText = await response.text();
|
|
if (!response.ok) {
|
|
return NextResponse.json({
|
|
error: responseText.slice(0, 500) || `Upstream returned HTTP ${response.status}`,
|
|
status: response.status,
|
|
}, { status: 502 });
|
|
}
|
|
|
|
let payload: unknown;
|
|
try {
|
|
payload = JSON.parse(responseText);
|
|
} catch {
|
|
return NextResponse.json({ error: "Upstream model list was not valid JSON" }, { status: 502 });
|
|
}
|
|
const models = parseDiscoveredModels(payload);
|
|
if (models.length === 0) {
|
|
return NextResponse.json({ error: "No models found in the upstream response" }, { status: 502 });
|
|
}
|
|
|
|
return NextResponse.json({ models, endpoint: endpoint.toString() });
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
const status = error instanceof DOMException && error.name === "TimeoutError" ? 504 : 500;
|
|
return NextResponse.json({ error: message }, { status });
|
|
}
|
|
}
|