From 2e340c004a0192dc39a6d80ad90bedb8270231ff Mon Sep 17 00:00:00 2001 From: 134 Date: Tue, 8 Sep 2026 10:14:03 +0800 Subject: [PATCH] fix: restore bananaimage models --- app/api/admin/bananaimage-models/route.ts | 2 + scripts/configure-image-provider-allowlist.ts | 54 ++++++++++++++++++- tests/routes/vip98k-reference-routing.test.ts | 20 +++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/app/api/admin/bananaimage-models/route.ts b/app/api/admin/bananaimage-models/route.ts index 0fb75d0fd..1076af550 100644 --- a/app/api/admin/bananaimage-models/route.ts +++ b/app/api/admin/bananaimage-models/route.ts @@ -65,6 +65,8 @@ export async function POST(request: Request) { description: preset.description, supportsVision: preset.supportsVision, supportsImageGen: preset.supportsImageGen, + isEnabled: true, + isHidden: false, sortOrder: preset.sortOrder, capabilities: preset.capabilities || null, defaultParams: preset.defaultParams || null, diff --git a/scripts/configure-image-provider-allowlist.ts b/scripts/configure-image-provider-allowlist.ts index 7844c4e05..62fb0d026 100644 --- a/scripts/configure-image-provider-allowlist.ts +++ b/scripts/configure-image-provider-allowlist.ts @@ -4,6 +4,7 @@ import { fetchVip98kModelPresets, type Vip98kModelPreset, } from "../lib/ai/vip98k-models"; +import { BANANAIMAGE_MODEL_PRESETS } from "../lib/ai/bananaimage-models"; import { defaultVip98kRuntimeConfig, VIP98K_RUNTIME_CONFIG_KEY, @@ -12,7 +13,11 @@ import { dotenv.config({ path: ".env.local" }); dotenv.config(); -const ALLOWED_IMAGE_PROVIDER_TYPES = ["alibaba-image", "vip98k"] as const; +const ALLOWED_IMAGE_PROVIDER_TYPES = [ + "alibaba-image", + "bananaimage", + "vip98k", +] as const; function hasFlag(name: string) { return process.argv.includes(name); @@ -47,14 +52,27 @@ async function main() { const alibabaProviders = providers.filter( (provider) => provider.type === "alibaba-image", ); + const bananaImageProviders = providers.filter( + (provider) => provider.type === "bananaimage", + ); 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"); } + if (bananaImageProviders.length !== 1) { + throw new Error( + `Expected one BananaImage provider, found ${bananaImageProviders.length}`, + ); + } const vip98kProvider = vip98kProviders[0]; + const bananaImageProvider = bananaImageProviders[0]; if (!vip98kProvider) throw new Error("VIP98K provider is missing"); + if (!bananaImageProvider) throw new Error("BananaImage provider is missing"); + const bananaImageModelIds = BANANAIMAGE_MODEL_PRESETS.map( + (preset) => preset.modelId, + ); const vip98kApiKey = String( process.env.VIP98K_API_KEY || vip98kProvider.apiKey || "", ).trim(); @@ -130,6 +148,7 @@ async function main() { protectedVip98kModels: qualifiedVip98kPresets.map( (preset) => preset.modelId, ), + restoredBananaImageModels: bananaImageModelIds, disallowedImageModelsToDisable: disallowedImageModels.length, staleVip98kImageModelsToDisable: staleVip98kImageModels.length, imageOnlyProvidersToDisable: imageOnlyProviders.map((provider) => ({ @@ -163,6 +182,23 @@ async function main() { update "AiProvider" set "isEnabled" = true, "updatedAt" = now() where type = 'alibaba-image' `; + await tx` + update "AiProvider" set "isEnabled" = true, "updatedAt" = now() + where id = ${bananaImageProvider.id}::uuid + `; + await tx` + update "AiModel" set + "isEnabled" = true, "isHidden" = false, + "excludeFromHealthBan" = true, "healthStatus" = 'healthy', + "healthConsecutiveFailures" = 0, "healthLastEventType" = null, + "healthLastError" = null, "healthAutoDisabledAt" = null, + "healthAutoDisabledReason" = null, "healthRecoverySuccessStreak" = 0, + "healthObservationStartedAt" = null, "healthUpdatedAt" = now(), + "updatedAt" = now() + where "providerId" = ${bananaImageProvider.id}::uuid + and "supportsImageGen" + and "modelId" = any(${bananaImageModelIds}::text[]) + `; await tx` update "AiModel" m set "isEnabled" = true, "excludeFromHealthBan" = true, @@ -256,17 +292,33 @@ async function main() { and m."isEnabled" and not m."isHidden" order by m."modelId" `; + const activeBananaImageModels = await sql>` + select m."modelId" + from "AiModel" m join "AiProvider" p on p.id = m."providerId" + where p.type = 'bananaimage' and p."isEnabled" and m."supportsImageGen" + and m."isEnabled" and not m."isHidden" + and m."modelId" = any(${bananaImageModelIds}::text[]) + order by m."sortOrder", m."modelId" + `; if (invalidModels.length > 0) { throw new Error("Disallowed image models remain enabled after apply"); } if (activeVip98kModels.length !== qualifiedVip98kPresets.length) { throw new Error("VIP98K active image model count does not match qualified catalog"); } + if (activeBananaImageModels.length !== bananaImageModelIds.length) { + throw new Error( + "BananaImage active image model count does not match configured presets", + ); + } console.log( JSON.stringify( { success: true, activeVip98kModels: activeVip98kModels.map((model) => model.modelId), + activeBananaImageModels: activeBananaImageModels.map( + (model) => model.modelId, + ), disabledImageModelCount: disallowedImageModels.length + staleVip98kImageModels.length, }, null, diff --git a/tests/routes/vip98k-reference-routing.test.ts b/tests/routes/vip98k-reference-routing.test.ts index 6ec2d0ae8..fbde48251 100644 --- a/tests/routes/vip98k-reference-routing.test.ts +++ b/tests/routes/vip98k-reference-routing.test.ts @@ -24,6 +24,7 @@ import { normalizeVip98kRuntimeConfig, resolveVip98kSubmissionTimeoutMs, } from "@/lib/ai/vip98k-runtime-config"; +import { BANANAIMAGE_MODEL_PRESETS } from "@/lib/ai/bananaimage-models"; const textToImageInvocation: Vip98kTaskInvocation = { kind: "tool", @@ -37,6 +38,25 @@ const textToImageInvocation: Vip98kTaskInvocation = { }; test.describe("VIP98K reference-image routing", () => { + test("keeps every restored BananaImage preset multi-reference capable", () => { + expect(BANANAIMAGE_MODEL_PRESETS.map((preset) => preset.modelId)).toEqual([ + "gemini-3-pro-image-preview", + "gemini-3.1-flash-image-preview", + "gemini-3-pro-image-preview-plus", + "gemini-3.1-flash-image-preview-plus", + "gpt-image-2", + ]); + expect( + BANANAIMAGE_MODEL_PRESETS.every( + (preset) => + preset.supportsImageGen && + preset.supportsVision && + preset.capabilities?.supportsMultiImageReference === true && + Number(preset.capabilities?.maxReferenceImages) >= 2, + ), + ).toBe(true); + }); + test("uses a stable cross-process cache key for staged references", () => { const source = "https://storage.example/uploads/reference.png"; const key = getVip98kReferenceStagingObjectKey(source);