feat: expand novel IP and production workflows
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
beginAiRequestInspection,
|
||||
clearAiRequestInspections,
|
||||
completeAiRequestInspection,
|
||||
isInspectableAiRequest,
|
||||
subscribeAiRequestInspections,
|
||||
type AiRequestInspectionRecord
|
||||
} from '../../../shared/frontend/ai-request-inspector/ai-request-inspector';
|
||||
|
||||
describe('AI request inspector route matching', () => {
|
||||
it.each([
|
||||
['/provider-lab/runs', 'POST'],
|
||||
['/projects/p1/reviews/text', 'POST'],
|
||||
['/assets/a1/review', 'POST'],
|
||||
['/admin/novel-creation-wizard/proposals/jobs', 'POST'],
|
||||
['/projects/p1/novel-generation/plans/n1/ip-bible/jobs', 'POST'],
|
||||
['/projects/p1/novel-generation/plans/n1/chapters/2/run/jobs', 'POST'],
|
||||
['/projects/p1/novel-generation/plans/n1/chapters/batch/jobs', 'POST'],
|
||||
['/admin/hit-analyses', 'POST'],
|
||||
['/admin/hit-analyses/h1/analyze', 'POST'],
|
||||
['/admin/hit-analyses/h1/patterns', 'POST'],
|
||||
['/admin/providers/provider-1/test', 'POST'],
|
||||
['/projects/p1/story-bible/generate', 'POST'],
|
||||
['/projects/p1/characters/extract', 'POST'],
|
||||
['/characters/c1/anchor-video-test', 'POST'],
|
||||
['/characters/c1/generate-images', 'POST'],
|
||||
['/characters/c1/images/i1/review-visual', 'POST'],
|
||||
['/projects/p1/episodes/plan-request-preview', 'POST'],
|
||||
['/episodes/e1/script/request-preview', 'POST'],
|
||||
['/episodes/e1/script/generate', 'POST'],
|
||||
['/episodes/e1/audio/segments/2/retry', 'POST'],
|
||||
['/episodes/e1/live-action/shots/prepare', 'POST'],
|
||||
['/episodes/e1/live-action/video-clips/preflight?provider_code=kling', 'GET'],
|
||||
['/episodes/e1/live-action/video-clips/cost-estimate?provider_code=kling', 'GET'],
|
||||
['/live-action/video-clips/v1/quality-check', 'POST'],
|
||||
['/episodes/e1/live-action/render', 'POST']
|
||||
])('captures %s %s', (path, method) => {
|
||||
expect(isInspectableAiRequest(path, method)).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['/projects/p1', 'PATCH'],
|
||||
['/projects/p1/novel-generation/plans', 'POST'],
|
||||
['/projects/p1/novel-generation/plans/n1', 'PATCH'],
|
||||
['/projects/p1/story-bible/confirm', 'POST'],
|
||||
['/characters/c1/set-anchor', 'POST'],
|
||||
['/episodes/e1/live-action/shots/s1/prompt', 'POST'],
|
||||
['/live-action/video-clips/v1/manual-review', 'POST'],
|
||||
['/live-action/video-clips/v1/select-candidate', 'POST'],
|
||||
['/assets/a1', 'GET']
|
||||
])('ignores non-AI mutation %s %s', (path, method) => {
|
||||
expect(isInspectableAiRequest(path, method)).toBe(false);
|
||||
});
|
||||
|
||||
it('records path, query and body while redacting secrets', () => {
|
||||
clearAiRequestInspections();
|
||||
let latest: AiRequestInspectionRecord[] = [];
|
||||
const unsubscribe = subscribeAiRequestInspections((records) => { latest = records; });
|
||||
beginAiRequestInspection({
|
||||
path: '/episodes/e1/live-action/video-clips/preflight?provider_code=kling&image_id=1&image_id=2',
|
||||
method: 'GET',
|
||||
body: { prompt: '测试提示词', api_key: 'must-not-leak' }
|
||||
});
|
||||
|
||||
expect(latest[0]?.request_body).toEqual({
|
||||
path_params: '/episodes/e1/live-action/video-clips/preflight',
|
||||
query_params: { provider_code: 'kling', image_id: ['1', '2'] },
|
||||
body: { prompt: '测试提示词', api_key: '[已隐藏敏感字段]' }
|
||||
});
|
||||
unsubscribe();
|
||||
clearAiRequestInspections();
|
||||
});
|
||||
|
||||
it('keeps the complete response from request previews as execution evidence', () => {
|
||||
clearAiRequestInspections();
|
||||
let latest: AiRequestInspectionRecord[] = [];
|
||||
const unsubscribe = subscribeAiRequestInspections((records) => { latest = records; });
|
||||
const id = beginAiRequestInspection({
|
||||
path: '/episodes/e1/script/request-preview',
|
||||
method: 'POST',
|
||||
body: { provider_code: 'text-provider' }
|
||||
});
|
||||
completeAiRequestInspection(id, {
|
||||
requestId: 'req-1',
|
||||
responseData: { compiled_prompt: '完整剧本提示词', temperature: 0.4 }
|
||||
});
|
||||
|
||||
expect(latest[0]?.execution_evidence).toEqual({
|
||||
compiled_prompt: '完整剧本提示词',
|
||||
temperature: 0.4
|
||||
});
|
||||
unsubscribe();
|
||||
clearAiRequestInspections();
|
||||
});
|
||||
});
|
||||
+2542
-63
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+11138
-263
File diff suppressed because it is too large
Load Diff
+3315
-10
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@ export const projectStatusLabels: Record<string, string> = {
|
||||
storyboard_generating: '分镜生成',
|
||||
waiting_storyboard_confirm: '待确认分镜',
|
||||
storyboard_confirmed: '分镜已确认',
|
||||
waiting_external_assets: '等待外部素材',
|
||||
preview_images_generated: '预览图完成',
|
||||
final_images_generated: '正式图完成',
|
||||
audio_generated: '音频完成',
|
||||
@@ -45,7 +46,9 @@ export const taskTypeLabels: Record<string, string> = {
|
||||
novel_generate: '小说生成',
|
||||
novel_parse: '小说解析',
|
||||
story_bible_generate: '故事圣经',
|
||||
long_memory_generate: '长篇记忆',
|
||||
character_extract: '角色抽取',
|
||||
character_provider_element_create: '可灵角色元素创建',
|
||||
episode_plan_generate: '分集计划',
|
||||
script_generate: '单集脚本',
|
||||
storyboard_generate: '分镜脚本',
|
||||
@@ -98,6 +101,7 @@ const statusStepIndex: Record<string, number> = {
|
||||
storyboard_generating: 7,
|
||||
waiting_storyboard_confirm: 7,
|
||||
storyboard_confirmed: 8,
|
||||
waiting_external_assets: 8,
|
||||
character_image_generated: 8,
|
||||
preview_images_generated: 8,
|
||||
final_images_generated: 9,
|
||||
|
||||
Reference in New Issue
Block a user