Files
miaojingAI/scripts/test-yuanjie-media-manifest-mapping.mjs

134 lines
6.0 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
const repoRoot = path.resolve(import.meta.dirname, '..');
const {
YUANJIE_IMAGE_MODEL_TEMPLATES,
buildYuanjieSubmit,
} = await import('../src/lib/yuanjie-image-model-templates.ts');
const {
YUANJIE_VIDEO_MODEL_TEMPLATES,
buildYuanjieVideoSubmit,
} = await import('../src/lib/yuanjie-video-model-templates.ts');
const {
getYuanjieSystemApiCapabilitiesFallback,
} = await import('../src/lib/yuanjie-system-manifest.ts');
async function runTest(name, fn) {
try {
await fn();
console.log(`PASS ${name}`);
} catch (error) {
console.error(`FAIL ${name}`);
console.error(error);
process.exitCode = 1;
}
}
function read(relativePath) {
return fs.readFileSync(path.join(repoRoot, relativePath), 'utf8');
}
function imageTemplate(modelName) {
const template = YUANJIE_IMAGE_MODEL_TEMPLATES.find(item => item.modelName === modelName);
assert.ok(template, `missing image template ${modelName}`);
return buildYuanjieSubmit(template);
}
function videoTemplate(modelName) {
const template = YUANJIE_VIDEO_MODEL_TEMPLATES.find(item => item.modelName === modelName);
assert.ok(template, `missing video template ${modelName}`);
return buildYuanjieVideoSubmit(template);
}
await runTest('image-to-image route passes all reference images into Manifest execution', () => {
const source = read('src/app/api/generate/image/route.ts');
assert.match(source, /extraImages/);
assert.match(source, /const referenceImages = normalizeReferenceImages\(image,\s*undefined,\s*extraImages\)/s);
assert.match(source, /inputImages:\s*referenceImages,/);
assert.doesNotMatch(source, /inputImages:\s*image \? \[image\] : \[\]/);
});
await runTest('yuanjie GPT Image 2 uses public image reference URLs for edit/image-to-image', () => {
const submit = imageTemplate('gpt-image-2');
assert.equal(submit.body?.params?.images, '$inputImages.urls');
assert.equal(submit.body?.images, '$inputImages.urls');
assert.equal(submit.body?.base64Array, '$inputImages.urls');
});
await runTest('manifest executor exposes normalized public input image URLs to templates', () => {
const source = read('src/lib/user-api-manifest-executor.ts');
assert.match(source, /inputImageUrls\?: string\[\]/);
assert.match(source, /inputImages:\s*\{\s*dataUrls:\s*input\.inputImages \|\| \[\],\s*urls:\s*input\.inputImageUrls \|\| input\.inputImages \|\| \[\],/s);
assert.match(source, /resolveManifestInputImageReferences\(input\.inputImages \|\| \[\]\)/);
});
await runTest('yuanjie video templates map documented reference fields and mode fields', () => {
assert.equal(videoTemplate('sora-2').body?.params?.input_reference, '$inputImages.urls.0');
assert.equal(videoTemplate('wan2.6-cankaosheng').body?.params?.reference_urls, '$inputImages.urls');
assert.equal(videoTemplate('wan2.6-shouzheng').body?.params?.img_url, '$inputImages.urls.0');
assert.equal(videoTemplate('kling-v3-omni-shouweizhen').body?.params?.image, '$inputImages.urls.0');
assert.equal(videoTemplate('kling-v3-omni-shouweizhen').body?.params?.image_tail, '$inputImages.urls.1');
assert.equal(videoTemplate('happyhorse-r2v').body?.params?.ratio, '$params.aspect_ratio');
assert.equal(videoTemplate('grok-video-3').body?.params?.size, '$params.resolution');
assert.equal(videoTemplate('veo3.1').body?.params?.generation_mode, '$params.quality');
assert.equal(videoTemplate('veo3.1').body?.params?.enhance_prompt, true);
assert.equal(videoTemplate('veo3.1').body?.params?.enable_upsample, false);
});
await runTest('yuanjie HappyHorse text-to-video uses documented media params and output task id path', () => {
const submit = videoTemplate('happyhorse-t2v');
const bodyKeys = Object.keys(submit.body || {}).sort();
assert.deepEqual(bodyKeys, ['model', 'params', 'prompt']);
assert.equal(submit.body?.model, '$profile.model');
assert.equal(submit.body?.prompt, '$prompt');
assert.equal(submit.body?.params?.resolution, '$params.resolution');
assert.equal(submit.body?.params?.ratio, '$params.aspect_ratio');
assert.equal(submit.body?.params?.duration, '$params.duration');
assert.equal(submit.body?.params?.aspect_ratio, undefined);
assert.deepEqual(Object.keys(submit.body?.params || {}).sort(), ['duration', 'ratio', 'resolution']);
assert.match(submit.taskIdPath || '', /output\.task_id/);
});
await runTest('yuanjie system API rows without manifest still expose built-in video capabilities', () => {
const capabilities = getYuanjieSystemApiCapabilitiesFallback({
provider: '元界 AI',
type: 'video',
model_name: 'happyhorse-t2v',
model_group: 'default',
});
assert.ok(capabilities, 'expected built-in capabilities for HappyHorse text-to-video');
assert.deepEqual(capabilities.resolutions?.map(item => item.value), ['720P', '1080P']);
assert.deepEqual(capabilities.aspectRatios?.map(item => item.value), ['16:9', '9:16', '1:1', '4:3', '3:4']);
assert.deepEqual(capabilities.durations?.map(item => item.value), ['3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']);
const mozheCapabilities = getYuanjieSystemApiCapabilitiesFallback({
provider: 'mozheAPI',
type: 'video',
model_name: 'happyhorse-t2v',
model_group: 'default',
});
assert.equal(mozheCapabilities, undefined);
});
await runTest('system default polling candidates repair stale yuanjie manifests before generation', () => {
const source = read('src/lib/server-api-config.ts');
const pollingFunction = source.match(/export async function resolveSystemApiPollingCandidates[\s\S]*?^}/m)?.[0] || '';
assert.match(pollingFunction, /ensureYuanjieSystemApiManifest\(client,\s*row\)/);
assert.match(pollingFunction, /yuanjieManifest\?\.manifestPath/);
assert.match(pollingFunction, /yuanjieManifest\?\.apiUrl/);
});
await runTest('video route passes negative prompt through Manifest params for providers that document it', () => {
const source = read('src/app/api/generate/video/route.ts');
assert.match(source, /negative_prompt:\s*negativePrompt,/);
});
if (process.exitCode) process.exit(process.exitCode);