feat: expand novel IP and production workflows

This commit is contained in:
www
2026-09-18 08:14:05 +02:00
parent b2ae4600b4
commit d9c81a3ac0
235 changed files with 117971 additions and 2721 deletions
@@ -0,0 +1,41 @@
CREATE TABLE `character_design_versions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`character_id` BIGINT NOT NULL,
`version_no` INTEGER NOT NULL,
`prompt_text` TEXT NULL,
`negative_prompt` TEXT NULL,
`image_asset_id` BIGINT NULL,
`notes` TEXT NULL,
`source` VARCHAR(50) NOT NULL DEFAULT 'workshop',
`is_final` BOOLEAN NOT NULL DEFAULT false,
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
UNIQUE INDEX `character_design_versions_character_id_version_no_key`(`character_id`, `version_no`),
INDEX `character_design_versions_project_id_character_id_idx`(`project_id`, `character_id`),
INDEX `character_design_versions_character_id_is_final_idx`(`character_id`, `is_final`),
INDEX `character_design_versions_image_asset_id_idx`(`image_asset_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `character_states` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`character_id` BIGINT NOT NULL,
`state_code` VARCHAR(80) NOT NULL,
`display_name` VARCHAR(120) NULL,
`description` TEXT NULL,
`wardrobe_rules` TEXT NULL,
`emotion_rules` TEXT NULL,
`prompt_suffix` TEXT NULL,
`negative_rules` TEXT NULL,
`reference_asset_id` BIGINT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `character_states_character_id_state_code_key`(`character_id`, `state_code`),
INDEX `character_states_project_id_character_id_idx`(`project_id`, `character_id`),
INDEX `character_states_status_idx`(`status`),
INDEX `character_states_reference_asset_id_idx`(`reference_asset_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,51 @@
INSERT INTO `provider_configs`
(`provider_type`, `provider_code`, `display_name`, `mode`, `model_name`, `config_json`, `is_enabled`, `priority`, `rate_limit_json`, `cost_rule_json`, `created_at`, `updated_at`)
VALUES
(
'VideoProvider',
'volcengine_seedance_20_fast',
'火山方舟 Seedance 2.0 Fast 图生视频',
'real',
'doubao-seedance-2.0-fast',
'{"driver":"configurable_image_to_video","error_prefix":"SEEDANCE_VIDEO","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","timeout_ms":300000,"create_endpoint":"/videos/generations","task_endpoint_template":"/videos/generations/{task_id}","poll_interval_ms":10000,"max_poll_attempts":150,"image_field":"image","prompt_field":"prompt","model_field":"model","duration_field":"duration","resolution_field":"resolution","aspect_ratio_field":"ratio","duration":5,"resolution":"720p","aspect_ratio":"9:16","extra_body_json":{"watermark":false,"audio":false},"supports_reference_image":true,"supports_start_end_frame":true,"supports_audio":true,"supports_lipsync":true,"supports_character_reference":true,"note":"默认禁用。Seedance 2.0 Fast 适合先做快速小样;模型 ID 和字段请以火山方舟控制台实际开通为准。"}',
false,
77,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0,"currency":"CNY","max_cost_per_call":0,"daily_cost_limit":0,"estimated_seconds":5,"note":"Seedance 2.0 Fast 价格按火山方舟实际账单回填;启用前请设置单次和每日成本上限。"}',
NOW(3),
NOW(3)
),
(
'VideoProvider',
'volcengine_seedance_20',
'火山方舟 Seedance 2.0 图生视频',
'real',
'doubao-seedance-2.0',
'{"driver":"configurable_image_to_video","error_prefix":"SEEDANCE_VIDEO","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","timeout_ms":300000,"create_endpoint":"/videos/generations","task_endpoint_template":"/videos/generations/{task_id}","poll_interval_ms":10000,"max_poll_attempts":180,"image_field":"image","prompt_field":"prompt","model_field":"model","duration_field":"duration","resolution_field":"resolution","aspect_ratio_field":"ratio","duration":5,"resolution":"720p","aspect_ratio":"9:16","extra_body_json":{"watermark":false,"audio":false},"supports_reference_image":true,"supports_start_end_frame":true,"supports_audio":true,"supports_lipsync":true,"supports_character_reference":true,"note":"默认禁用。质量优先的 Seedance 2.0 配置;正式调用前先用单镜头验证请求字段、返回结构和账单。"}',
false,
76,
'{"rpm":5,"concurrency":1,"retry_limit":2}',
'{"flat_cost":0,"unit":"video_seconds","price_per_second":0,"currency":"CNY","max_cost_per_call":0,"daily_cost_limit":0,"estimated_seconds":5,"note":"Seedance 2.0 价格按火山方舟实际账单回填;启用前请设置单次和每日成本上限。"}',
NOW(3),
NOW(3)
)
ON DUPLICATE KEY UPDATE
`display_name` = VALUES(`display_name`),
`mode` = VALUES(`mode`),
`model_name` = VALUES(`model_name`),
`priority` = VALUES(`priority`),
`rate_limit_json` = VALUES(`rate_limit_json`),
`updated_at` = NOW(3);
UPDATE `system_configs`
SET
`config_value` = JSON_SET(
`config_value`,
'$."live_action_video"."zh-CN"."normal"."fallback_chain"',
JSON_ARRAY('minimax_hailuo_23_fast', 'volcengine_seedance_20_fast', 'volcengine_seedance_20', 'jimeng_seedance', 'mock-video'),
'$."live_action_video"."zh-CN"."premium"."fallback_chain"',
JSON_ARRAY('kling-image-to-video', 'volcengine_seedance_20', 'minimax_hailuo_23_fast', 'volcengine_seedance_20_fast', 'jimeng_seedance', 'mock-video')
),
`updated_at` = NOW(3)
WHERE `config_key` = 'ai.router.v1'
AND JSON_TYPE(`config_value`) = 'OBJECT';
@@ -0,0 +1,105 @@
INSERT INTO `provider_configs`
(`provider_type`, `provider_code`, `display_name`, `mode`, `model_name`, `config_json`, `is_enabled`, `priority`, `rate_limit_json`, `cost_rule_json`, `created_at`, `updated_at`)
VALUES
(
'TextProvider',
'volcengine-doubao-seed20-pro-text',
'Volcengine Doubao Seed 2.0 Pro Text',
'real',
'doubao-seed-2-0-pro-260215',
'{"driver":"openai_compatible_chat","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","chat_endpoint":"/chat/completions","timeout_ms":120000,"max_tokens":3200,"temperature":0.7,"instructions":"你是中文短剧总编剧,负责高质量剧情策划、世界观、人设、分集大纲和可拍摄脚本。输出结构清晰、冲突强、方便继续生成分镜。"}',
false,
30,
'{"rpm":60,"concurrency":3}',
'{"flat_cost":0,"unit":"provider_usage_metadata","estimated_output_chars":1200,"note":"真实费用以后续账单或 usage 元数据核算;默认禁用,启用前请填写成本阈值。"}',
NOW(3),
NOW(3)
),
(
'TextProvider',
'volcengine-doubao-seed20-lite-text',
'Volcengine Doubao Seed 2.0 Lite Text',
'real',
'doubao-seed-2-0-lite-260215',
'{"driver":"openai_compatible_chat","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","chat_endpoint":"/chat/completions","timeout_ms":90000,"max_tokens":2400,"temperature":0.7,"instructions":"你是中文短剧生产助手,适合高频改写、分镜描述、标题钩子和运营文案。"}',
false,
30,
'{"rpm":60,"concurrency":3}',
'{"flat_cost":0,"unit":"provider_usage_metadata","estimated_output_chars":1200,"note":"真实费用以后续账单或 usage 元数据核算;默认禁用,启用前请填写成本阈值。"}',
NOW(3),
NOW(3)
),
(
'NovelProvider',
'volcengine-doubao-seed20-pro-novel',
'Volcengine Doubao Seed 2.0 Pro Novel',
'real',
'doubao-seed-2-0-pro-260215',
'{"driver":"openai_compatible_chat","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","chat_endpoint":"/chat/completions","timeout_ms":150000,"max_tokens":5000,"temperature":0.7,"instructions":"你是中文网文与短剧改编主笔,擅长长上下文改编、人物弧光、爽点节奏和连续剧集结构。"}',
false,
30,
'{"rpm":30,"concurrency":2}',
'{"flat_cost":0,"unit":"provider_usage_metadata","estimated_output_chars":1200,"note":"真实费用以后续账单或 usage 元数据核算;默认禁用,启用前请填写成本阈值。"}',
NOW(3),
NOW(3)
),
(
'ImageProvider',
'volcengine-seedream-50-image',
'Volcengine Seedream 5.0 Image',
'real',
'doubao-seedream-5-0-260128',
'{"driver":"configurable_image_generation","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","timeout_ms":600000,"create_endpoint":"/images/generations","size":"1440x2560","output_format":"png","response_format":"b64_json","aspect_ratio":"9:16","extra_body_json":{"watermark":false},"note":"默认禁用。用于角色定妆、场景概念图、分镜关键帧;启用前先单图验证返回格式和账单。"}',
false,
30,
'{"rpm":20,"concurrency":2}',
'{"flat_cost":0,"unit":"provider_usage_metadata","estimated_output_chars":800,"note":"图片费用按供应商账单核算;默认禁用,启用前请填写单次/每日成本阈值。"}',
NOW(3),
NOW(3)
),
(
'ImageProvider',
'volcengine-seedream-50-lite-image',
'Volcengine Seedream 5.0 Lite Image',
'real',
'doubao-seedream-5-0-lite-260128',
'{"driver":"configurable_image_generation","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","timeout_ms":600000,"create_endpoint":"/images/generations","size":"1440x2560","output_format":"png","response_format":"b64_json","aspect_ratio":"9:16","extra_body_json":{"watermark":false},"note":"默认禁用。用于低成本高频角色草图、分镜草图和运营配图。"}',
false,
30,
'{"rpm":20,"concurrency":2}',
'{"flat_cost":0,"unit":"provider_usage_metadata","estimated_output_chars":800,"note":"图片费用按供应商账单核算;默认禁用,启用前请填写单次/每日成本阈值。"}',
NOW(3),
NOW(3)
),
(
'ImageProvider',
'volcengine-seedream-45-image',
'Volcengine Seedream 4.5 Image',
'real',
'doubao-seedream-4-5-251128',
'{"driver":"configurable_image_generation","api_key_env":"VOLCENGINE_API_KEY","base_url":"https://ark.cn-beijing.volces.com/api/v3","timeout_ms":600000,"create_endpoint":"/images/generations","size":"1440x2560","output_format":"png","response_format":"b64_json","aspect_ratio":"9:16","extra_body_json":{"watermark":false},"note":"默认禁用。作为 Seedream 5.0 不可用时的图片生成 fallback。"}',
false,
30,
'{"rpm":20,"concurrency":2}',
'{"flat_cost":0,"unit":"provider_usage_metadata","estimated_output_chars":800,"note":"图片费用按供应商账单核算;默认禁用,启用前请填写单次/每日成本阈值。"}',
NOW(3),
NOW(3)
)
ON DUPLICATE KEY UPDATE
`display_name` = VALUES(`display_name`),
`mode` = VALUES(`mode`),
`model_name` = VALUES(`model_name`),
`priority` = VALUES(`priority`),
`rate_limit_json` = VALUES(`rate_limit_json`),
`updated_at` = NOW(3);
UPDATE `provider_configs`
SET
`model_name` = CASE
WHEN `provider_code` = 'volcengine_seedance_20' THEN 'doubao-seedance-2-0-260128'
WHEN `provider_code` = 'volcengine_seedance_20_fast' THEN 'doubao-seedance-2-0-fast-260128'
ELSE `model_name`
END,
`updated_at` = NOW(3)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` IN ('volcengine_seedance_20', 'volcengine_seedance_20_fast');
@@ -0,0 +1,164 @@
INSERT INTO `provider_configs`
(`provider_type`, `provider_code`, `display_name`, `mode`, `model_name`, `config_json`, `is_enabled`, `priority`, `rate_limit_json`, `cost_rule_json`, `created_at`, `updated_at`)
VALUES
(
'TextProvider',
'openai-gpt54-text',
'OpenAI GPT-5.4 Text',
'real',
'gpt-5.4',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":90000,"max_output_tokens":1800,"instructions":"你是中文漫剧创作助手,适合剧情策划、脚本润色、分镜提示词优化和复杂改写。"}',
false,
58,
'{"rpm":60,"concurrency":4}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'TextProvider',
'openai-gpt54-mini-text',
'OpenAI GPT-5.4 Mini Text',
'real',
'gpt-5.4-mini',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":60000,"max_output_tokens":1600,"instructions":"你是中文短剧生产助手,适合高频、低延迟的分镜草稿、标题、简介和运营文案。"}',
false,
57,
'{"rpm":60,"concurrency":4}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'TextProvider',
'openai-gpt54-nano-text',
'OpenAI GPT-5.4 Nano Text',
'real',
'gpt-5.4-nano',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":45000,"max_output_tokens":1200,"instructions":"你是中文短剧轻量助手,适合分类、短提示词改写、标题和低成本批处理。"}',
false,
56,
'{"rpm":60,"concurrency":4}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'TextProvider',
'openai-gpt5-text',
'OpenAI GPT-5 Text',
'real',
'gpt-5',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":90000,"max_output_tokens":1800,"instructions":"你是中文漫剧创作助手,用于和 GPT-5.4/5.5 做质量、成本、稳定性对比。"}',
false,
55,
'{"rpm":60,"concurrency":4}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'TextProvider',
'openai-gpt41-text',
'OpenAI GPT-4.1 Text',
'real',
'gpt-4.1',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":60000,"max_output_tokens":1600,"instructions":"你是中文漫剧创作助手,用于非推理模型基线测试、脚本润色和结构化输出。"}',
false,
54,
'{"rpm":60,"concurrency":4}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'TextProvider',
'openai-gpt41-mini-text',
'OpenAI GPT-4.1 Mini Text',
'real',
'gpt-4.1-mini',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":45000,"max_output_tokens":1200,"instructions":"你是中文短剧轻量助手,用于低成本文案、分镜草稿和结构化摘要。"}',
false,
53,
'{"rpm":60,"concurrency":4}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'NovelProvider',
'openai-gpt54-novel',
'OpenAI GPT-5.4 Novel',
'real',
'gpt-5.4',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":150000,"max_output_tokens":5000,"instructions":"你是中文网文和短剧改编主笔,擅长长上下文改编、分集大纲、角色弧光和连续剧结构。"}',
false,
58,
'{"rpm":30,"concurrency":2}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'NovelProvider',
'openai-gpt54-mini-novel',
'OpenAI GPT-5.4 Mini Novel',
'real',
'gpt-5.4-mini',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":120000,"max_output_tokens":3600,"instructions":"你是中文网文改编助手,适合低成本生成章节摘要、分集计划和脚本草稿。"}',
false,
57,
'{"rpm":30,"concurrency":2}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'NovelProvider',
'openai-gpt5-novel',
'OpenAI GPT-5 Novel',
'real',
'gpt-5',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":150000,"max_output_tokens":4200,"instructions":"你是中文网文和短剧改编主笔,用于和 GPT-5.4/5.5 做长文改编对比。"}',
false,
55,
'{"rpm":30,"concurrency":2}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'NovelProvider',
'openai-gpt41-novel',
'OpenAI GPT-4.1 Novel',
'real',
'gpt-4.1',
'{"driver":"openai_responses","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":120000,"max_output_tokens":3200,"instructions":"你是中文网文改编助手,用于非推理模型基线测试和长文本结构化整理。"}',
false,
54,
'{"rpm":30,"concurrency":2}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"真实费用以后续账单或 usage 元数据核算;默认禁用,用于后台多模型测试。"}',
NOW(3),
NOW(3)
),
(
'VideoProvider',
'openai-sora-2-pro-video',
'OpenAI Sora 2 Pro Video',
'real',
'sora-2-pro',
'{"driver":"openai_video_generation","api_key_env":"OPENAI_API_KEY","base_url":"https://api.openai.com/v1","base_url_env":"OPENAI_BASE_URL","timeout_ms":60000,"create_endpoint":"/videos","status_endpoint_template":"/videos/{video_id}","content_endpoint_template":"/videos/{video_id}/content","poll_interval_ms":10000,"max_poll_attempts":90,"size":"1280x720","seconds":"8"}',
false,
45,
'{"rpm":3,"concurrency":1}',
'{"flat_cost":0,"unit":"provider_usage_metadata","note":"Sora Pro 费用较高;默认禁用,启用前必须设置单次和每日成本上限。"}',
NOW(3),
NOW(3)
)
ON DUPLICATE KEY UPDATE
`display_name` = VALUES(`display_name`),
`mode` = VALUES(`mode`),
`model_name` = VALUES(`model_name`),
`priority` = VALUES(`priority`),
`rate_limit_json` = VALUES(`rate_limit_json`),
`updated_at` = NOW(3);
@@ -0,0 +1,22 @@
UPDATE `provider_configs`
SET `config_json` = JSON_SET(
`config_json`,
'$.body_style', 'volcengine_content_generation',
'$.create_endpoint', '/contents/generations/tasks',
'$.task_endpoint_template', '/contents/generations/tasks/{task_id}'
)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` IN ('volcengine_seedance_20_fast', 'volcengine_seedance_20', 'jimeng_seedance');
UPDATE `provider_configs`
SET `config_json` = JSON_REMOVE(
`config_json`,
'$.image_field',
'$.prompt_field',
'$.model_field',
'$.duration_field',
'$.resolution_field',
'$.aspect_ratio_field'
)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` IN ('volcengine_seedance_20_fast', 'volcengine_seedance_20', 'jimeng_seedance');
@@ -0,0 +1,11 @@
UPDATE `provider_configs`
SET `cost_rule_json` = JSON_SET(
`cost_rule_json`,
'$.price_per_second', 0.9936,
'$.currency', 'CNY',
'$.max_cost_per_call', 20,
'$.daily_cost_limit', 500,
'$.note', '按火山方舟公开价格估算:720p、24fps、无视频输入约 46 元/百万 token,即约 0.9936 元/秒;最终以账单为准。'
)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` IN ('volcengine_seedance_20_fast', 'volcengine_seedance_20');
@@ -0,0 +1,10 @@
UPDATE `provider_configs`
SET
`config_json` = JSON_SET(COALESCE(`config_json`, JSON_OBJECT()), '$.size', '1440x2560'),
`updated_at` = NOW(3)
WHERE `provider_type` = 'ImageProvider'
AND `provider_code` IN (
'volcengine-seedream-50-image',
'volcengine-seedream-50-lite-image',
'volcengine-seedream-45-image'
);
@@ -0,0 +1,10 @@
UPDATE `provider_configs`
SET
`config_json` = JSON_SET(COALESCE(`config_json`, JSON_OBJECT()), '$.timeout_ms', 600000),
`updated_at` = NOW(3)
WHERE `provider_type` = 'ImageProvider'
AND `provider_code` IN (
'volcengine-seedream-50-image',
'volcengine-seedream-50-lite-image',
'volcengine-seedream-45-image'
);
@@ -0,0 +1,6 @@
ALTER TABLE `novel_chapters`
ADD COLUMN `volume_no` INT NULL AFTER `novel_source_id`,
ADD COLUMN `volume_title` VARCHAR(255) NULL AFTER `volume_no`;
CREATE INDEX `novel_chapters_novel_source_id_volume_no_idx`
ON `novel_chapters`(`novel_source_id`, `volume_no`);
@@ -0,0 +1,44 @@
CREATE TABLE `novel_reading_progress` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NOT NULL,
`chapter_id` BIGINT NOT NULL,
`page_index` INT NOT NULL DEFAULT 0,
`page_count` INT NOT NULL DEFAULT 1,
`progress_percent` DECIMAL(6, 3) NULL,
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `novel_reading_progress_user_id_novel_source_id_key` (`user_id`, `novel_source_id`),
KEY `novel_reading_progress_user_id_updated_at_idx` (`user_id`, `updated_at`),
KEY `novel_reading_progress_novel_source_id_idx` (`novel_source_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `novel_bookmarks` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NOT NULL,
`chapter_id` BIGINT NOT NULL,
`page_index` INT NOT NULL DEFAULT 0,
`title` VARCHAR(255) NULL,
`note_text` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `novel_bookmarks_user_id_novel_source_id_created_at_idx` (`user_id`, `novel_source_id`, `created_at`),
KEY `novel_bookmarks_chapter_id_idx` (`chapter_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `novel_annotations` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NOT NULL,
`chapter_id` BIGINT NOT NULL,
`page_index` INT NOT NULL DEFAULT 0,
`selected_text` TEXT NOT NULL,
`note_text` TEXT NULL,
`color` VARCHAR(30) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `novel_annotations_user_id_novel_source_id_created_at_idx` (`user_id`, `novel_source_id`, `created_at`),
KEY `novel_annotations_chapter_id_idx` (`chapter_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,187 @@
CREATE TABLE `novel_generation_plans` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NULL,
`user_id` BIGINT NOT NULL,
`novel_scale` VARCHAR(30) NOT NULL DEFAULT 'medium',
`target_words` INT NULL,
`target_chapters` INT NULL,
`genre` VARCHAR(100) NULL,
`style_code` VARCHAR(100) NULL,
`brief_json` JSON NULL,
`ip_bible_json` JSON NULL,
`volume_plan_json` JSON NULL,
`pipeline_config_json` JSON NULL,
`quality_threshold_json` JSON NULL,
`automation_level` VARCHAR(20) NOT NULL DEFAULT 'L1',
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`current_chapter_no` INT NOT NULL DEFAULT 0,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `npg_project_status_idx` (`project_id`, `status`),
KEY `npg_source_idx` (`novel_source_id`),
KEY `npg_user_status_idx` (`user_id`, `status`),
KEY `npg_scale_level_idx` (`novel_scale`, `automation_level`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `novel_chapter_versions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NULL,
`novel_chapter_id` BIGINT NULL,
`chapter_no` INT NOT NULL,
`version_no` INT NOT NULL,
`version_type` VARCHAR(50) NOT NULL,
`title` VARCHAR(255) NULL,
`content_text` LONGTEXT NULL,
`content_json` JSON NULL,
`source_agent` VARCHAR(100) NULL,
`provider_code` VARCHAR(100) NULL,
`model_name` VARCHAR(100) NULL,
`quality_score` DECIMAL(5, 2) NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `ncv_source_chapter_version_type_key` (`novel_source_id`, `chapter_no`, `version_no`, `version_type`),
KEY `ncv_project_chapter_idx` (`project_id`, `chapter_no`),
KEY `ncv_chapter_idx` (`novel_chapter_id`),
KEY `ncv_agent_idx` (`source_agent`),
KEY `ncv_status_idx` (`status`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `novel_context_memories` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NULL,
`chapter_no` INT NULL,
`memory_type` VARCHAR(80) NOT NULL,
`memory_text` LONGTEXT NULL,
`memory_json` JSON NULL,
`importance_level` INT NOT NULL DEFAULT 0,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `ncm_project_type_idx` (`project_id`, `memory_type`),
KEY `ncm_source_chapter_idx` (`novel_source_id`, `chapter_no`),
KEY `ncm_type_status_idx` (`memory_type`, `status`),
KEY `ncm_importance_idx` (`importance_level`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `novel_quality_reports` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NULL,
`novel_chapter_id` BIGINT NULL,
`chapter_no` INT NULL,
`report_type` VARCHAR(50) NOT NULL,
`total_score` DECIMAL(5, 2) NULL,
`score_json` JSON NULL,
`problems_json` JSON NULL,
`suggestions_json` JSON NULL,
`pass_status` BOOLEAN NOT NULL DEFAULT false,
`rewrite_required` BOOLEAN NOT NULL DEFAULT false,
`source_agent` VARCHAR(100) NULL,
`provider_code` VARCHAR(100) NULL,
`model_name` VARCHAR(100) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `nqr_project_chapter_idx` (`project_id`, `chapter_no`),
KEY `nqr_source_chapter_idx` (`novel_source_id`, `chapter_no`),
KEY `nqr_chapter_idx` (`novel_chapter_id`),
KEY `nqr_pass_idx` (`pass_status`),
KEY `nqr_agent_idx` (`source_agent`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `novel_version_snapshots` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NOT NULL,
`version_no` INT NOT NULL,
`title` VARCHAR(255) NULL,
`snapshot_scope` VARCHAR(50) NOT NULL,
`chapter_start` INT NULL,
`chapter_end` INT NULL,
`source_hash` VARCHAR(128) NULL,
`snapshot_text` LONGTEXT NULL,
`snapshot_json` JSON NULL,
`created_for` VARCHAR(50) NULL,
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `nvs_source_version_key` (`novel_source_id`, `version_no`),
KEY `nvs_project_idx` (`project_id`),
KEY `nvs_for_idx` (`created_for`),
KEY `nvs_range_idx` (`chapter_start`, `chapter_end`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `novel_derivative_jobs` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NOT NULL,
`snapshot_id` BIGINT NULL,
`user_id` BIGINT NOT NULL,
`derivative_type` VARCHAR(50) NOT NULL,
`target_project_id` BIGINT NULL,
`target_ref_id` BIGINT NULL,
`chapter_start` INT NULL,
`chapter_end` INT NULL,
`config_json` JSON NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`progress_json` JSON NULL,
`error_message` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `ndj_project_type_idx` (`project_id`, `derivative_type`),
KEY `ndj_source_status_idx` (`novel_source_id`, `status`),
KEY `ndj_snapshot_idx` (`snapshot_id`),
KEY `ndj_user_status_idx` (`user_id`, `status`),
KEY `ndj_target_project_idx` (`target_project_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `agent_prompts` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`agent_name` VARCHAR(100) NOT NULL,
`version` INT NOT NULL DEFAULT 1,
`provider_type` VARCHAR(80) NOT NULL DEFAULT 'NovelProvider',
`default_provider_code` VARCHAR(100) NULL,
`system_prompt` LONGTEXT NOT NULL,
`user_prompt_template` LONGTEXT NOT NULL,
`output_schema_json` JSON NULL,
`temperature` DECIMAL(4, 2) NULL,
`max_output_tokens` INT NULL,
`is_active` BOOLEAN NOT NULL DEFAULT true,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `ap_name_version_key` (`agent_name`, `version`),
KEY `ap_name_active_idx` (`agent_name`, `is_active`),
KEY `ap_provider_type_idx` (`provider_type`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `agent_runs` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NULL,
`novel_source_id` BIGINT NULL,
`chapter_no` INT NULL,
`agent_name` VARCHAR(100) NOT NULL,
`prompt_version` INT NULL,
`provider_code` VARCHAR(100) NULL,
`model_name` VARCHAR(100) NULL,
`provider_log_id` BIGINT NULL,
`input_json` JSON NULL,
`output_json` JSON NULL,
`output_text` LONGTEXT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'running',
`quality_score` DECIMAL(5, 2) NULL,
`error_code` VARCHAR(100) NULL,
`error_message` TEXT NULL,
`started_at` DATETIME(3) NULL,
`finished_at` DATETIME(3) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `ar_project_agent_idx` (`project_id`, `agent_name`),
KEY `ar_source_chapter_idx` (`novel_source_id`, `chapter_no`),
KEY `ar_status_created_idx` (`status`, `created_at`),
KEY `ar_provider_log_idx` (`provider_log_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,13 @@
ALTER TABLE `novel_sources`
ADD COLUMN `ai_provider_code` VARCHAR(100) NULL,
ADD COLUMN `ai_model_name` VARCHAR(160) NULL,
ADD COLUMN `ai_cost_estimate` DECIMAL(12, 4) NULL,
ADD COLUMN `ai_cost_actual` DECIMAL(12, 4) NULL,
ADD INDEX `ns_ai_provider_idx` (`ai_provider_code`);
ALTER TABLE `novel_chapters`
ADD COLUMN `ai_provider_code` VARCHAR(100) NULL,
ADD COLUMN `ai_model_name` VARCHAR(160) NULL,
ADD COLUMN `ai_cost_estimate` DECIMAL(12, 4) NULL,
ADD COLUMN `ai_cost_actual` DECIMAL(12, 4) NULL,
ADD INDEX `nc_ai_provider_idx` (`ai_provider_code`);
@@ -0,0 +1,7 @@
ALTER TABLE `novel_sources`
ADD COLUMN `design_json` JSON NULL AFTER `parse_report`,
ADD COLUMN `volume_plan_json` JSON NULL AFTER `design_json`;
ALTER TABLE `novel_chapters`
ADD COLUMN `outline_json` JSON NULL AFTER `visual_summary`,
ADD COLUMN `analysis_json` JSON NULL AFTER `outline_json`;
@@ -0,0 +1,2 @@
ALTER TABLE `story_bibles`
MODIFY `tone` TEXT NULL;
@@ -0,0 +1,3 @@
ALTER TABLE `story_bibles`
ADD COLUMN `production_bible_json` JSON NULL,
ADD COLUMN `quality_report_json` JSON NULL;
@@ -0,0 +1,35 @@
CREATE TABLE `character_extraction_versions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`story_bible_id` BIGINT NULL,
`provider_log_id` BIGINT NULL,
`source_type` VARCHAR(50) NOT NULL,
`source_label` VARCHAR(120) NULL,
`prompt_category` VARCHAR(100) NULL,
`prompt_version` VARCHAR(80) NULL,
`prompt_text` LONGTEXT NULL,
`provider_code` VARCHAR(100) NULL,
`model_name` VARCHAR(100) NULL,
`raw_text` LONGTEXT NULL,
`role_pool_plan_json` JSON NULL,
`characters_json` JSON NULL,
`quality_score` INT NULL,
`review_comment` TEXT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE INDEX `character_extraction_versions_project_id_status_idx`
ON `character_extraction_versions`(`project_id`, `status`);
CREATE INDEX `character_extraction_versions_story_bible_id_idx`
ON `character_extraction_versions`(`story_bible_id`);
CREATE INDEX `character_extraction_versions_provider_log_id_idx`
ON `character_extraction_versions`(`provider_log_id`);
CREATE INDEX `character_extraction_versions_source_type_idx`
ON `character_extraction_versions`(`source_type`);
@@ -0,0 +1,58 @@
ALTER TABLE `characters`
ADD COLUMN `global_character_look_version_id` BIGINT NULL,
ADD COLUMN `global_character_asset_id` BIGINT NULL,
ADD COLUMN `inherit_voice_from_global` BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN `inherit_digital_human_from_global` BOOLEAN NOT NULL DEFAULT false,
ADD INDEX `characters_global_character_look_version_id_idx` (`global_character_look_version_id`),
ADD INDEX `characters_global_character_asset_id_idx` (`global_character_asset_id`);
CREATE TABLE `global_character_look_versions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`global_character_id` BIGINT NOT NULL,
`version_name` VARCHAR(160) NOT NULL,
`style_type` VARCHAR(80) NULL,
`appearance_desc` TEXT NULL,
`hair_desc` TEXT NULL,
`makeup_desc` TEXT NULL,
`body_desc` TEXT NULL,
`costume_rules` TEXT NULL,
`color_palette` TEXT NULL,
`key_props` TEXT NULL,
`negative_rules` TEXT NULL,
`main_anchor_asset_id` BIGINT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'candidate',
`quality_score` DECIMAL(5,2) NULL,
`reviewer_comment` TEXT NULL,
`source_project_id` BIGINT NULL,
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
INDEX `global_character_look_versions_global_character_id_status_idx` (`global_character_id`, `status`),
INDEX `global_character_look_versions_main_anchor_asset_id_idx` (`main_anchor_asset_id`),
INDEX `global_character_look_versions_source_project_id_idx` (`source_project_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `global_character_assets`
ADD COLUMN `look_version_id` BIGINT NULL,
ADD COLUMN `source_type` VARCHAR(50) NOT NULL DEFAULT 'upload',
ADD COLUMN `source_label` VARCHAR(160) NULL,
ADD COLUMN `negative_prompt` TEXT NULL,
ADD COLUMN `model_name` VARCHAR(160) NULL,
ADD COLUMN `seed` VARCHAR(100) NULL,
ADD COLUMN `resolution` VARCHAR(50) NULL,
ADD COLUMN `aspect_ratio` VARCHAR(50) NULL,
ADD COLUMN `cost_estimate` DECIMAL(12,4) NULL,
ADD COLUMN `cost_actual` DECIMAL(12,4) NULL,
ADD COLUMN `quality_score` DECIMAL(5,2) NULL,
ADD COLUMN `consistency_score` DECIMAL(5,2) NULL,
ADD COLUMN `face_similarity_score` DECIMAL(5,2) NULL,
ADD COLUMN `license_status` VARCHAR(50) NOT NULL DEFAULT 'internal_test',
ADD COLUMN `commercial_allowed` BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN `review_status` VARCHAR(50) NOT NULL DEFAULT 'pending',
ADD COLUMN `reviewer_note` TEXT NULL,
ADD COLUMN `usage_count` INT NOT NULL DEFAULT 0,
ADD COLUMN `metadata_json` JSON NULL,
ADD INDEX `global_character_assets_look_version_id_idx` (`look_version_id`),
ADD INDEX `global_character_assets_source_type_idx` (`source_type`),
ADD INDEX `global_character_assets_review_status_idx` (`review_status`);
@@ -0,0 +1,100 @@
ALTER TABLE `characters`
ADD COLUMN `story_character_id` BIGINT NULL,
ADD INDEX `characters_story_character_id_idx` (`story_character_id`);
CREATE TABLE `story_characters` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NULL,
`story_bible_id` BIGINT NULL,
`global_character_id` BIGINT NULL,
`name` VARCHAR(100) NOT NULL,
`alias_names` JSON NULL,
`role_type` VARCHAR(50) NOT NULL DEFAULT 'supporting',
`identity_desc` TEXT NULL,
`background_desc` TEXT NULL,
`personality_desc` TEXT NULL,
`relationship_json` JSON NULL,
`character_arc` TEXT NULL,
`world_role_desc` TEXT NULL,
`negative_rules` TEXT NULL,
`source_type` VARCHAR(50) NOT NULL DEFAULT 'character_extraction',
`source_version_id` BIGINT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
INDEX `story_characters_project_id_status_idx` (`project_id`, `status`),
INDEX `story_characters_novel_source_id_idx` (`novel_source_id`),
INDEX `story_characters_story_bible_id_idx` (`story_bible_id`),
INDEX `story_characters_global_character_id_idx` (`global_character_id`),
INDEX `story_characters_source_type_idx` (`source_type`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `video_clips`
ADD COLUMN `engine_version` VARCHAR(80) NULL,
ADD COLUMN `asset_lock_json` JSON NULL,
ADD COLUMN `motion_control_json` JSON NULL,
ADD COLUMN `camera_control_json` JSON NULL,
ADD COLUMN `composer_usage_json` JSON NULL,
ADD INDEX `video_clips_engine_version_idx` (`engine_version`);
CREATE TABLE `project_pipeline_configs` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`ip_isolation_enabled` BOOLEAN NOT NULL DEFAULT true,
`video_engine_enabled` BOOLEAN NOT NULL DEFAULT true,
`scene_composer_enabled` BOOLEAN NOT NULL DEFAULT false,
`default_generation_mode` VARCHAR(50) NOT NULL DEFAULT 'single_model',
`video_engine_version` VARCHAR(80) NOT NULL DEFAULT 'video_engine_v1',
`scene_composer_version` VARCHAR(80) NOT NULL DEFAULT 'scene_composer_v1_reserved',
`ip_rules_json` JSON NULL,
`video_engine_config_json` JSON NULL,
`scene_composer_config_json` JSON NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE INDEX `project_pipeline_configs_project_id_key` (`project_id`),
INDEX `project_pipeline_configs_scene_composer_enabled_idx` (`scene_composer_enabled`),
INDEX `project_pipeline_configs_default_generation_mode_idx` (`default_generation_mode`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
INSERT INTO `project_pipeline_configs` (
`project_id`,
`ip_isolation_enabled`,
`video_engine_enabled`,
`scene_composer_enabled`,
`default_generation_mode`,
`video_engine_version`,
`scene_composer_version`,
`ip_rules_json`,
`video_engine_config_json`,
`scene_composer_config_json`
)
SELECT
`id`,
true,
true,
false,
'single_model',
'video_engine_v1',
'scene_composer_v1_reserved',
JSON_OBJECT(
'character_ip_policy', 'clean_reusable_assets_only',
'story_character_policy', 'story_bible_and_relationships_only',
'project_character_policy', 'project_look_anchor_scene_state_only',
'forbidden_ip_fields', JSON_ARRAY('episode_plot', 'shot_state', 'project_costume', 'scene_action')
),
JSON_OBJECT(
'mode', 'single_model_first',
'candidate_count_default', 1,
'multi_model_compare_default_enabled', false,
'requires_human_clip_selection', true
),
JSON_OBJECT(
'enabled', false,
'reserved_stage', 'phase_5',
'notes', 'Scene Composer is recorded for future automatic ordering/emotion/transition logic. Current production keeps human clip selection and FFmpeg render.'
)
FROM `projects`
ON DUPLICATE KEY UPDATE `updated_at` = CURRENT_TIMESTAMP(3);
@@ -0,0 +1,34 @@
CREATE TABLE `character_prompt_versions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NULL,
`character_id` BIGINT NULL,
`global_character_id` BIGINT NULL,
`look_version_id` BIGINT NULL,
`version_no` INTEGER NOT NULL,
`layer_code` VARCHAR(80) NOT NULL DEFAULT 'main_anchor',
`channel` VARCHAR(80) NOT NULL DEFAULT 'chatgpt_web',
`title` VARCHAR(160) NULL,
`source_type` VARCHAR(80) NOT NULL DEFAULT 'system_refresh',
`source_label` VARCHAR(160) NULL,
`prompt_engine_version` VARCHAR(80) NULL,
`prompt_text` LONGTEXT NOT NULL,
`negative_prompt` LONGTEXT NULL,
`model_name` VARCHAR(160) NULL,
`usage_note` TEXT NULL,
`quality_score` DECIMAL(5, 2) NULL,
`review_comment` TEXT NULL,
`is_active` BOOLEAN NOT NULL DEFAULT false,
`metadata_json` JSON NULL,
`created_by_user_id` BIGINT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE INDEX `cpv_character_version_key` (`character_id`, `version_no`),
UNIQUE INDEX `cpv_global_version_key` (`global_character_id`, `version_no`),
INDEX `cpv_project_idx` (`project_id`),
INDEX `cpv_character_layer_channel_idx` (`character_id`, `layer_code`, `channel`),
INDEX `cpv_global_layer_channel_idx` (`global_character_id`, `layer_code`, `channel`),
INDEX `cpv_look_version_idx` (`look_version_id`),
INDEX `cpv_active_status_idx` (`is_active`, `status`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,60 @@
CREATE TABLE `character_prompt_reviews` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`task_id` BIGINT NULL,
`character_id` BIGINT NULL,
`global_character_id` BIGINT NULL,
`prompt_version_id` BIGINT NULL,
`layer_code` VARCHAR(80) NOT NULL,
`image_type` VARCHAR(80) NULL,
`review_round` INTEGER NOT NULL DEFAULT 1,
`review_mode` VARCHAR(80) NOT NULL DEFAULT 'api_preflight',
`prompt_engine_version` VARCHAR(80) NULL,
`threshold_score` DECIMAL(5, 2) NOT NULL DEFAULT 92.00,
`score` DECIMAL(5, 2) NULL,
`passed` BOOLEAN NOT NULL DEFAULT false,
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`source_prompt` LONGTEXT NULL,
`optimized_prompt` LONGTEXT NULL,
`negative_prompt` LONGTEXT NULL,
`issues_json` JSON NULL,
`suggestions_json` JSON NULL,
`reusable_rules_json` JSON NULL,
`quality_gate_json` JSON NULL,
`provider_log_id` BIGINT NULL,
`provider_code` VARCHAR(100) NULL,
`model_name` VARCHAR(160) NULL,
`raw_text` LONGTEXT NULL,
`error_message` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
INDEX `cpr_project_created_idx` (`project_id`, `created_at`),
INDEX `cpr_task_idx` (`task_id`),
INDEX `cpr_character_layer_idx` (`character_id`, `layer_code`),
INDEX `cpr_global_layer_idx` (`global_character_id`, `layer_code`),
INDEX `cpr_prompt_version_idx` (`prompt_version_id`),
INDEX `cpr_layer_passed_idx` (`layer_code`, `passed`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `character_prompt_optimization_lessons` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`scope_type` VARCHAR(50) NOT NULL DEFAULT 'global',
`layer_code` VARCHAR(80) NOT NULL,
`lesson_type` VARCHAR(80) NOT NULL DEFAULT 'prompt_rule',
`title` VARCHAR(160) NULL,
`rule_text` TEXT NOT NULL,
`prompt_engine_version` VARCHAR(80) NULL,
`source_review_id` BIGINT NULL,
`source_character_id` BIGINT NULL,
`source_prompt_version_id` BIGINT NULL,
`quality_score` DECIMAL(5, 2) NULL,
`usage_count` INTEGER NOT NULL DEFAULT 0,
`metadata_json` JSON NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
INDEX `cpol_scope_layer_status_idx` (`scope_type`, `layer_code`, `status`),
INDEX `cpol_review_idx` (`source_review_id`),
INDEX `cpol_character_idx` (`source_character_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,48 @@
CREATE TABLE `user_model_preferences` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`scope_key` VARCHAR(120) NOT NULL DEFAULT 'global',
`project_id` BIGINT NULL,
`preference_key` VARCHAR(120) NOT NULL,
`text_provider_code` VARCHAR(100) NULL,
`image_provider_code` VARCHAR(100) NULL,
`video_provider_code` VARCHAR(100) NULL,
`voice_provider_code` VARCHAR(100) NULL,
`metadata_json` JSON NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE INDEX `ump_user_scope_key_unique` (`user_id`, `scope_key`, `preference_key`),
INDEX `ump_user_scope_idx` (`user_id`, `scope_key`),
INDEX `ump_project_idx` (`project_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `project_visual_assets` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`user_id` BIGINT NOT NULL,
`asset_id` BIGINT NULL,
`asset_kind` VARCHAR(50) NOT NULL,
`asset_type` VARCHAR(80) NOT NULL,
`name` VARCHAR(160) NOT NULL,
`label` VARCHAR(160) NULL,
`ownership_type` VARCHAR(50) NOT NULL DEFAULT 'shared_story',
`owner_character_id` BIGINT NULL,
`source_type` VARCHAR(80) NOT NULL DEFAULT 'manual',
`source_label` VARCHAR(160) NULL,
`prompt_text` LONGTEXT NULL,
`negative_prompt` LONGTEXT NULL,
`usage_tags_json` JSON NULL,
`quality_score` DECIMAL(5, 2) NULL,
`is_primary` BOOLEAN NOT NULL DEFAULT false,
`metadata_json` JSON NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'active',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
INDEX `pva_project_kind_status_idx` (`project_id`, `asset_kind`, `status`),
INDEX `pva_project_type_idx` (`project_id`, `asset_type`),
INDEX `pva_owner_character_idx` (`owner_character_id`),
INDEX `pva_asset_idx` (`asset_id`),
INDEX `pva_user_kind_idx` (`user_id`, `asset_kind`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,20 @@
ALTER TABLE `project_visual_assets`
ADD COLUMN `aliases_json` JSON NULL,
ADD COLUMN `allowed_roles_json` JSON NULL,
ADD COLUMN `detected_from` VARCHAR(160) NULL,
ADD COLUMN `importance` INT NOT NULL DEFAULT 50,
ADD COLUMN `visual_lock` LONGTEXT NULL,
ADD COLUMN `key_objects_json` JSON NULL,
ADD COLUMN `prompt_block` LONGTEXT NULL,
ADD COLUMN `anchor_prompt` LONGTEXT NULL,
ADD COLUMN `reference_images_json` JSON NULL,
ADD COLUMN `anchor_images_json` JSON NULL,
ADD COLUMN `render_variants_json` JSON NULL,
ADD COLUMN `linked_assets_json` JSON NULL,
ADD COLUMN `reuse_rule` LONGTEXT NULL,
ADD COLUMN `story_scope` VARCHAR(120) NULL,
ADD COLUMN `version_note` LONGTEXT NULL,
ADD COLUMN `notes` LONGTEXT NULL;
CREATE INDEX `pva_project_ownership_idx` ON `project_visual_assets`(`project_id`, `ownership_type`);
CREATE INDEX `pva_project_status_importance_idx` ON `project_visual_assets`(`project_id`, `status`, `importance`);
@@ -0,0 +1,7 @@
ALTER TABLE `assets`
ADD COLUMN `display_name` VARCHAR(255) NULL,
ADD COLUMN `selection_status` VARCHAR(30) NOT NULL DEFAULT 'candidate',
ADD COLUMN `selection_note` TEXT NULL,
ADD COLUMN `metadata_json` JSON NULL;
CREATE INDEX `assets_project_id_selection_status_idx` ON `assets`(`project_id`, `selection_status`);
@@ -0,0 +1,4 @@
ALTER TABLE `storyboard_shots`
ADD COLUMN `transition_to_next` VARCHAR(50) NULL,
ADD COLUMN `transition_to_next_duration` DECIMAL(5, 2) NULL,
ADD COLUMN `transition_to_next_reason` TEXT NULL;
@@ -0,0 +1,42 @@
ALTER TABLE `character_images`
ADD COLUMN `prompt_quality_score` DECIMAL(5, 2) NULL AFTER `is_anchor`;
UPDATE `character_images`
SET `prompt_quality_score` = `quality_score`,
`quality_score` = NULL
WHERE `quality_score` IS NOT NULL;
CREATE TABLE `character_image_quality_reviews` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`character_id` BIGINT NOT NULL,
`character_image_id` BIGINT NOT NULL,
`asset_id` BIGINT NOT NULL,
`image_type` VARCHAR(80) NOT NULL,
`review_round` INTEGER NOT NULL DEFAULT 1,
`review_mode` VARCHAR(80) NOT NULL DEFAULT 'post_generation_visual',
`threshold_score` DECIMAL(5, 2) NOT NULL DEFAULT 96.00,
`score` DECIMAL(5, 2) NULL,
`grade` VARCHAR(20) NULL,
`passed` BOOLEAN NOT NULL DEFAULT false,
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
`dimensions_json` JSON NULL,
`strengths_json` JSON NULL,
`issues_json` JSON NULL,
`improvement_rules_json` JSON NULL,
`character_observations_json` JSON NULL,
`quality_gate_json` JSON NULL,
`provider_log_id` BIGINT NULL,
`provider_code` VARCHAR(100) NULL,
`model_name` VARCHAR(160) NULL,
`raw_text` LONGTEXT NULL,
`error_message` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX `ciqr_project_created_idx` (`project_id`, `created_at`),
INDEX `ciqr_character_type_idx` (`character_id`, `image_type`),
INDEX `ciqr_image_round_idx` (`character_image_id`, `review_round`),
INDEX `ciqr_asset_idx` (`asset_id`),
INDEX `ciqr_type_passed_idx` (`image_type`, `passed`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,79 @@
ALTER TABLE `projects`
ADD COLUMN `engine_version` VARCHAR(50) NOT NULL DEFAULT 'legacy_v1',
ADD COLUMN `production_lifecycle` VARCHAR(80) NOT NULL DEFAULT 'legacy_snapshot';
CREATE INDEX `project_engine_lifecycle_idx`
ON `projects`(`engine_version`, `production_lifecycle`);
CREATE TABLE `production_contracts` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`contract_type` VARCHAR(50) NOT NULL,
`schema_version` VARCHAR(80) NOT NULL,
`version` INTEGER NOT NULL,
`scope_key` VARCHAR(100) NOT NULL DEFAULT 'project',
`parent_contract_id` BIGINT NULL,
`source_snapshot_id` VARCHAR(160) NULL,
`input_hash` VARCHAR(64) NOT NULL,
`input_snapshot_json` JSON NULL,
`payload_json` JSON NOT NULL,
`source_refs_json` JSON NULL,
`quality_result_json` JSON NULL,
`reviewer_delta_json` JSON NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'draft',
`downstream_status` VARCHAR(50) NOT NULL DEFAULT 'blocked',
`created_by_user_id` BIGINT NULL,
`confirmed_at` DATETIME(3) NULL,
`released_at` DATETIME(3) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `pc_project_type_scope_version_key`(`project_id`, `contract_type`, `scope_key`, `version`),
INDEX `pc_project_type_status_idx`(`project_id`, `contract_type`, `status`),
INDEX `pc_parent_idx`(`parent_contract_id`),
INDEX `pc_input_hash_idx`(`input_hash`),
INDEX `pc_downstream_status_idx`(`downstream_status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `production_source_snapshots` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`novel_source_id` BIGINT NULL,
`snapshot_version` INTEGER NOT NULL,
`source_type` VARCHAR(50) NOT NULL,
`title` VARCHAR(255) NULL,
`content_hash` VARCHAR(64) NOT NULL,
`content_text` LONGTEXT NOT NULL,
`chapter_manifest_json` JSON NULL,
`metadata_json` JSON NULL,
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
UNIQUE INDEX `pss_project_version_key`(`project_id`, `snapshot_version`),
INDEX `pss_project_source_idx`(`project_id`, `novel_source_id`),
INDEX `pss_content_hash_idx`(`content_hash`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `production_contract_reviews` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`contract_id` BIGINT NOT NULL,
`reviewer_type` VARCHAR(80) NOT NULL,
`reviewer_version` VARCHAR(80) NOT NULL,
`review_no` INTEGER NOT NULL,
`passed` BOOLEAN NOT NULL DEFAULT false,
`quality_score` DECIMAL(5, 2) NULL,
`issues_json` JSON NULL,
`delta_json` JSON NULL,
`provider_log_id` BIGINT NULL,
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
UNIQUE INDEX `pcr_contract_reviewer_no_key`(`contract_id`, `reviewer_type`, `review_no`),
INDEX `pcr_project_reviewer_idx`(`project_id`, `reviewer_type`),
INDEX `pcr_contract_passed_idx`(`contract_id`, `passed`),
INDEX `pcr_provider_log_idx`(`provider_log_id`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,213 @@
UPDATE `provider_configs`
SET
`display_name` = CASE
WHEN `provider_code` = 'kling-v3-omni-native-audio-720p-video' THEN '可灵 v3 Omni 原生音频 720P 视频'
ELSE '可灵 v3 Omni 原生音频 1080P 视频'
END,
`config_json` = JSON_REMOVE(
JSON_SET(
COALESCE(`config_json`, JSON_OBJECT()),
'$.driver', 'kling_omni_video',
'$.auth_type', 'bearer',
'$.api_key_env', 'KLING_API_KEY',
'$.base_url', 'https://api-singapore.klingai.com',
'$.timeout_ms', 600000,
'$.create_endpoint', '/v1/videos/omni-video',
'$.task_endpoint_template', '/v1/videos/omni-video/{task_id}',
'$.poll_interval_ms', 10000,
'$.max_poll_attempts', 120,
'$.model_field', 'model_name',
'$.prompt_field', 'prompt',
'$.duration_field', 'duration',
'$.aspect_ratio_field', 'aspect_ratio',
'$.mode_field', 'mode',
'$.aspect_ratio', '9:16',
'$.mode', CASE
WHEN `provider_code` = 'kling-v3-omni-native-audio-720p-video' THEN 'std'
ELSE 'pro'
END,
'$.resolution', CASE
WHEN `provider_code` = 'kling-v3-omni-native-audio-720p-video' THEN '720p'
ELSE '1080p'
END,
'$.duration', 5,
'$.allowed_modes', JSON_ARRAY('std', 'pro', '4k'),
'$.allowed_resolutions', JSON_ARRAY('720p', '1080p', '4k'),
'$.allowed_durations', JSON_ARRAY(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
'$.max_prompt_length', 2500,
'$.max_multi_prompt_length', 512,
'$.max_multi_shots', 6,
'$.max_image_inputs', 7,
'$.web_max_image_inputs', 7,
'$.max_reference_images', 6,
'$.reference_image_limit', 6,
'$.max_image_inputs_without_video', 7,
'$.max_image_inputs_with_video', 4,
'$.max_video_inputs', 1,
'$.max_elements_with_start_end_frames', 3,
'$.input_image_formats', JSON_ARRAY('jpg', 'jpeg', 'png'),
'$.input_image_mime_types', JSON_ARRAY('image/jpeg', 'image/png'),
'$.input_image_max_mb', 10,
'$.input_image_min_dimension', 300,
'$.input_image_min_aspect_ratio', 0.4,
'$.input_image_max_aspect_ratio', 2.5,
'$.video_input_formats', JSON_ARRAY('mp4', 'mov'),
'$.video_input_min_seconds', 3,
'$.video_input_max_seconds', 15.5,
'$.video_input_max_mb', 200,
'$.video_input_min_fps', 24,
'$.video_input_max_fps', 60,
'$.video_input_min_dimension', 700,
'$.video_input_max_dimension', 4553,
'$.video_input_max_area', 8294400,
'$.video_modes', JSON_ARRAY('text_to_video', 'first_frame', 'start_end_frame', 'multi_image_reference', 'video_reference'),
'$.extra_body_json', JSON_OBJECT('sound', 'on', 'watermark_info', JSON_OBJECT('enabled', false)),
'$.supports_4k', true,
'$.supports_reference_image', true,
'$.supports_start_end_frame', true,
'$.supports_multi_image_reference', true,
'$.supports_reference_video', true,
'$.supports_multi_shot', true,
'$.supports_audio', true,
'$.supports_lipsync', true,
'$.supports_character_reference', true,
'$.supports_multi_character', true,
'$.supports_style_reference', true,
'$.note', 'Kling 3.0 Omni 官方新接口:支持原生 4K、最多 7 个图片与元素参考、单个参考视频、多镜头及原生音频。'
),
'$.access_key_env',
'$.secret_key_env',
'$.jwt_ttl_seconds',
'$.jwt_nbf_skew_seconds',
'$.image_field',
'$.reference_images_field',
'$.resolution_field'
),
`cost_rule_json` = JSON_SET(
COALESCE(`cost_rule_json`, JSON_OBJECT()),
'$.unit', 'video_seconds',
'$.currency', 'USD',
'$.price_per_second', CASE
WHEN `provider_code` = 'kling-v3-omni-native-audio-720p-video' THEN 0.112
ELSE 0.14
END,
'$.cny_fx_rate', 6.79,
'$.max_cost_per_call', CASE
WHEN `provider_code` = 'kling-v3-omni-native-audio-720p-video' THEN 2
ELSE 3
END,
'$.daily_cost_limit', CASE
WHEN `provider_code` = 'kling-v3-omni-native-audio-720p-video' THEN 30
ELSE 50
END,
'$.estimated_seconds', 10,
'$.note', CASE
WHEN `provider_code` = 'kling-v3-omni-native-audio-720p-video'
THEN 'Kling v3 Omni 原生音频 720P$0.112/s10s 约 $1.12 / ¥7.61;最终以 Kling 账单为准。'
ELSE 'Kling v3 Omni 原生音频 1080P$0.14/s10s 约 $1.40 / ¥9.51;最终以 Kling 账单为准。'
END
),
`updated_at` = NOW(3)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` IN (
'kling-v3-omni-native-audio-720p-video',
'kling-v3-omni-native-audio-1080p-video'
);
INSERT INTO `provider_configs`
(`provider_type`, `provider_code`, `display_name`, `mode`, `model_name`, `config_json`, `is_enabled`, `priority`, `rate_limit_json`, `cost_rule_json`, `created_at`, `updated_at`)
VALUES
(
'VideoProvider',
'kling-v3-omni-native-audio-4k-video',
'可灵 v3 Omni 原生音频 4K 视频',
'real',
'kling-v3-omni',
JSON_OBJECT(
'driver', 'kling_omni_video',
'auth_type', 'bearer',
'api_key_env', 'KLING_API_KEY',
'base_url', 'https://api-singapore.klingai.com',
'timeout_ms', 600000,
'create_endpoint', '/v1/videos/omni-video',
'task_endpoint_template', '/v1/videos/omni-video/{task_id}',
'poll_interval_ms', 10000,
'max_poll_attempts', 120,
'model_field', 'model_name',
'prompt_field', 'prompt',
'duration_field', 'duration',
'aspect_ratio_field', 'aspect_ratio',
'mode_field', 'mode',
'aspect_ratio', '9:16',
'mode', '4k',
'resolution', '4k',
'duration', 5,
'allowed_modes', JSON_ARRAY('std', 'pro', '4k'),
'allowed_resolutions', JSON_ARRAY('720p', '1080p', '4k'),
'allowed_durations', JSON_ARRAY(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
'max_prompt_length', 2500,
'max_multi_prompt_length', 512,
'max_multi_shots', 6,
'max_image_inputs', 7,
'web_max_image_inputs', 7,
'max_reference_images', 6,
'reference_image_limit', 6,
'max_image_inputs_without_video', 7,
'max_image_inputs_with_video', 4,
'max_video_inputs', 1,
'max_elements_with_start_end_frames', 3,
'input_image_formats', JSON_ARRAY('jpg', 'jpeg', 'png'),
'input_image_mime_types', JSON_ARRAY('image/jpeg', 'image/png'),
'input_image_max_mb', 10,
'input_image_min_dimension', 300,
'input_image_min_aspect_ratio', 0.4,
'input_image_max_aspect_ratio', 2.5,
'video_input_formats', JSON_ARRAY('mp4', 'mov'),
'video_input_min_seconds', 3,
'video_input_max_seconds', 15.5,
'video_input_max_mb', 200,
'video_input_min_fps', 24,
'video_input_max_fps', 60,
'video_input_min_dimension', 700,
'video_input_max_dimension', 4553,
'video_input_max_area', 8294400,
'video_modes', JSON_ARRAY('text_to_video', 'first_frame', 'start_end_frame', 'multi_image_reference', 'video_reference'),
'extra_body_json', JSON_OBJECT('sound', 'on', 'watermark_info', JSON_OBJECT('enabled', false)),
'supports_4k', true,
'supports_reference_image', true,
'supports_start_end_frame', true,
'supports_multi_image_reference', true,
'supports_reference_video', true,
'supports_multi_shot', true,
'supports_audio', true,
'supports_lipsync', true,
'supports_character_reference', true,
'supports_multi_character', true,
'supports_style_reference', true,
'note', '默认禁用。Kling 3.0 Omni 官方原生 4K 模式,支持最多 7 个图片与元素参考;4K 成本较高,正式批量前需单镜头验证。'
),
false,
41,
JSON_OBJECT('rpm', 5, 'concurrency', 1),
JSON_OBJECT(
'flat_cost', 0,
'unit', 'video_seconds',
'price_per_second', 0.42,
'currency', 'USD',
'cny_fx_rate', 6.79,
'max_cost_per_call', 7,
'daily_cost_limit', 100,
'estimated_seconds', 10,
'note', 'Kling v3 Omni 原生音频 4K$0.42/s10s 约 $4.20 / ¥28.52;最终以 Kling 账单为准。'
),
NOW(3),
NOW(3)
)
ON DUPLICATE KEY UPDATE
`display_name` = VALUES(`display_name`),
`mode` = VALUES(`mode`),
`model_name` = VALUES(`model_name`),
`priority` = VALUES(`priority`),
`rate_limit_json` = VALUES(`rate_limit_json`),
`cost_rule_json` = VALUES(`cost_rule_json`),
`updated_at` = NOW(3);
@@ -0,0 +1,230 @@
UPDATE `provider_configs`
SET
`config_json` = JSON_REMOVE(
JSON_SET(
COALESCE(`config_json`, JSON_OBJECT()),
'$.driver', 'kling_v3_image_to_video',
'$.auth_type', 'bearer',
'$.api_key_env', 'KLING_API_KEY',
'$.base_url', 'https://api-singapore.klingai.com',
'$.timeout_ms', 600000,
'$.create_endpoint', '/v1/videos/image2video',
'$.task_endpoint_template', '/v1/videos/image2video/{task_id}',
'$.poll_interval_ms', 10000,
'$.max_poll_attempts', 120,
'$.aspect_ratio', '9:16',
'$.mode', CASE
WHEN `provider_code` = 'kling-v3-native-audio-720p-video' THEN 'std'
ELSE 'pro'
END,
'$.resolution', CASE
WHEN `provider_code` = 'kling-v3-native-audio-720p-video' THEN '720p'
ELSE '1080p'
END,
'$.duration', 5,
'$.allowed_modes', JSON_ARRAY('std', 'pro', '4k'),
'$.allowed_resolutions', JSON_ARRAY('720p', '1080p', '4k'),
'$.allowed_durations', JSON_ARRAY(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
'$.max_prompt_length', 2500,
'$.max_multi_prompt_length', 512,
'$.max_multi_shots', 6,
'$.max_image_inputs', 2,
'$.web_max_image_inputs', 2,
'$.max_elements', 3,
'$.max_voices', 2,
'$.input_image_formats', JSON_ARRAY('jpg', 'jpeg', 'png'),
'$.input_image_mime_types', JSON_ARRAY('image/jpeg', 'image/png'),
'$.input_image_max_mb', 10,
'$.input_image_min_dimension', 300,
'$.input_image_min_aspect_ratio', 0.4,
'$.input_image_max_aspect_ratio', 2.5,
'$.video_modes', JSON_ARRAY('first_frame', 'end_frame', 'start_end_frame'),
'$.extra_body_json', JSON_OBJECT('sound', 'on', 'watermark_info', JSON_OBJECT('enabled', false)),
'$.supports_4k', true,
'$.supports_reference_image', true,
'$.supports_start_end_frame', true,
'$.supports_elements', true,
'$.supports_voices', true,
'$.supports_multi_shot', true,
'$.supports_audio', true,
'$.supports_lipsync', true,
'$.supports_character_reference', true,
'$.supports_multi_character', true,
'$.supports_style_reference', false,
'$.capability_version', 'kling_video_capabilities_2026-07-15',
'$.formal_splus_model', true,
'$.official_doc_url', 'https://kling.ai/document-api/api/video/3-0-omni/image-to-video',
'$.note', 'Kling v3 官方 image2video 适配器:首帧/尾帧、最多3个元素、最多2个音色、自定义或智能多镜头、原生音频与4K。'
),
'$.access_key_env',
'$.secret_key_env',
'$.jwt_ttl_seconds',
'$.jwt_nbf_skew_seconds',
'$.model_field',
'$.image_field',
'$.prompt_field',
'$.duration_field',
'$.aspect_ratio_field',
'$.resolution_field',
'$.reference_images_field',
'$.max_reference_images',
'$.reference_image_limit'
),
`updated_at` = NOW(3)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` IN (
'kling-v3-native-audio-720p-video',
'kling-v3-native-audio-video'
);
INSERT INTO `provider_configs`
(`provider_type`, `provider_code`, `display_name`, `mode`, `model_name`, `config_json`, `is_enabled`, `priority`, `rate_limit_json`, `cost_rule_json`, `created_at`, `updated_at`)
VALUES
(
'VideoProvider',
'kling-v3-native-audio-4k-video',
'可灵 v3 原生音频 4K 图生视频',
'real',
'kling-v3',
JSON_OBJECT(
'driver', 'kling_v3_image_to_video',
'auth_type', 'bearer',
'api_key_env', 'KLING_API_KEY',
'base_url', 'https://api-singapore.klingai.com',
'timeout_ms', 600000,
'create_endpoint', '/v1/videos/image2video',
'task_endpoint_template', '/v1/videos/image2video/{task_id}',
'poll_interval_ms', 10000,
'max_poll_attempts', 120,
'aspect_ratio', '9:16',
'mode', '4k',
'resolution', '4k',
'duration', 5,
'allowed_modes', JSON_ARRAY('std', 'pro', '4k'),
'allowed_resolutions', JSON_ARRAY('720p', '1080p', '4k'),
'allowed_durations', JSON_ARRAY(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
'max_prompt_length', 2500,
'max_multi_prompt_length', 512,
'max_multi_shots', 6,
'max_image_inputs', 2,
'web_max_image_inputs', 2,
'max_elements', 3,
'max_voices', 2,
'input_image_formats', JSON_ARRAY('jpg', 'jpeg', 'png'),
'input_image_mime_types', JSON_ARRAY('image/jpeg', 'image/png'),
'input_image_max_mb', 10,
'input_image_min_dimension', 300,
'input_image_min_aspect_ratio', 0.4,
'input_image_max_aspect_ratio', 2.5,
'video_modes', JSON_ARRAY('first_frame', 'end_frame', 'start_end_frame'),
'extra_body_json', JSON_OBJECT('sound', 'on', 'watermark_info', JSON_OBJECT('enabled', false)),
'supports_4k', true,
'supports_reference_image', true,
'supports_start_end_frame', true,
'supports_elements', true,
'supports_voices', true,
'supports_multi_shot', true,
'supports_audio', true,
'supports_lipsync', true,
'supports_character_reference', true,
'supports_multi_character', true,
'supports_style_reference', false,
'capability_version', 'kling_video_capabilities_2026-07-15',
'formal_splus_model', true,
'official_doc_url', 'https://kling.ai/document-api/api/video/3-0-omni/image-to-video',
'note', '默认禁用。Kling v3 官方图生视频4K模式;正式批量前需单镜头验证。'
),
false,
43,
JSON_OBJECT('rpm', 5, 'concurrency', 1),
JSON_OBJECT(
'flat_cost', 0,
'unit', 'video_seconds',
'price_per_second', 0.504,
'currency', 'USD',
'cny_fx_rate', 6.79,
'max_cost_per_call', 8,
'daily_cost_limit', 120,
'estimated_seconds', 10,
'note', 'Kling v3 原生音频4K暂估 $0.504/s10s约 $5.04 / ¥34.22;最终以Kling账单为准。'
),
NOW(3),
NOW(3)
)
ON DUPLICATE KEY UPDATE
`display_name` = VALUES(`display_name`),
`mode` = VALUES(`mode`),
`model_name` = VALUES(`model_name`),
`priority` = VALUES(`priority`),
`rate_limit_json` = VALUES(`rate_limit_json`),
`cost_rule_json` = VALUES(`cost_rule_json`),
`updated_at` = NOW(3);
UPDATE `provider_configs`
SET
`config_json` = JSON_SET(
COALESCE(`config_json`, JSON_OBJECT()),
'$.capability_version', 'kling_video_capabilities_2026-07-15',
'$.formal_splus_model', true,
'$.official_doc_url', 'https://kling.ai/document-api/api/video/3-0-omni/video-omni',
'$.reference_video_default_type', 'base',
'$.reference_video_character_policy', 'reject_with_reference_video',
'$.custom_multi_shot_omits_top_prompt', true
),
`updated_at` = NOW(3)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` IN (
'kling-v3-omni-native-audio-720p-video',
'kling-v3-omni-native-audio-1080p-video',
'kling-v3-omni-native-audio-4k-video'
);
UPDATE `provider_configs`
SET
`config_json` = JSON_SET(
COALESCE(`config_json`, JSON_OBJECT()),
'$.formal_splus_model', false,
'$.splus_usage', 'legacy_or_experiment_only'
),
`updated_at` = NOW(3)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` LIKE 'kling%'
AND `provider_code` NOT IN (
'kling-v3-native-audio-720p-video',
'kling-v3-native-audio-video',
'kling-v3-native-audio-4k-video',
'kling-v3-omni-native-audio-720p-video',
'kling-v3-omni-native-audio-1080p-video',
'kling-v3-omni-native-audio-4k-video'
);
UPDATE `system_configs`
SET
`config_value` = JSON_SET(
COALESCE(`config_value`, JSON_OBJECT()),
'$.splus_live_action_video',
JSON_OBJECT(
'capability_version', 'kling_video_capabilities_2026-07-15',
'formal_provider_codes', JSON_ARRAY(
'kling-v3-omni-native-audio-720p-video',
'kling-v3-omni-native-audio-1080p-video',
'kling-v3-omni-native-audio-4k-video',
'kling-v3-native-audio-720p-video',
'kling-v3-native-audio-video',
'kling-v3-native-audio-4k-video'
),
'zh-CN', JSON_OBJECT(
'thresholds', JSON_OBJECT('premium_importance_gt', 7, 'premium_action_gt', 5),
'normal', JSON_OBJECT(
'provider_code', 'kling-v3-omni-native-audio-1080p-video',
'fallback_chain', JSON_ARRAY('kling-v3-omni-native-audio-1080p-video', 'kling-v3-native-audio-video')
),
'premium', JSON_OBJECT(
'provider_code', 'kling-v3-omni-native-audio-1080p-video',
'fallback_chain', JSON_ARRAY('kling-v3-omni-native-audio-1080p-video', 'kling-v3-native-audio-video')
)
)
)
),
`updated_at` = NOW(3)
WHERE `config_key` = 'ai.router.v1';
@@ -0,0 +1,15 @@
UPDATE `provider_configs`
SET
`cost_rule_json` = JSON_SET(
COALESCE(`cost_rule_json`, JSON_OBJECT()),
'$.price_per_second', 0.42,
'$.currency', 'USD',
'$.cny_fx_rate', 6.79,
'$.max_cost_per_call', 7,
'$.daily_cost_limit', 100,
'$.estimated_seconds', 10,
'$.note', 'Kling v3 原生音频4K $0.42/s10s约 $4.20 / ¥28.52;最终以Kling账单为准。'
),
`updated_at` = NOW(3)
WHERE `provider_type` = 'VideoProvider'
AND `provider_code` = 'kling-v3-native-audio-4k-video';
@@ -0,0 +1,64 @@
CREATE TABLE `shot_generation_plans` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`project_id` BIGINT NOT NULL,
`episode_id` BIGINT NOT NULL,
`shot_id` BIGINT NOT NULL,
`revision` INTEGER NOT NULL,
`plan_version` VARCHAR(80) NOT NULL,
`plan_hash` VARCHAR(64) NOT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'frozen',
`source_engine_version` VARCHAR(50) NOT NULL,
`provider_code` VARCHAR(100) NOT NULL,
`provider_id` BIGINT NULL,
`model_name` VARCHAR(160) NULL,
`endpoint` VARCHAR(255) NULL,
`capability_version` VARCHAR(100) NULL,
`route_tier` VARCHAR(50) NULL,
`effective_mode` VARCHAR(30) NULL,
`resolution_recommendation` VARCHAR(30) NULL,
`effective_generation_resolution` VARCHAR(30) NULL,
`aspect_ratio` VARCHAR(30) NULL,
`duration` DECIMAL(6, 2) NULL,
`sound_enabled` BOOLEAN NOT NULL DEFAULT true,
`multi_shot` BOOLEAN NOT NULL DEFAULT false,
`native_4k_candidate` BOOLEAN NOT NULL DEFAULT false,
`project_native_4k_enabled` BOOLEAN NOT NULL DEFAULT false,
`required_assets_json` JSON NULL,
`element_plan_json` JSON NULL,
`voice_plan_json` JSON NULL,
`keyframe_plan_json` JSON NULL,
`video_request_json` JSON NULL,
`router_decision_json` JSON NOT NULL,
`quality_policy_json` JSON NOT NULL,
`retry_policy_json` JSON NOT NULL,
`fallback_chain_json` JSON NOT NULL,
`cost_policy_json` JSON NULL,
`provider_snapshot_json` JSON NULL,
`prompt_snapshot_json` JSON NULL,
`frozen_by_user_id` BIGINT NULL,
`frozen_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
UNIQUE INDEX `sgp_shot_revision_key` (`shot_id`, `revision`),
UNIQUE INDEX `sgp_shot_hash_key` (`shot_id`, `plan_hash`),
INDEX `sgp_project_episode_shot_idx` (`project_id`, `episode_id`, `shot_id`),
INDEX `sgp_provider_capability_idx` (`provider_code`, `capability_version`),
INDEX `sgp_status_created_idx` (`status`, `created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `storyboard_shots`
ADD COLUMN `active_generation_plan_id` BIGINT NULL,
ADD INDEX `ss_active_generation_plan_idx` (`active_generation_plan_id`);
ALTER TABLE `shot_images`
ADD COLUMN `generation_plan_id` BIGINT NULL,
ADD INDEX `si_generation_plan_idx` (`generation_plan_id`);
ALTER TABLE `video_clips`
ADD COLUMN `generation_plan_id` BIGINT NULL,
ADD INDEX `vc_generation_plan_idx` (`generation_plan_id`);
ALTER TABLE `render_tasks`
ADD COLUMN `generation_plan_id` BIGINT NULL,
ADD INDEX `rt_generation_plan_idx` (`generation_plan_id`);
@@ -0,0 +1,71 @@
ALTER TABLE `shot_generation_plans`
ADD COLUMN `capability_registry_version_id` BIGINT NULL AFTER `provider_id`,
ADD COLUMN `parameter_schema_version_id` BIGINT NULL AFTER `capability_registry_version_id`,
ADD COLUMN `pricing_version_id` BIGINT NULL AFTER `parameter_schema_version_id`;
CREATE INDEX `sgp_capability_registry_idx`
ON `shot_generation_plans`(`capability_registry_version_id`);
CREATE INDEX `sgp_parameter_schema_idx`
ON `shot_generation_plans`(`parameter_schema_version_id`);
CREATE INDEX `sgp_pricing_version_idx`
ON `shot_generation_plans`(`pricing_version_id`);
CREATE TABLE `model_capability_versions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`provider_type` VARCHAR(80) NOT NULL,
`provider_code` VARCHAR(100) NOT NULL,
`model_name` VARCHAR(160) NULL,
`revision` INTEGER NOT NULL,
`version_key` VARCHAR(160) NOT NULL,
`content_hash` VARCHAR(64) NOT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'published',
`capability_json` JSON NOT NULL,
`source_json` JSON NULL,
`effective_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE INDEX `mcv_provider_revision_key`(`provider_type`, `provider_code`, `revision`),
UNIQUE INDEX `mcv_provider_hash_key`(`provider_type`, `provider_code`, `content_hash`),
INDEX `mcv_provider_status_effective_idx`(`provider_code`, `status`, `effective_at`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `model_parameter_schema_versions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`provider_type` VARCHAR(80) NOT NULL,
`provider_code` VARCHAR(100) NOT NULL,
`model_name` VARCHAR(160) NULL,
`revision` INTEGER NOT NULL,
`version_key` VARCHAR(160) NOT NULL,
`content_hash` VARCHAR(64) NOT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'published',
`schema_json` JSON NOT NULL,
`source_json` JSON NULL,
`effective_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE INDEX `mpsv_provider_revision_key`(`provider_type`, `provider_code`, `revision`),
UNIQUE INDEX `mpsv_provider_hash_key`(`provider_type`, `provider_code`, `content_hash`),
INDEX `mpsv_provider_status_effective_idx`(`provider_code`, `status`, `effective_at`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `model_pricing_versions` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`provider_type` VARCHAR(80) NOT NULL,
`provider_code` VARCHAR(100) NOT NULL,
`model_name` VARCHAR(160) NULL,
`revision` INTEGER NOT NULL,
`version_key` VARCHAR(160) NOT NULL,
`content_hash` VARCHAR(64) NOT NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'published',
`pricing_json` JSON NOT NULL,
`source_json` JSON NULL,
`effective_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE INDEX `mpv_provider_revision_key`(`provider_type`, `provider_code`, `revision`),
UNIQUE INDEX `mpv_provider_hash_key`(`provider_type`, `provider_code`, `content_hash`),
INDEX `mpv_provider_status_effective_idx`(`provider_code`, `status`, `effective_at`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -0,0 +1,60 @@
CREATE TABLE `character_provider_bindings` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`global_character_id` BIGINT NOT NULL,
`look_version_id` BIGINT NULL,
`source_asset_id` BIGINT NULL,
`provider_code` VARCHAR(100) NOT NULL,
`provider_asset_type` VARCHAR(50) NOT NULL DEFAULT 'video_character_element',
`provider_element_id` VARCHAR(191) NOT NULL,
`element_name` VARCHAR(160) NOT NULL,
`element_description` TEXT NULL,
`source_duration` DECIMAL(5,2) NULL,
`voice_bound` BOOLEAN NOT NULL DEFAULT false,
`voice_id` VARCHAR(160) NULL,
`voice_description` TEXT NULL,
`binding_version` INTEGER NOT NULL DEFAULT 1,
`validation_score` DECIMAL(5,2) NULL,
`validation_report_json` JSON NULL,
`status` VARCHAR(50) NOT NULL DEFAULT 'candidate',
`is_primary` BOOLEAN NOT NULL DEFAULT false,
`created_by_user_id` BIGINT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
UNIQUE INDEX `cpb_provider_element_key`(`provider_code`, `provider_element_id`),
INDEX `cpb_character_provider_status_idx`(`global_character_id`, `provider_code`, `status`),
INDEX `cpb_look_version_idx`(`look_version_id`),
INDEX `cpb_source_asset_idx`(`source_asset_id`),
INDEX `cpb_primary_status_idx`(`is_primary`, `status`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
UPDATE `provider_configs`
SET `config_json` = JSON_SET(
COALESCE(`config_json`, JSON_OBJECT()),
'$.aspect_ratio', '16:9',
'$.allowed_aspect_ratios', JSON_ARRAY('16:9'),
'$.production_aspect_ratio', '16:9'
)
WHERE `provider_type` IN ('ImageProvider', 'VideoProvider');
UPDATE `provider_configs`
SET `config_json` = JSON_SET(`config_json`, '$.size', '2560x1440')
WHERE `provider_type` = 'ImageProvider'
AND JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.size')) = '1440x2560';
UPDATE `provider_configs`
SET `config_json` = JSON_SET(`config_json`, '$.size', '1536x1024')
WHERE `provider_type` = 'ImageProvider'
AND JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.size')) = '1024x1792';
UPDATE `provider_configs`
SET `config_json` = JSON_SET(
`config_json`,
'$.width', CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.height')) AS UNSIGNED),
'$.height', CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.width')) AS UNSIGNED)
)
WHERE `provider_type` IN ('ImageProvider', 'VideoProvider')
AND JSON_TYPE(JSON_EXTRACT(`config_json`, '$.width')) IN ('INTEGER', 'DOUBLE')
AND JSON_TYPE(JSON_EXTRACT(`config_json`, '$.height')) IN ('INTEGER', 'DOUBLE')
AND CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.width')) AS UNSIGNED)
< CAST(JSON_UNQUOTE(JSON_EXTRACT(`config_json`, '$.height')) AS UNSIGNED);
@@ -0,0 +1,45 @@
UPDATE `provider_configs`
SET
`config_json` = JSON_SET(
COALESCE(`config_json`, JSON_OBJECT()),
'$.size', '2560x1440',
'$.quality', 'medium',
'$.allowed_sizes', JSON_ARRAY('1920x1080', '2560x1440', '3840x2160'),
'$.allowed_qualities', JSON_ARRAY('low', 'medium', 'high', 'auto'),
'$.supports_custom_size', true,
'$.supports_4k', true,
'$.max_width', 3840,
'$.max_height', 3840,
'$.max_pixels', 8294400,
'$.max_aspect_ratio', 3,
'$.experimental_above_size', '2560x1440',
'$.official_doc_url', 'https://developers.openai.com/api/docs/guides/image-generation'
),
`cost_rule_json` = JSON_REMOVE(
JSON_SET(
COALESCE(`cost_rule_json`, JSON_OBJECT()),
'$.flat_cost', 0,
'$.unit', 'openai_image_tokens',
'$.currency', 'USD',
'$.cny_fx_rate', 7.2,
'$.pricing_basis', 'official_openai_gpt_image_2_token_formula_2026_07_16',
'$.default_quality', 'medium',
'$.default_size', '2560x1440',
'$.estimated_cost_per_image', 0.05529,
'$.price_per_1m_text_input_tokens', 5,
'$.price_per_1m_cached_text_input_tokens', 1.25,
'$.price_per_1m_image_input_tokens', 8,
'$.price_per_1m_cached_image_input_tokens', 2,
'$.price_per_1m_image_output_tokens', 30,
'$.output_token_formula', 'ceil(long_quality_tokens*round(long_quality_tokens*short_edge/long_edge)*(2000000+width*height)/4000000)',
'$.output_price_examples', JSON_OBJECT(
'2560x1440_high', 0.2211,
'3840x2160_high', 0.40026
),
'$.note', 'GPT Image 2 按文字输入、参考图输入和图片输出 token 计费;2560×1440 High 纯输出约 $0.22113840×2160 High 纯输出约 $0.4003。参考图与提示词输入另计,以 OpenAI usage 和账单为准。'
),
'$.price_per_image_by_quality',
'$.comparable_models'
)
WHERE `provider_type` = 'ImageProvider'
AND `provider_code` = 'openai-image';
@@ -0,0 +1,2 @@
ALTER TABLE `project_pipeline_configs`
ALTER COLUMN `scene_composer_version` SET DEFAULT 'scene_composer_plan_v2';
@@ -0,0 +1,26 @@
UPDATE `project_pipeline_configs`
SET
`scene_composer_version` = 'scene_composer_plan_v2',
`scene_composer_config_json` = JSON_SET(
COALESCE(`scene_composer_config_json`, JSON_OBJECT()),
'$.plan_version', 'scene_composer_plan_v2',
'$.transition_contract_version', 'transition_contract_v2',
'$.picture_edit_policy', 'hard_picture_by_default_explicit_blends_only',
'$.audio_edit_policy', 'independent_j_cut_l_cut_and_audio_bridge',
'$.planned_features', JSON_ARRAY(
'shot_ordering',
'emotion_curve',
'transition_contract_v2',
'boundary_frame_policy',
'screen_vector_continuity',
'hard_picture_cut',
'explicit_visual_blends_only',
'independent_j_cut_l_cut',
'episode_builder',
'bgm_alignment',
'original_music_bible',
'sfx_cue_plan',
'ducking_plan'
),
'$.note', 'Scene Composer Plan V2 keeps picture hard by default, permits visual overlap only for explicit blend contracts, and schedules J-cuts, L-cuts and audio bridges independently.'
);
+1151 -256
View File
File diff suppressed because it is too large Load Diff
+324 -1
View File
@@ -1,4 +1,4 @@
import { PrismaClient } from '@prisma/client';
import { Prisma, PrismaClient } from '@prisma/client';
import { hash } from 'bcryptjs';
import { DEFAULT_AI_ROUTER_CONFIG } from '../src/ai-router/ai-router.types';
@@ -17,6 +17,296 @@ const mockProviders = [
['EmbeddingProvider', 'mock-embedding', 'Mock Embedding Provider', 'mock-embedding-v1']
] as const;
const defaultAgentPrompts = [
{
agent_name: 'NovelIdeaCoachAgent',
provider_type: 'TextProvider',
default_provider_code: 'volcengine-doubao-seed20-mini-text',
system_prompt: '你是网文平台金牌主编、商业化小说策划、听书编导和短剧改编策划。你的用户是不会写小说的运营,你必须把他的白话需求变成可选择的高质量小说方案。',
user_prompt_template: [
'请根据运营输入生成 3 个高商业化小说方向卡。必须严格输出紧凑 JSON,不要 Markdown,不要解释。',
'运营不是专业作家,所以每个方案要短、准、好判断:为什么能火、主角是谁、开篇怎么抓人。',
'本步骤只做方向筛选,不写卷纲和章节蓝图;后续 IP 圣经 Agent 会补完整结构。',
'必须兼顾后续听书和短剧改编:人物关系清楚、场景可视化、每章能有钩子。',
'输出 JSON 字段:proposals。proposals 是数组,每个元素包含:',
'id, title, genre, novel_scale, target_audience, target_words, target_chapters, style_code, logline, opening_hook, main_character, core_conflict, selling_points, risk_notes, adaptation_targets, writing_rules, forbidden_rules, score, operator_reason。',
'要求:',
'1. title 要像真实网文书名,不要泛泛而谈。',
'2. logline 不超过 35 字,opening_hook 不超过 55 字。',
'3. main_character 和 core_conflict 各不超过 35 字。',
'4. selling_points 只给 3 条,每条不超过 10 字;risk_notes 只给 2 条,每条不超过 12 字。',
'5. writing_rules 只给 2 条,forbidden_rules 只给 2 条,每条不超过 12 字。',
'6. target_words 和 target_chapters 必须是数字或 null。',
'7. adaptation_targets 只输出 ["听书","短剧"]operator_reason 不超过 35 字,score 是 0-100。',
'小说长度:{{novel_scale}}',
'目标读者:{{target_audience}}',
'题材:{{genre}}',
'风格代码:{{style_code}}',
'目标字数:{{target_words}}',
'目标章节数:{{target_chapters}}',
'后续用途:{{adaptation_targets}}',
'运营灵感:{{inspiration_text}}',
'必须包含:{{must_have_text}}',
'禁止内容:{{avoid_text}}',
'参考风格/作品:{{reference_titles}}'
].join('\n\n'),
output_schema_json: {
type: 'object',
required: ['proposals'],
properties: {
proposals: {
type: 'array',
items: {
type: 'object',
required: ['title', 'logline', 'opening_hook', 'main_character', 'core_conflict', 'selling_points', 'score'],
properties: {
id: { type: 'string' },
title: { type: 'string' },
genre: { type: 'string' },
novel_scale: { type: 'string' },
target_audience: { type: 'string' },
target_words: { type: 'number' },
target_chapters: { type: 'number' },
style_code: { type: 'string' },
logline: { type: 'string' },
opening_hook: { type: 'string' },
main_character: { type: 'string' },
core_conflict: { type: 'string' },
selling_points: { type: 'array' },
risk_notes: { type: 'array' },
adaptation_targets: { type: 'array' },
writing_rules: { type: 'array' },
forbidden_rules: { type: 'array' },
score: { type: 'number' },
operator_reason: { type: 'string' }
}
}
}
}
},
temperature: 0.72,
max_output_tokens: 1200
},
{
agent_name: 'IPBibleAgent',
provider_type: 'NovelProvider',
default_provider_code: 'openai-responses-novel',
system_prompt: '你是专业长篇小说总策划、网文主编、文学作家、剧作结构师和影视导演。你的任务是建立稳定、可长期创作、可听书、可短剧改编的 IP 圣经。',
user_prompt_template: [
'请根据用户 Brief 生成 IP 圣经。必须严格输出 JSON,不要 Markdown。',
'JSON 字段:core_logline, theme, worldbuilding, main_characters, supporting_characters, antagonist_system, volume_structure, foreshadow_plan, writing_rules, forbidden_rules, adaptation_rules。',
'要求:主线清晰,人设稳定,有禁止改动项,有伏笔规划,考虑听书和 AI 短剧改编。',
'用户 Brief{{brief}}',
'小说类型:{{novel_scale}}',
'题材:{{genre}}',
'风格:{{style}}',
'禁止内容:{{avoid}}'
].join('\n\n'),
output_schema_json: {
type: 'object',
required: ['core_logline', 'theme', 'worldbuilding', 'main_characters', 'volume_structure', 'writing_rules', 'forbidden_rules'],
properties: {
core_logline: { type: 'string' },
theme: { type: 'string' },
worldbuilding: { type: 'object' },
main_characters: { type: 'array' },
supporting_characters: { type: 'array' },
antagonist_system: { type: 'object' },
volume_structure: { type: 'array' },
foreshadow_plan: { type: 'array' },
writing_rules: { type: 'array' },
forbidden_rules: { type: 'array' },
adaptation_rules: { type: 'object' }
}
},
temperature: 0.6,
max_output_tokens: 6000
},
{
agent_name: 'ChapterCardAgent',
provider_type: 'NovelProvider',
default_provider_code: 'openai-responses-novel',
system_prompt: '你是长篇小说章节导演。你的任务是设计下一章章节卡,不写正文,只输出可执行的章节方案。',
user_prompt_template: [
'请生成第 {{chapter_no}} 章章节卡。必须严格输出 JSON,不要 Markdown。',
'本章必须承接上一章,推动主线、人物变化或伏笔进展,不得水剧情。',
'IP 圣经摘要:{{ip_bible_summary}}',
'当前卷纲:{{volume_outline}}',
'最近章节摘要:{{recent_summaries}}',
'人物当前状态:{{character_states}}',
'当前伏笔:{{active_foreshadows}}',
'禁止事项:{{forbidden_rules}}',
'请输出:chapter_no, title, chapter_goal, scenes, must_happen, must_not_happen, foreshadows_to_add, foreshadows_to_advance, foreshadows_to_resolve, ending_hook, adaptation_notes。'
].join('\n\n'),
output_schema_json: {
type: 'object',
required: ['chapter_no', 'title', 'chapter_goal', 'scenes', 'must_happen', 'must_not_happen', 'ending_hook'],
properties: {
chapter_no: { type: 'number' },
title: { type: 'string' },
chapter_goal: { type: 'string' },
scenes: { type: 'array' },
must_happen: { type: 'array' },
must_not_happen: { type: 'array' },
foreshadows_to_add: { type: 'array' },
foreshadows_to_advance: { type: 'array' },
foreshadows_to_resolve: { type: 'array' },
ending_hook: { type: 'string' },
adaptation_notes: { type: 'object' }
}
},
temperature: 0.45,
max_output_tokens: 4000
},
{
agent_name: 'NovelWriterAgent',
provider_type: 'NovelProvider',
default_provider_code: 'anthropic-claude-novel',
system_prompt: '你是专业小说家。你必须严格按章节卡写正文,不改变 IP 圣经,不改变人物状态,不提前揭露秘密。',
user_prompt_template: [
'请创作第 {{chapter_no}} 章正文,只输出正文,不要解释。',
'目标字数:{{target_words}}',
'文风规则:{{style_rules}}',
'IP 圣经摘要:{{ip_bible_summary}}',
'人物档案:{{character_profiles}}',
'人物当前状态:{{character_states}}',
'最近章节摘要:{{recent_summaries}}',
'章节卡:{{chapter_card}}',
'禁止规则:{{forbidden_rules}}',
'要求:画面感强、对白符合身份、每场戏推动剧情/人物/伏笔,不能流水账,不能狗血。'
].join('\n\n'),
output_schema_json: null,
temperature: 0.75,
max_output_tokens: 9000
},
{
agent_name: 'NovelPolishAgent',
provider_type: 'NovelProvider',
default_provider_code: 'anthropic-claude-novel',
system_prompt: '你是小说修辞和节奏编辑。你只负责润色文风、节奏、对白和画面感,不得改变剧情事实。',
user_prompt_template: [
'请润色下面章节,输出完整润色稿,不要解释。',
'目标文风:{{style_rules}}',
'章节卡:{{chapter_card}}',
'禁止改变:{{forbidden_rules}}',
'原稿:{{draft_text}}'
].join('\n\n'),
output_schema_json: null,
temperature: 0.65,
max_output_tokens: 9000
},
{
agent_name: 'ContinuityCheckAgent',
provider_type: 'NovelProvider',
default_provider_code: 'openai-responses-novel',
system_prompt: '你是严苛的小说连续性审稿人,只检查冲突,不负责夸奖。',
user_prompt_template: [
'请检查本章是否违背设定、人设、时间线、地点、伏笔。必须严格输出 JSON,不要 Markdown。',
'IP 圣经:{{ip_bible}}',
'人物状态:{{character_states}}',
'伏笔库:{{active_foreshadows}}',
'章节卡:{{chapter_card}}',
'本章正文:{{chapter_text}}',
'请输出:has_conflict, conflicts, character_drift, timeline_errors, setting_errors, foreshadow_errors, fix_suggestions。'
].join('\n\n'),
output_schema_json: {
type: 'object',
required: ['has_conflict', 'conflicts', 'fix_suggestions'],
properties: {
has_conflict: { type: 'boolean' },
conflicts: { type: 'array' },
character_drift: { type: 'array' },
timeline_errors: { type: 'array' },
setting_errors: { type: 'array' },
foreshadow_errors: { type: 'array' },
fix_suggestions: { type: 'array' }
}
},
temperature: 0.2,
max_output_tokens: 3000
},
{
agent_name: 'QualityCheckAgent',
provider_type: 'NovelProvider',
default_provider_code: 'openai-responses-novel',
system_prompt: '你是严苛的小说主编和质量评分官。你只输出评分、问题和修复策略。',
user_prompt_template: [
'请为本章评分。必须严格输出 JSON,不要 Markdown。',
'评分维度:剧情推进、人物一致性、上下文连续性、文风质量、情绪张力、伏笔管理、场景画面感、听书适配度、短剧适配度。',
'IP 圣经:{{ip_bible}}',
'章节卡:{{chapter_card}}',
'连续性检查:{{continuity_check}}',
'本章正文:{{chapter_text}}',
'请输出:pass, total_score, scores, problems, must_fix, optional_suggestions, rewrite_required, rewrite_strategy。'
].join('\n\n'),
output_schema_json: {
type: 'object',
required: ['pass', 'total_score', 'scores', 'problems', 'rewrite_required'],
properties: {
pass: { type: 'boolean' },
total_score: { type: 'number' },
scores: { type: 'object' },
problems: { type: 'array' },
must_fix: { type: 'array' },
optional_suggestions: { type: 'array' },
rewrite_required: { type: 'boolean' },
rewrite_strategy: { type: 'array' }
}
},
temperature: 0.2,
max_output_tokens: 3000
},
{
agent_name: 'RepairAgent',
provider_type: 'NovelProvider',
default_provider_code: 'anthropic-claude-novel',
system_prompt: '你是小说修稿编辑。你只修复质检指出的问题,不新增无关设定,不破坏上下文。',
user_prompt_template: [
'请根据质检报告修复本章,输出完整修复稿,不要解释。',
'IP 圣经摘要:{{ip_bible_summary}}',
'章节卡:{{chapter_card}}',
'原正文:{{chapter_text}}',
'质检报告:{{quality_report}}',
'连续性检查:{{continuity_check}}'
].join('\n\n'),
output_schema_json: null,
temperature: 0.55,
max_output_tokens: 9000
},
{
agent_name: 'MemoryUpdateAgent',
provider_type: 'NovelProvider',
default_provider_code: 'volcengine-doubao-seed20-pro-novel',
system_prompt: '你是小说连续性档案管理员。你必须准确、简洁,不得加入正文没有发生的内容,不得猜测未来。',
user_prompt_template: [
'请根据本章正文更新记忆库。必须严格输出 JSON,不要 Markdown。',
'已有角色状态:{{character_states}}',
'已有伏笔库:{{active_foreshadows}}',
'本章正文:{{chapter_text}}',
'请输出:chapter_summary, character_updates, relationship_updates, world_updates, timeline_update, location_update, new_foreshadows, updated_foreshadows, resolved_foreshadows, next_chapter_must_continue, forbidden_to_forget。'
].join('\n\n'),
output_schema_json: {
type: 'object',
required: ['chapter_summary', 'character_updates', 'timeline_update', 'next_chapter_must_continue'],
properties: {
chapter_summary: { type: 'string' },
character_updates: { type: 'array' },
relationship_updates: { type: 'array' },
world_updates: { type: 'array' },
timeline_update: { type: 'string' },
location_update: { type: 'string' },
new_foreshadows: { type: 'array' },
updated_foreshadows: { type: 'array' },
resolved_foreshadows: { type: 'array' },
next_chapter_must_continue: { type: 'array' },
forbidden_to_forget: { type: 'array' }
}
},
temperature: 0.25,
max_output_tokens: 3500
}
] as const;
async function main() {
const adminPassword = process.env.SEED_ADMIN_PASSWORD || 'Admin123!';
const adminPasswordHash = await hash(adminPassword, 12);
@@ -84,6 +374,39 @@ async function main() {
});
}
for (const prompt of defaultAgentPrompts) {
await prisma.agentPrompt.upsert({
where: {
agent_name_version: {
agent_name: prompt.agent_name,
version: 1
}
},
update: {
provider_type: prompt.provider_type,
default_provider_code: prompt.default_provider_code ?? null,
system_prompt: prompt.system_prompt,
user_prompt_template: prompt.user_prompt_template,
output_schema_json: prompt.output_schema_json ?? Prisma.JsonNull,
temperature: prompt.temperature,
max_output_tokens: prompt.max_output_tokens,
is_active: true
},
create: {
agent_name: prompt.agent_name,
version: 1,
provider_type: prompt.provider_type,
default_provider_code: prompt.default_provider_code ?? null,
system_prompt: prompt.system_prompt,
user_prompt_template: prompt.user_prompt_template,
output_schema_json: prompt.output_schema_json ?? Prisma.JsonNull,
temperature: prompt.temperature,
max_output_tokens: prompt.max_output_tokens,
is_active: true
}
});
}
await prisma.systemConfig.upsert({
where: { config_key: 'system_a.current_stage' },
update: {