ai-chatbot/scripts/verify-atlascloud-frontend.ts

205 lines
6.3 KiB
TypeScript

import { fetchAtlasCloudImagePresets } from "../lib/ai/atlascloud-models";
import { createAtlasCloudRequestBody } from "../lib/ai/atlascloud-request";
import {
is3DModelDescriptor,
isImageGenerationModelDescriptor,
isVideoModelDescriptor,
} from "../lib/ai/model-classification";
const FAKE_IMAGES = Array.from(
{ length: 14 },
(_, index) => `https://example.com/reference-${index + 1}.png`
);
const FAKE_VIDEOS = Array.from(
{ length: 4 },
(_, index) => `https://example.com/reference-${index + 1}.mp4`
);
type Failure = {
modelId: string;
scenario: string;
error: string;
};
function validateBody(
body: Record<string, unknown>,
runtime: any
) {
const missing = runtime.requiredFields.filter(
(key: string) => body[key] === undefined
);
if (missing.length > 0) {
throw new Error(`缺少必填参数:${missing.join(", ")}`);
}
for (const [key, values] of Object.entries(
runtime.enums as Record<string, Array<string | number>>
)) {
if (body[key] === undefined || !values.length) continue;
if (!values.some((value) => String(value) === String(body[key]))) {
throw new Error(`${key}=${String(body[key])} 不在官方枚举中`);
}
}
if (runtime.videoClipInput && body[runtime.videoClipInput.key] !== undefined) {
const clips = body[runtime.videoClipInput.key];
if (!Array.isArray(clips) || clips.length === 0) {
throw new Error("video_clips 请求格式无效");
}
for (const clip of clips) {
if (!clip || typeof clip !== "object" || !("url" in clip)) {
throw new Error("video_clips 缺少 url");
}
}
}
}
async function main() {
const catalog = await fetchAtlasCloudImagePresets();
const failures: Failure[] = catalog.errors.map((item) => ({
modelId: item.modelId,
scenario: "catalog-schema",
error: item.error,
}));
let simulatedCases = 0;
let imageModels = 0;
let model3dModels = 0;
let videoReferenceModels = 0;
for (const preset of catalog.presets) {
const capabilities = preset.capabilities as Record<string, any>;
const descriptor = {
providerType: "atlascloud",
modelId: preset.modelId,
capabilities,
supportsImageGen: preset.supportsImageGen,
};
const mediaType = String(capabilities.mediaType || "");
if (mediaType === "3d") {
model3dModels += 1;
simulatedCases += 1;
if (
!is3DModelDescriptor(descriptor) ||
isImageGenerationModelDescriptor(descriptor) ||
isVideoModelDescriptor(descriptor)
) {
failures.push({
modelId: preset.modelId,
scenario: "3d-classification",
error: "3D 模型被错误归入图片或视频模型",
});
}
continue;
}
imageModels += 1;
if (
!isImageGenerationModelDescriptor(descriptor) ||
is3DModelDescriptor(descriptor) ||
isVideoModelDescriptor(descriptor)
) {
failures.push({
modelId: preset.modelId,
scenario: "image-classification",
error: "图片模型分类不正确",
});
continue;
}
const imageGenConfig = preset.imageGenConfig as Record<string, any>;
const runtime = imageGenConfig.atlasCloud;
if (!runtime) {
failures.push({
modelId: preset.modelId,
scenario: "runtime-config",
error: "缺少 AtlasCloud 运行配置",
});
continue;
}
const ratios = imageGenConfig.supportedAspectRatios?.length
? imageGenConfig.supportedAspectRatios
: [String(preset.defaultParams.aspectRatio || "1:1")];
const sizes = imageGenConfig.supportedImageSizes?.length
? imageGenConfig.supportedImageSizes
: [String(preset.defaultParams.imageSize || "1K")];
const counts = runtime.outputCountKey && runtime.maxImages > 1
? [1, runtime.maxImages]
: [1];
const requiredImageCount = runtime.imageInputs
.filter((input: any) => input.required)
.reduce((total: number, input: any) => total + input.maxItems, 0);
const imageReferenceCounts = new Set<number>([
requiredImageCount,
...(runtime.maxReferenceImages > requiredImageCount
? [runtime.maxReferenceImages]
: []),
]);
const requiredVideoCount = runtime.videoClipInput?.required
? runtime.videoClipInput.maxItems
: 0;
const videoReferenceCounts = new Set<number>([
requiredVideoCount,
...(runtime.maxVideoReferences > requiredVideoCount
? [runtime.maxVideoReferences]
: []),
]);
if (runtime.maxVideoReferences > 0) videoReferenceModels += 1;
for (const ratio of ratios) {
for (const size of sizes) {
for (const imageCount of counts) {
for (const referenceImageCount of imageReferenceCounts) {
for (const referenceVideoCount of videoReferenceCounts) {
const scenario = [
`ratio=${ratio}`,
`size=${size}`,
`count=${imageCount}`,
`images=${referenceImageCount}`,
`videos=${referenceVideoCount}`,
].join(",");
simulatedCases += 1;
try {
const { body } = createAtlasCloudRequestBody({
modelId: preset.modelId,
prompt: "AtlasCloud 前端请求模拟测试",
referenceImages: FAKE_IMAGES.slice(0, referenceImageCount),
referenceVideos: FAKE_VIDEOS.slice(0, referenceVideoCount),
imageParams: {
aspectRatio: ratio,
imageSize: size,
imageCount,
},
modelConfig: imageGenConfig,
});
validateBody(body, runtime);
} catch (error) {
failures.push({
modelId: preset.modelId,
scenario,
error: error instanceof Error ? error.message : String(error),
});
}
}
}
}
}
}
}
const summary = {
catalogModels: catalog.catalogCount,
availableModels: catalog.presets.length,
imageModels,
model3dModels,
videoReferenceModels,
simulatedCases,
passedCases: simulatedCases - failures.length,
failedCases: failures.length,
failures,
};
console.log(JSON.stringify(summary, null, 2));
if (failures.length > 0) process.exitCode = 1;
}
void main();