fix: restore vip98k text image models

This commit is contained in:
134 2026-09-07 15:05:45 +08:00
parent 79705c4360
commit 2dbb14bb6b
3 changed files with 64 additions and 34 deletions

View File

@ -181,6 +181,7 @@ const IMAGE_TOOL_OPERATION_BY_MODEL_ID: Record<string, string> = {
// available to backend routing but remain hidden until their 2026+ release is verified.
const VERIFIED_2026_IMAGE_MODEL_PATTERNS = [
/^nano-banana-2(?:$|[-/])/,
/^nano-banana-fast$/,
/^nano-banana-pro$/,
/^gpt-image-2(?:$|[-/])/,
/^alibaba\/wan-2\.(?:6|7)(?:$|[-/])/,
@ -425,8 +426,14 @@ export function isVip98kCatalogModelAvailable(model: Vip98kCatalogModel) {
export function isVip98kCatalogModelEligible(model: Vip98kCatalogModel) {
if (!getModelId(model) || !isVip98kCatalogModelAvailable(model)) return false;
const type = resolvePresetType(model);
if (type !== "image" && type !== "video") return true;
return hasMediaReferenceInput(model, type);
if (type === "image") {
return (
hasMediaReferenceInput(model, type) ||
isVip98kTextToImageCatalogModel(model)
);
}
if (type === "video") return hasMediaReferenceInput(model, type);
return true;
}
export function filterVip98kCatalogModels(models: Vip98kCatalogModel[]) {
@ -652,7 +659,7 @@ function createImagePreset(
type: "image",
supportsVision: supportsReferenceImages,
supportsImageGen: true,
isHidden: !isVip98kImageModelVisible(model) || !supportsReferenceImages,
isHidden: !isVip98kImageModelVisible(model),
capabilities: {
provider: "vip98k",
kind: "image",

View File

@ -19,12 +19,7 @@ function toJsonValue(value: unknown): postgres.JSONValue {
}
function isQualifiedVip98kImagePreset(preset: Vip98kModelPreset) {
const capabilities = preset.capabilities as Record<string, unknown>;
return (
preset.supportsImageGen &&
!preset.isHidden &&
capabilities.supportsMultiImageReference === true
);
return preset.supportsImageGen && !preset.isHidden;
}
async function main() {
@ -40,7 +35,7 @@ async function main() {
});
const qualifiedVip98kPresets = presets.filter(isQualifiedVip98kImagePreset);
if (qualifiedVip98kPresets.length === 0) {
throw new Error("VIP98K catalog contains no qualified multi-image model");
throw new Error("VIP98K catalog contains no qualified image model");
}
const sql = postgres(databaseUrl, { prepare: false, max: 1 });
@ -64,23 +59,15 @@ async function main() {
Array<{
id: string;
modelId: string;
healthStatus: string | null;
}>
>`
select id, "modelId", "healthStatus"
select id, "modelId"
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 }>
@ -123,7 +110,9 @@ async function main() {
mode: apply ? "apply" : "dry-run",
allowedImageProviderTypes: ALLOWED_IMAGE_PROVIDER_TYPES,
qualifiedVip98kModels: qualifiedVip98kPresets.map((preset) => preset.modelId),
autoDisabledVip98kModelsPreserved: autoDisabledQualifiedModelIds,
protectedVip98kModels: qualifiedVip98kPresets.map(
(preset) => preset.modelId,
),
disallowedImageModelsToDisable: disallowedImageModels.length,
staleVip98kImageModelsToDisable: staleVip98kImageModels.length,
imageOnlyProvidersToDisable: imageOnlyProviders.map((provider) => ({
@ -148,11 +137,21 @@ async function main() {
update "AiProvider" set "isEnabled" = true, "updatedAt" = now()
where type = 'alibaba-image'
`;
await tx`
update "AiModel" m set
"isEnabled" = true, "excludeFromHealthBan" = true,
"healthStatus" = 'healthy', "healthConsecutiveFailures" = 0,
"healthLastEventType" = null, "healthLastError" = null,
"healthAutoDisabledAt" = null, "healthAutoDisabledReason" = null,
"healthRecoverySuccessStreak" = 0,
"healthObservationStartedAt" = null,
"healthUpdatedAt" = now(), "updatedAt" = now()
from "AiProvider" p
where p.id = m."providerId" and p.type = 'alibaba-image'
and m."supportsImageGen" and not m."isHidden"
`;
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
@ -164,7 +163,12 @@ async function main() {
update "AiModel" set
"displayName" = ${preset.displayName}, description = ${preset.description},
"supportsVision" = ${preset.supportsVision}, "supportsImageGen" = true,
"isEnabled" = ${!preserveAutoDisabled}, "isHidden" = false,
"isEnabled" = true, "isHidden" = false,
"excludeFromHealthBan" = true, "healthStatus" = 'healthy',
"healthConsecutiveFailures" = 0, "healthLastEventType" = null,
"healthLastError" = null, "healthAutoDisabledAt" = null,
"healthAutoDisabledReason" = null, "healthRecoverySuccessStreak" = 0,
"healthObservationStartedAt" = null, "healthUpdatedAt" = now(),
"sortOrder" = ${preset.sortOrder},
capabilities = ${capabilities}, "defaultParams" = ${defaultParams},
"imageGenConfig" = ${imageGenConfig}, "fallbackModelId1" = null,
@ -176,11 +180,13 @@ async function main() {
insert into "AiModel" (
"providerId", "modelId", "displayName", description, "supportsVision",
"supportsImageGen", "isEnabled", "isHidden", "sortOrder", capabilities,
"defaultParams", "imageGenConfig", "isDefault", "isDefaultTextEditModel"
"defaultParams", "imageGenConfig", "isDefault", "isDefaultTextEditModel",
"excludeFromHealthBan", "healthStatus"
) values (
${vip98kProvider.id}::uuid, ${preset.modelId}, ${preset.displayName},
${preset.description}, ${preset.supportsVision}, true, true, false,
${preset.sortOrder}, ${capabilities}, ${defaultParams}, ${imageGenConfig}, false, false
${preset.sortOrder}, ${capabilities}, ${defaultParams}, ${imageGenConfig},
false, false, true, 'healthy'
)
`;
}
@ -227,9 +233,7 @@ async function main() {
if (invalidModels.length > 0) {
throw new Error("Disallowed image models remain enabled after apply");
}
const expectedActiveVip98kCount =
qualifiedVip98kPresets.length - autoDisabledQualifiedModelIds.length;
if (activeVip98kModels.length !== expectedActiveVip98kCount) {
if (activeVip98kModels.length !== qualifiedVip98kPresets.length) {
throw new Error("VIP98K active image model count does not match qualified catalog");
}
console.log(

View File

@ -355,10 +355,10 @@ test.describe("VIP98K reference-image routing", () => {
).toBe(false);
});
test("excludes text-only VIP98K image models from editing workflows", () => {
test("publishes text-only VIP98K models without reference-image support", () => {
const [preset] = buildVip98kModelPresets([
{
id: "nano-banana-pro",
id: "gpt-image-2-vip",
type: "image",
capabilities: ["openai.images.generations", "text_to_image"],
invocation: {
@ -366,12 +366,22 @@ test.describe("VIP98K reference-image routing", () => {
method: "POST",
path: "/v1/images/generations",
content_type: "application/json",
request_example: { model: "nano-banana-pro", n: 1, prompt: "Generate" },
request_example: { model: "gpt-image-2-vip", n: 1, prompt: "Generate" },
},
},
]);
expect(preset).toBeUndefined();
expect(preset).toMatchObject({
modelId: "gpt-image-2-vip",
supportsVision: false,
supportsImageGen: true,
isHidden: false,
capabilities: {
supportedModes: ["text-to-image"],
supportsMultiImageReference: false,
maxReferenceImages: 0,
},
});
});
test("does not inherit multi-image support from broad authorized metadata", () => {
@ -395,6 +405,15 @@ test.describe("VIP98K reference-image routing", () => {
},
]);
expect(presets).toEqual([]);
expect(presets).toHaveLength(1);
expect(presets[0]).toMatchObject({
modelId: "nano-banana-2-lite",
supportsVision: false,
capabilities: {
supportedModes: ["text-to-image"],
supportsMultiImageReference: false,
maxReferenceImages: 0,
},
});
});
});