import dotenv from "dotenv"; import postgres from "postgres"; import { fetchVip98kModelPresets, type Vip98kModelPreset, } from "../lib/ai/vip98k-models"; dotenv.config({ path: ".env.local" }); dotenv.config(); const ALLOWED_IMAGE_PROVIDER_TYPES = ["alibaba-image", "vip98k"] as const; function hasFlag(name: string) { return process.argv.includes(name); } function toJsonValue(value: unknown): postgres.JSONValue { return JSON.parse(JSON.stringify(value)) as postgres.JSONValue; } function isQualifiedVip98kImagePreset(preset: Vip98kModelPreset) { const capabilities = preset.capabilities as Record; return ( preset.supportsImageGen && !preset.isHidden && capabilities.supportsMultiImageReference === true ); } async function main() { const apply = hasFlag("--apply"); const databaseUrl = String(process.env.POSTGRES_URL || "").trim(); const vip98kApiKey = String(process.env.VIP98K_API_KEY || "").trim(); if (!databaseUrl) throw new Error("POSTGRES_URL is required"); if (!vip98kApiKey) throw new Error("VIP98K_API_KEY is required"); const presets = await fetchVip98kModelPresets({ baseURL: process.env.VIP98K_BASE_URL || "https://api.vip98k.com", apiKey: vip98kApiKey, }); const qualifiedVip98kPresets = presets.filter(isQualifiedVip98kImagePreset); if (qualifiedVip98kPresets.length === 0) { throw new Error("VIP98K catalog contains no qualified multi-image model"); } const sql = postgres(databaseUrl, { prepare: false, max: 1 }); try { const providers = await sql< Array<{ id: string; type: string; name: string; isEnabled: boolean }> >`select id, type, name, "isEnabled" from "AiProvider" order by "createdAt"`; const vip98kProviders = providers.filter((provider) => provider.type === "vip98k"); const alibabaProviders = providers.filter( (provider) => provider.type === "alibaba-image", ); if (vip98kProviders.length !== 1) { throw new Error(`Expected one VIP98K provider, found ${vip98kProviders.length}`); } if (alibabaProviders.length === 0) { throw new Error("Alibaba official image provider is missing"); } const vip98kProvider = vip98kProviders[0]; if (!vip98kProvider) throw new Error("VIP98K provider is missing"); const existingVip98kModels = await sql< Array<{ id: string; modelId: string; healthStatus: string | null; }> >` select id, "modelId", "healthStatus" from "AiModel" where "providerId" = ${vip98kProvider.id}::uuid `; const existingVip98kByModelId = new Map( existingVip98kModels.map((model) => [model.modelId, model]), ); const autoDisabledQualifiedModelIds = qualifiedVip98kPresets .filter( (preset) => existingVip98kByModelId.get(preset.modelId)?.healthStatus === "auto_disabled", ) .map((preset) => preset.modelId); const disallowedImageModels = await sql< Array<{ id: string; providerType: string; modelId: string }> >` select m.id, p.type as "providerType", m."modelId" from "AiModel" m join "AiProvider" p on p.id = m."providerId" where m."supportsImageGen" and p.type != all(${ALLOWED_IMAGE_PROVIDER_TYPES}::text[]) order by p.type, m."modelId" `; const staleVip98kImageModels = await sql>` select m.id, m."modelId" from "AiModel" m where m."providerId" = ${vip98kProvider.id}::uuid and m."supportsImageGen" and m."modelId" != all(${qualifiedVip98kPresets.map((preset) => preset.modelId)}::text[]) order by m."modelId" `; const imageOnlyProviders = await sql>` select p.id, p.type, p.name from "AiProvider" p where p.type != all(${ALLOWED_IMAGE_PROVIDER_TYPES}::text[]) and ( p.type like '%-image' or p.type in ('bananaimage', 'gemini', 'huanke-gemini') or ( exists (select 1 from "AiModel" m where m."providerId" = p.id and m."supportsImageGen") and not exists ( select 1 from "AiModel" m where m."providerId" = p.id and not m."supportsImageGen" and m."isEnabled" ) ) ) order by p.type, p.name `; console.log( JSON.stringify( { mode: apply ? "apply" : "dry-run", allowedImageProviderTypes: ALLOWED_IMAGE_PROVIDER_TYPES, qualifiedVip98kModels: qualifiedVip98kPresets.map((preset) => preset.modelId), autoDisabledVip98kModelsPreserved: autoDisabledQualifiedModelIds, disallowedImageModelsToDisable: disallowedImageModels.length, staleVip98kImageModelsToDisable: staleVip98kImageModels.length, imageOnlyProvidersToDisable: imageOnlyProviders.map((provider) => ({ type: provider.type, name: provider.name, })), }, null, 2, ), ); if (!apply) return; await sql.begin(async (tx) => { await tx` update "AiProvider" set "baseUrl" = 'https://api.vip98k.com', "apiKey" = ${vip98kApiKey}, "isEnabled" = true, "updatedAt" = now() where id = ${vip98kProvider.id}::uuid `; await tx` update "AiProvider" set "isEnabled" = true, "updatedAt" = now() where type = 'alibaba-image' `; for (const preset of qualifiedVip98kPresets) { const preserveAutoDisabled = autoDisabledQualifiedModelIds.includes( preset.modelId, ); const capabilities = tx.json(toJsonValue(preset.capabilities)); const defaultParams = tx.json(toJsonValue(preset.defaultParams)); const imageGenConfig = preset.imageGenConfig ? tx.json(toJsonValue(preset.imageGenConfig)) : null; const existing = existingVip98kByModelId.get(preset.modelId); if (existing) { await tx` update "AiModel" set "displayName" = ${preset.displayName}, description = ${preset.description}, "supportsVision" = ${preset.supportsVision}, "supportsImageGen" = true, "isEnabled" = ${!preserveAutoDisabled}, "isHidden" = false, "sortOrder" = ${preset.sortOrder}, capabilities = ${capabilities}, "defaultParams" = ${defaultParams}, "imageGenConfig" = ${imageGenConfig}, "fallbackModelId1" = null, "fallbackModelId2" = null, "updatedAt" = now() where id = ${existing.id}::uuid `; } else { await tx` insert into "AiModel" ( "providerId", "modelId", "displayName", description, "supportsVision", "supportsImageGen", "isEnabled", "isHidden", "sortOrder", capabilities, "defaultParams", "imageGenConfig", "isDefault", "isDefaultTextEditModel" ) values ( ${vip98kProvider.id}::uuid, ${preset.modelId}, ${preset.displayName}, ${preset.description}, ${preset.supportsVision}, true, true, false, ${preset.sortOrder}, ${capabilities}, ${defaultParams}, ${imageGenConfig}, false, false ) `; } } await tx` update "AiModel" m set "isEnabled" = false, "isHidden" = true, "isDefault" = false, "isDefaultTextEditModel" = false, "updatedAt" = now() from "AiProvider" p where p.id = m."providerId" and m."supportsImageGen" and p.type != all(${ALLOWED_IMAGE_PROVIDER_TYPES}::text[]) `; await tx` update "AiModel" set "isEnabled" = false, "isHidden" = true, "isDefault" = false, "isDefaultTextEditModel" = false, "updatedAt" = now() where "providerId" = ${vip98kProvider.id}::uuid and "supportsImageGen" and "modelId" != all(${qualifiedVip98kPresets.map((preset) => preset.modelId)}::text[]) `; if (imageOnlyProviders.length > 0) { await tx` update "AiProvider" set "isEnabled" = false, "updatedAt" = now() where id = any(${imageOnlyProviders.map((provider) => provider.id)}::uuid[]) `; } }); const invalidModels = await sql< Array<{ providerType: string; modelId: string }> >` select p.type as "providerType", m."modelId" from "AiModel" m join "AiProvider" p on p.id = m."providerId" where m."supportsImageGen" and m."isEnabled" and p.type != all(${ALLOWED_IMAGE_PROVIDER_TYPES}::text[]) `; const activeVip98kModels = await sql>` select m."modelId" from "AiModel" m join "AiProvider" p on p.id = m."providerId" where p.type = 'vip98k' and p."isEnabled" and m."supportsImageGen" and m."isEnabled" and not m."isHidden" order by m."modelId" `; if (invalidModels.length > 0) { throw new Error("Disallowed image models remain enabled after apply"); } const expectedActiveVip98kCount = qualifiedVip98kPresets.length - autoDisabledQualifiedModelIds.length; if (activeVip98kModels.length !== expectedActiveVip98kCount) { throw new Error("VIP98K active image model count does not match qualified catalog"); } console.log( JSON.stringify( { success: true, activeVip98kModels: activeVip98kModels.map((model) => model.modelId), disabledImageModelCount: disallowedImageModels.length + staleVip98kImageModels.length, }, null, 2, ), ); } finally { await sql.end(); } } main().catch((error) => { console.error(error instanceof Error ? error.message : String(error)); process.exit(1); });