import { NextResponse } from "next/server"; import { getModelAliases, setModelAlias, getProviderConnections } from "@/models"; import { AI_MODELS, PROVIDER_ID_TO_ALIAS } from "@/shared/constants/models"; import { updateModelAliasSchema } from "@/shared/validation/schemas"; import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; import { hasEligibleConnectionForModel } from "@/domain/connectionModelRules"; // GET /api/models - Get models with aliases (only from active providers by default) export async function GET(request: Request) { try { const { searchParams } = new URL(request.url); const showAll = searchParams.get("all") === "true"; const modelAliases = await getModelAliases(); // Get active provider connections to filter available models let activeProviders: Set | null = null; if (!showAll) { try { const connections = await getProviderConnections(); const active = connections.filter((c: any) => c.isActive !== false); // Include both provider IDs and their aliases in the active set. // PROVIDER_MODELS keys are aliases (e.g. 'cc' for 'claude', 'gh' for 'github'). // DB connections are stored under provider IDs ('claude', 'github'). // Without this, models for aliased providers always appear unconfigured. activeProviders = new Set(); for (const c of active) { const pId = String((c as Record).provider); activeProviders.add(pId); const alias = PROVIDER_ID_TO_ALIAS[pId]; if (alias) activeProviders.add(alias); } const connectionsByProvider = new Map(); const registerConnectionKey = ( key: string | null | undefined, connection: (typeof active)[number] ) => { if (!key) return; const existing = connectionsByProvider.get(key) || []; existing.push(connection); connectionsByProvider.set(key, existing); }; for (const connection of active) { registerConnectionKey(connection.provider, connection); registerConnectionKey(PROVIDER_ID_TO_ALIAS[connection.provider], connection); } const getConnectionsForProvider = (...keys: Array) => { const seen = new Set(); const collected: typeof active = []; for (const key of keys) { if (!key) continue; for (const connection of connectionsByProvider.get(key) || []) { if (!connection?.id || seen.has(connection.id)) continue; seen.add(connection.id); collected.push(connection); } } return collected; }; activeProviders = new Set( AI_MODELS.flatMap((model: any) => { const providerKeys = [model.provider, PROVIDER_ID_TO_ALIAS[model.provider]]; return hasEligibleConnectionForModel( getConnectionsForProvider(...providerKeys), model.model ) ? providerKeys.filter(Boolean) : []; }) ); } catch { // If DB unavailable, show all models } } const models = AI_MODELS.map((m: any) => { const fullModel = `${m.provider}/${m.model}`; const available = !activeProviders || activeProviders.has(m.provider); return { ...m, fullModel, alias: modelAliases[fullModel] || m.model, available, }; }).filter((m: any) => showAll || m.available); return NextResponse.json({ models }); } catch (error) { console.log("Error fetching models:", error); return NextResponse.json({ error: "Failed to fetch models" }, { status: 500 }); } } // PUT /api/models - Update model alias export async function PUT(request) { let rawBody; try { rawBody = await request.json(); } catch { return NextResponse.json( { error: { message: "Invalid request", details: [{ field: "body", message: "Invalid JSON body" }], }, }, { status: 400 } ); } try { const validation = validateBody(updateModelAliasSchema, rawBody); if (isValidationFailure(validation)) { return NextResponse.json({ error: validation.error }, { status: 400 }); } const { model, alias } = validation.data; const modelAliases = await getModelAliases(); // Check if alias already exists for different model const existingModel = Object.entries(modelAliases).find( ([key, val]) => val === alias && key !== model ); if (existingModel) { return NextResponse.json({ error: "Alias already in use" }, { status: 400 }); } // Update alias await setModelAlias(model, alias); return NextResponse.json({ success: true, model, alias }); } catch (error) { console.log("Error updating alias:", error); return NextResponse.json({ error: "Failed to update alias" }, { status: 500 }); } }