Spaces:
Runtime error
Runtime error
| 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<string> | 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<string>(); | |
| for (const c of active) { | |
| const pId = String((c as Record<string, unknown>).provider); | |
| activeProviders.add(pId); | |
| const alias = PROVIDER_ID_TO_ALIAS[pId]; | |
| if (alias) activeProviders.add(alias); | |
| } | |
| const connectionsByProvider = new Map<string, typeof active>(); | |
| 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<string | null | undefined>) => { | |
| const seen = new Set<string>(); | |
| 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 }); | |
| } | |
| } | |