实现 SFX V2 T1 契约与提示词基础

统一前后端 SFX Prompt Unicode canonicalization、字符计数和 2048 边界
固化 52 个音效预设、追加规则及跨语言测试向量
演进共享音频请求响应、duration、Loop 和 V2 metadata 契约
增加 External model 输入矩阵纯函数并保持正式接线归属 T5
补齐前端提交、读取、深拷贝与请求边界回归测试
更新共享计划并标记 T1 完成且当前不可发布
This commit is contained in:
2026-08-06 13:05:48 +00:00
parent 6fd731f56c
commit cd80c085ff
24 changed files with 1559 additions and 97 deletions
@@ -0,0 +1,60 @@
import { describe, expect, it } from 'vitest';
import soundEffectPromptCanonicalizationCases from '../../../packages/shared/test-fixtures/sound-effect-prompt-canonicalization.json';
import {
canonicalizeSoundEffectPrompt,
countSoundEffectPromptCodePoints,
SOUND_EFFECT_PROMPT_MAX_CODE_POINTS,
validateSoundEffectPrompt,
} from './ImageCanvasSoundEffectPromptModel';
describe('ImageCanvasSoundEffectPromptModel', () => {
it('matches the shared Unicode canonicalization fixtures', () => {
for (const fixture of soundEffectPromptCanonicalizationCases) {
expect(canonicalizeSoundEffectPrompt(fixture.input), fixture.name).toBe(
fixture.prompt,
);
expect(
countSoundEffectPromptCodePoints(fixture.input),
fixture.name,
).toBe(fixture.charCount);
expect(
canonicalizeSoundEffectPrompt(
canonicalizeSoundEffectPrompt(fixture.input),
),
fixture.name,
).toBe(fixture.prompt);
}
});
it('accepts 2048 code points and rejects 2049 without truncating', () => {
const maximum = `\u0085${'😀'.repeat(
SOUND_EFFECT_PROMPT_MAX_CODE_POINTS,
)}\u2003`;
const accepted = validateSoundEffectPrompt(maximum);
expect(accepted).toEqual({
ok: true,
prompt: '😀'.repeat(SOUND_EFFECT_PROMPT_MAX_CODE_POINTS),
charCount: SOUND_EFFECT_PROMPT_MAX_CODE_POINTS,
});
const overlong = '声'.repeat(SOUND_EFFECT_PROMPT_MAX_CODE_POINTS + 1);
expect(validateSoundEffectPrompt(overlong)).toEqual({
ok: false,
prompt: overlong,
charCount: SOUND_EFFECT_PROMPT_MAX_CODE_POINTS + 1,
reason: 'too-long',
});
});
it('rejects empty canonical prompts without adding a default description', () => {
for (const prompt of ['', ' \t\r\n', '\u0085\u2003\u00a0']) {
expect(validateSoundEffectPrompt(prompt)).toEqual({
ok: false,
prompt: '',
charCount: 0,
reason: 'empty',
});
}
});
});