fix: restore bananaimage models

This commit is contained in:
134 2026-09-08 10:14:03 +08:00
parent f5a4cca1fe
commit 3f34197eeb
3 changed files with 75 additions and 1 deletions

View File

@ -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,

View File

@ -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<Array<{ modelId: string }>>`
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,

View File

@ -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);