chore: add ruxa image model verification script
This commit is contained in:
parent
afd1f84789
commit
a8e62962b8
@ -33,6 +33,7 @@
|
||||
"clean:artifacts": "node scripts/clean-local-artifacts.mjs",
|
||||
"smoke:routes": "node scripts/smoke-routes.mjs",
|
||||
"verify:release": "node scripts/verify-release.mjs",
|
||||
"verify:ruxa-models": "pnpm exec tsx scripts/verify-ruxa-image-models.ts",
|
||||
"backfill:agent-preview-images": "pnpm exec tsx scripts/backfill-agent-workflow-preview-images.ts",
|
||||
"rewrite:agent-preview-image-urls": "pnpm exec tsx scripts/rewrite-agent-workflow-preview-image-urls.ts",
|
||||
"format": "pnpm exec ultracite fix",
|
||||
|
||||
208
scripts/verify-ruxa-image-models.ts
Normal file
208
scripts/verify-ruxa-image-models.ts
Normal file
@ -0,0 +1,208 @@
|
||||
import { config as loadEnv } from "dotenv";
|
||||
import { and, eq, sql } from "drizzle-orm";
|
||||
|
||||
import { db } from "../lib/db";
|
||||
import { aiModel, aiProvider } from "../lib/db/schema";
|
||||
|
||||
loadEnv({ path: ".env.local" });
|
||||
|
||||
type ModelExpectation = {
|
||||
modelId: string;
|
||||
displayName: string;
|
||||
};
|
||||
|
||||
const EXPECTED_MODELS: ModelExpectation[] = [
|
||||
{ modelId: "google/nano-banana-2", displayName: "nano-banana-2「专线」" },
|
||||
{ modelId: "google/nano-banana-pro", displayName: "nano-banana-pro「专线」" },
|
||||
];
|
||||
|
||||
type Row = {
|
||||
providerId: string;
|
||||
providerName: string;
|
||||
providerType: string;
|
||||
modelId: string;
|
||||
displayName: string;
|
||||
supportsImageGen: boolean | null;
|
||||
supportsVision: boolean | null;
|
||||
isEnabled: boolean | null;
|
||||
isHidden: boolean | null;
|
||||
healthStatus: string | null;
|
||||
imageGenConfig: Record<string, unknown> | null;
|
||||
};
|
||||
|
||||
function getConfigFlag(
|
||||
config: Record<string, unknown> | null,
|
||||
key: string
|
||||
): boolean | null {
|
||||
const raw = config?.[key];
|
||||
return typeof raw === "boolean" ? raw : null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const rows = await db
|
||||
.select({
|
||||
providerId: aiProvider.id,
|
||||
providerName: aiProvider.name,
|
||||
providerType: aiProvider.type,
|
||||
modelId: aiModel.modelId,
|
||||
displayName: aiModel.displayName,
|
||||
supportsImageGen: aiModel.supportsImageGen,
|
||||
supportsVision: aiModel.supportsVision,
|
||||
isEnabled: aiModel.isEnabled,
|
||||
isHidden: aiModel.isHidden,
|
||||
healthStatus: aiModel.healthStatus,
|
||||
imageGenConfig: aiModel.imageGenConfig,
|
||||
})
|
||||
.from(aiModel)
|
||||
.innerJoin(aiProvider, eq(aiModel.providerId, aiProvider.id))
|
||||
.where(
|
||||
and(
|
||||
eq(aiProvider.type, "ruxa-image"),
|
||||
sql`LOWER(${aiProvider.name}) LIKE ${"%ruxa%"}`,
|
||||
sql`LOWER(${aiModel.modelId}) IN (${"google/nano-banana-2"}, ${"google/nano-banana-pro"})`
|
||||
)
|
||||
);
|
||||
|
||||
const found = new Map<string, Row[]>();
|
||||
for (const row of rows) {
|
||||
const bucket = found.get(row.providerId) ?? [];
|
||||
bucket.push(row);
|
||||
found.set(row.providerId, bucket);
|
||||
}
|
||||
|
||||
const providers = [...found.entries()].map(([providerId, models]) => ({
|
||||
providerId,
|
||||
providerName: models[0]?.providerName ?? "",
|
||||
providerType: models[0]?.providerType ?? "",
|
||||
modelCount: models.length,
|
||||
models,
|
||||
}));
|
||||
|
||||
const failures: string[] = [];
|
||||
const expectByModelId = new Map(
|
||||
EXPECTED_MODELS.map((entry) => [entry.modelId, entry.displayName])
|
||||
);
|
||||
const expectedSet = new Set(expectByModelId.keys());
|
||||
|
||||
for (const row of rows) {
|
||||
if (!expectedSet.has(row.modelId)) {
|
||||
failures.push(`Unexpected model present in RUXA image provider: ${row.modelId}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const expectedDisplayName = expectByModelId.get(row.modelId);
|
||||
if (expectedDisplayName && row.displayName !== expectedDisplayName) {
|
||||
failures.push(
|
||||
`${row.providerName} ${row.modelId}: displayName mismatch (expected "${expectedDisplayName}", got "${row.displayName}")`
|
||||
);
|
||||
}
|
||||
|
||||
const config =
|
||||
row.imageGenConfig &&
|
||||
typeof row.imageGenConfig === "object" &&
|
||||
!Array.isArray(row.imageGenConfig)
|
||||
? (row.imageGenConfig as Record<string, unknown>)
|
||||
: null;
|
||||
|
||||
if (row.supportsImageGen !== true) {
|
||||
failures.push(`${row.providerName} ${row.modelId}: supportsImageGen is not true`);
|
||||
}
|
||||
if (row.supportsVision !== true) {
|
||||
failures.push(`${row.providerName} ${row.modelId}: supportsVision is not true`);
|
||||
}
|
||||
if (row.isEnabled !== true) {
|
||||
failures.push(`${row.providerName} ${row.modelId}: isEnabled is not true`);
|
||||
}
|
||||
if (row.isHidden === true) {
|
||||
failures.push(`${row.providerName} ${row.modelId}: isHidden should be false`);
|
||||
}
|
||||
if (row.healthStatus !== "healthy") {
|
||||
failures.push(
|
||||
`${row.providerName} ${row.modelId}: healthStatus is ${row.healthStatus ?? "null"}, expected healthy`
|
||||
);
|
||||
}
|
||||
|
||||
if (getConfigFlag(config, "supportsSize") !== true) {
|
||||
failures.push(`${row.providerName} ${row.modelId}: imageGenConfig.supportsSize should be true`);
|
||||
}
|
||||
if (getConfigFlag(config, "supports1K") !== true) {
|
||||
failures.push(
|
||||
`${row.providerName} ${row.modelId}: imageGenConfig.supports1K should be true`
|
||||
);
|
||||
}
|
||||
if (getConfigFlag(config, "supports2K") !== true) {
|
||||
failures.push(
|
||||
`${row.providerName} ${row.modelId}: imageGenConfig.supports2K should be true`
|
||||
);
|
||||
}
|
||||
if (getConfigFlag(config, "supports4K") !== true) {
|
||||
failures.push(
|
||||
`${row.providerName} ${row.modelId}: imageGenConfig.supports4K should be true`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const provider of providers) {
|
||||
const providerModels = provider.models.map((item) => item.modelId);
|
||||
for (const expected of EXPECTED_MODELS) {
|
||||
if (!providerModels.includes(expected.modelId)) {
|
||||
failures.push(
|
||||
`${provider.providerName}: missing required model ${expected.modelId} (${expected.displayName})`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const providerSummary = providers
|
||||
.sort((a, b) => a.providerName.localeCompare(b.providerName))
|
||||
.map((provider) => {
|
||||
return {
|
||||
providerName: provider.providerName,
|
||||
providerType: provider.providerType,
|
||||
providerId: provider.providerId,
|
||||
hasAllTargets: EXPECTED_MODELS.every((expected) =>
|
||||
provider.models.some((row) => row.modelId === expected.modelId)
|
||||
),
|
||||
modelRows: provider.models
|
||||
.sort((a, b) => a.modelId.localeCompare(b.modelId))
|
||||
.map((row) => ({
|
||||
modelId: row.modelId,
|
||||
displayName: row.displayName,
|
||||
isEnabled: row.isEnabled,
|
||||
healthStatus: row.healthStatus,
|
||||
imageSize4K: getConfigFlag(
|
||||
row.imageGenConfig &&
|
||||
typeof row.imageGenConfig === "object" &&
|
||||
!Array.isArray(row.imageGenConfig)
|
||||
? (row.imageGenConfig as Record<string, unknown>)
|
||||
: null,
|
||||
"supports4K"
|
||||
),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
expected: EXPECTED_MODELS,
|
||||
providerSummary,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error("RUXA image model check failed:");
|
||||
failures.forEach((item) => console.error(`- ${item}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("RUXA image model check passed.");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("RUXA image model verification failed to execute:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user