import { and, eq } from "drizzle-orm"; import { db } from "@/lib/db"; import { aiModel, aiProvider } from "@/lib/db/schema"; import { normalizeVip98kBaseUrl } from "@/lib/ai/vip98k-models"; import { selectVip98kChatInvocation, vip98kChatSupportsVision } from "@/lib/ai/vip98k-chat-contract"; // Explicit administration command: discover the provider's published vision // chat routes. Never re-enable a disabled model or change the user's defaults. async function main() { const providers = await db.select().from(aiProvider).where(and(eq(aiProvider.type, "vip98k"), eq(aiProvider.isEnabled, true))); const added: string[] = []; for (const provider of providers) { const response = await fetch(`${normalizeVip98kBaseUrl(provider.baseUrl)}/api/v2/catalog/models`, { headers: { Accept: "application/json", "User-Agent": "Mozilla/5.0" }, redirect: "error", signal: AbortSignal.timeout(15_000), }); if (!response.ok) throw new Error(`Provider catalog unavailable (${response.status})`); const payload = await response.json(); const entries = (payload?.data?.sections || []).flatMap((section: { models?: Record[] }) => section.models || []); const existing = await db.select({ modelId: aiModel.modelId }).from(aiModel).where(eq(aiModel.providerId, provider.id)); const known = new Set(existing.map(model => model.modelId)); let index = 0; for (const entry of entries) { const contract = selectVip98kChatInvocation(entry); if (!contract || !vip98kChatSupportsVision(entry) || !entry.id || known.has(entry.id)) continue; const id = String(entry.id); // The catalog's chat contract and vision modality are both required. // Pure generation, media-task and Anthropic-only routes are excluded. await db.insert(aiModel).values({ providerId: provider.id, modelId: id, displayName: id, supportsVision: true, supportsImageGen: false, isEnabled: true, isHidden: true, isDefault: false, sortOrder: 10_000 + index++, defaultParams: { agentAutoModel: false }, capabilities: { kind: "chat", multimodalModel: true, agentFallbackOnly: true, vip98k: entry }, }); known.add(id); added.push(id); } } console.log(JSON.stringify({ added, count: added.length, imageModelsUnchanged: true, defaultsUnchanged: true })); } main().then(() => process.exit(0)).catch(() => { console.error("Multimodal fallback catalog sync failed; no credentials were logged"); process.exit(1); });