Files
9router/src/app/api/providers/[id]/test-models/route.js
T
decoluaandCursor dd1e0f9bcc refactor(registry): B2 migrate to LiteLLM-style schema — unified models[] with kind field
- 71 registry files: flat `media.*Config.models` → `models[]` with `kind` field
- `media` wrapper removed → serviceKinds, *Config fields promoted top-level
- `type` field renamed to `kind` (llm/image/tts/stt/embedding/embedding/video/music)
- providers/index.js: PROVIDER_MEDIA now built from flat top-level media fields
- shared/constants/providers.js: buildProviderEntry reads flat top-level media fields
- route /v1/models: modelKind() uses kind||type; removed subConfig merge block
- models/info route: removed sub-config fallback lookup (all models in PROVIDER_MODELS)
- ttsProviders/index.js: synthesizeViaConfig reads tts models from PROVIDER_MODELS
- test-models route, helpers.js, validate route: kind||type compat
- Baselines: PROVIDERS 62/62 , Alias 90/90 

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 14:32:59 +07:00

67 lines
2.8 KiB
JavaScript

import { NextResponse } from "next/server";
import { getProviderConnectionById } from "@/lib/localDb";
import { getProviderModels, PROVIDER_ID_TO_ALIAS } from "open-sse/config/providerModels.js";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
import { UPDATER_CONFIG } from "@/shared/constants/config";
import { pingModelByKind } from "@/app/api/models/test/ping";
/**
* POST /api/providers/[id]/test-models
* id = connectionId — used only to resolve provider + model list.
* Actual requests go through the internal endpoint that matches each model kind.
*/
export async function POST(request, { params }) {
try {
const { id } = await params;
const connection = await getProviderConnectionById(id);
if (!connection) {
return NextResponse.json({ error: "Connection not found" }, { status: 404 });
}
const providerId = connection.provider;
const isCompatible = isOpenAICompatibleProvider(providerId) || isAnthropicCompatibleProvider(providerId);
const alias = PROVIDER_ID_TO_ALIAS[providerId] || providerId;
let models = getProviderModels(alias);
const baseUrl = `http://127.0.0.1:${process.env.PORT || UPDATER_CONFIG.appPort}`;
// Compatible providers: fetch live model list
if (isCompatible && models.length === 0) {
try {
const modelsRes = await fetch(`${baseUrl}/api/providers/${id}/models`);
if (modelsRes.ok) {
const data = await modelsRes.json();
models = (data.models || []).map((m) => ({ id: m.id || m.name, name: m.name || m.id }));
}
} catch { /* fallback to empty */ }
}
if (models.length === 0) {
return NextResponse.json({ error: "No models configured for this provider" }, { status: 400 });
}
// Warm up with first model to trigger token refresh (if needed) before parallel calls.
// This prevents race condition where multiple requests concurrently refresh the same token.
const [first, ...rest] = models;
const firstKind = first.kind || first.type || "llm";
const firstResult = await pingModelByKind(`${alias}/${first.id}`, firstKind, baseUrl);
const results = [{ modelId: first.id, name: first.name || first.id, ...firstResult }];
if (rest.length > 0) {
const restResults = await Promise.all(
rest.map(async (model) => {
const result = await pingModelByKind(`${alias}/${model.id}`, model.kind || model.type || "llm", baseUrl);
return { modelId: model.id, name: model.name || model.id, ...result };
})
);
results.push(...restResults);
}
return NextResponse.json({ provider: providerId, connectionId: id, results });
} catch (error) {
console.log("Error testing models:", error);
return NextResponse.json({ error: "Test failed" }, { status: 500 });
}
}