import { NextResponse } from "next/server"; import { getProviderConnectionById } from "@/models"; import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers"; // Provider models endpoints configuration const PROVIDER_MODELS_CONFIG = { claude: { url: "https://api.anthropic.com/v1/models", method: "GET", headers: { "Anthropic-Version": "2023-06-01", "Content-Type": "application/json" }, authHeader: "x-api-key", parseResponse: (data) => data.data || [] }, gemini: { url: "https://generativelanguage.googleapis.com/v1beta/models", method: "GET", headers: { "Content-Type": "application/json" }, authQuery: "key", // Use query param for API key parseResponse: (data) => data.models || [] }, "gemini-cli": { url: "https://generativelanguage.googleapis.com/v1beta/models", method: "GET", headers: { "Content-Type": "application/json" }, authHeader: "Authorization", authPrefix: "Bearer ", parseResponse: (data) => data.models || [] }, qwen: { url: "https://portal.qwen.ai/v1/models", method: "GET", headers: { "Content-Type": "application/json" }, authHeader: "Authorization", authPrefix: "Bearer ", parseResponse: (data) => data.data || [] }, antigravity: { url: "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:models", method: "POST", headers: { "Content-Type": "application/json" }, authHeader: "Authorization", authPrefix: "Bearer ", body: {}, parseResponse: (data) => data.models || [] }, openai: { url: "https://api.openai.com/v1/models", method: "GET", headers: { "Content-Type": "application/json" }, authHeader: "Authorization", authPrefix: "Bearer ", parseResponse: (data) => data.data || [] }, openrouter: { url: "https://openrouter.ai/api/v1/models", method: "GET", headers: { "Content-Type": "application/json" }, authHeader: "Authorization", authPrefix: "Bearer ", parseResponse: (data) => data.data || [] }, anthropic: { url: "https://api.anthropic.com/v1/models", method: "GET", headers: { "Anthropic-Version": "2023-06-01", "Content-Type": "application/json" }, authHeader: "x-api-key", parseResponse: (data) => data.data || [] }, alicloud: { url: "https://coding.dashscope.aliyuncs.com/v1/models", method: "GET", headers: { "Content-Type": "application/json" }, authHeader: "Authorization", authPrefix: "Bearer ", parseResponse: (data) => data.data || [] } }; /** * GET /api/providers/[id]/models - Get models list from provider */ export async function GET(request, { params }) { try { const { id } = await params; const connection = await getProviderConnectionById(id); if (!connection) { return NextResponse.json({ error: "Connection not found" }, { status: 404 }); } if (isOpenAICompatibleProvider(connection.provider)) { const baseUrl = connection.providerSpecificData?.baseUrl; if (!baseUrl) { return NextResponse.json({ error: "No base URL configured for OpenAI compatible provider" }, { status: 400 }); } const url = `${baseUrl.replace(/\/$/, "")}/models`; const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${connection.apiKey}`, }, }); if (!response.ok) { const errorText = await response.text(); console.log(`Error fetching models from ${connection.provider}:`, errorText); return NextResponse.json( { error: `Failed to fetch models: ${response.status}` }, { status: response.status } ); } const data = await response.json(); const models = data.data || data.models || []; return NextResponse.json({ provider: connection.provider, connectionId: connection.id, models }); } if (isAnthropicCompatibleProvider(connection.provider)) { let baseUrl = connection.providerSpecificData?.baseUrl; if (!baseUrl) { return NextResponse.json({ error: "No base URL configured for Anthropic compatible provider" }, { status: 400 }); } baseUrl = baseUrl.replace(/\/$/, ""); if (baseUrl.endsWith("/messages")) { baseUrl = baseUrl.slice(0, -9); } const url = `${baseUrl}/models`; const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json", "x-api-key": connection.apiKey, "anthropic-version": "2023-06-01", "Authorization": `Bearer ${connection.apiKey}` }, }); if (!response.ok) { const errorText = await response.text(); console.log(`Error fetching models from ${connection.provider}:`, errorText); return NextResponse.json( { error: `Failed to fetch models: ${response.status}` }, { status: response.status } ); } const data = await response.json(); const models = data.data || data.models || []; return NextResponse.json({ provider: connection.provider, connectionId: connection.id, models }); } const config = PROVIDER_MODELS_CONFIG[connection.provider]; if (!config) { return NextResponse.json( { error: `Provider ${connection.provider} does not support models listing` }, { status: 400 } ); } // Get auth token const token = connection.accessToken || connection.apiKey; if (!token) { return NextResponse.json({ error: "No valid token found" }, { status: 401 }); } // Build request URL let url = config.url; if (config.authQuery) { url += `?${config.authQuery}=${token}`; } // Build headers const headers = { ...config.headers }; if (config.authHeader && !config.authQuery) { headers[config.authHeader] = (config.authPrefix || "") + token; } // Make request const fetchOptions = { method: config.method, headers }; if (config.body && config.method === "POST") { fetchOptions.body = JSON.stringify(config.body); } const response = await fetch(url, fetchOptions); if (!response.ok) { const errorText = await response.text(); console.log(`Error fetching models from ${connection.provider}:`, errorText); return NextResponse.json( { error: `Failed to fetch models: ${response.status}` }, { status: response.status } ); } const data = await response.json(); const models = config.parseResponse(data); return NextResponse.json({ provider: connection.provider, connectionId: connection.id, models }); } catch (error) { console.log("Error fetching provider models:", error); return NextResponse.json({ error: "Failed to fetch models" }, { status: 500 }); } }