mirror of
https://github.com/linyqh/NarratoAI.git
synced 2026-07-24 06:58:19 +00:00
feat(webui, llm, i18n): 添加可自定义的解说文案字数配置
在WebUI脚本设置页面新增字数输入控件,支持100-5000区间的目标字数设置 更新LLM提示词模板,使用用户指定字数作为目标(±10%浮动)替代原固定范围 补充中英文国际化翻译文案 升级项目版本至0.8.7 更新相关服务函数与单元测试适配新参数
This commit is contained in:
parent
7639c9d289
commit
022b8bbea3
@ -409,6 +409,7 @@ class SubtitleAnalyzer:
|
||||
temperature: float = 0.7,
|
||||
narration_language: str = "简体中文(中国)",
|
||||
drama_genre: str = "逆袭/复仇",
|
||||
narration_word_count: int = 500,
|
||||
) -> Dict[str, Any]:
|
||||
"""生成供用户审核修改的解说正文。"""
|
||||
try:
|
||||
@ -420,6 +421,7 @@ class SubtitleAnalyzer:
|
||||
"plot_analysis": plot_analysis,
|
||||
"subtitle_content": subtitle_content,
|
||||
"narration_language": narration_language,
|
||||
"narration_word_count": int(narration_word_count),
|
||||
},
|
||||
)
|
||||
return self._generate_plain_text(prompt, system_prompt, temperature)
|
||||
@ -964,6 +966,7 @@ def generate_narration_copy(
|
||||
narration_language: str = "简体中文(中国)",
|
||||
drama_genre: str = "逆袭/复仇",
|
||||
prompt_category: str = "short_drama_narration",
|
||||
narration_word_count: int = 500,
|
||||
) -> Dict[str, Any]:
|
||||
"""生成可供用户审核修改的解说正文。"""
|
||||
analyzer = SubtitleAnalyzer(
|
||||
@ -982,6 +985,7 @@ def generate_narration_copy(
|
||||
temperature=temperature,
|
||||
narration_language=narration_language,
|
||||
drama_genre=drama_genre,
|
||||
narration_word_count=narration_word_count,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -292,6 +292,7 @@ class SubtitleAnalyzerAdapter:
|
||||
temperature: float = 0.7,
|
||||
narration_language: str = "简体中文(中国)",
|
||||
drama_genre: str = "逆袭/复仇",
|
||||
narration_word_count: int = 500,
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate editable narration copy before timeline matching."""
|
||||
try:
|
||||
@ -303,6 +304,7 @@ class SubtitleAnalyzerAdapter:
|
||||
"plot_analysis": plot_analysis,
|
||||
"subtitle_content": subtitle_content,
|
||||
"narration_language": narration_language,
|
||||
"narration_word_count": int(narration_word_count),
|
||||
},
|
||||
)
|
||||
narration_copy = self._generate_plain_text(prompt, system_prompt, temperature)
|
||||
|
||||
@ -24,11 +24,14 @@ class SubtitleAnalyzerAdapterPipelineTests(unittest.TestCase):
|
||||
temperature=0.7,
|
||||
narration_language="简体中文(中国)",
|
||||
drama_genre="家庭伦理",
|
||||
narration_word_count=800,
|
||||
)
|
||||
|
||||
self.assertEqual("success", result["status"])
|
||||
self.assertIn("反击", result["narration_copy"])
|
||||
self.assertIn("家庭伦理", call.call_args.kwargs["prompt"])
|
||||
self.assertIn("800", call.call_args.kwargs["prompt"])
|
||||
self.assertNotIn("300-650", call.call_args.kwargs["prompt"])
|
||||
self.assertNotIn("response_format", call.call_args.kwargs)
|
||||
|
||||
def test_generate_narration_copy_can_use_film_tv_prompt_category(self):
|
||||
@ -49,11 +52,14 @@ class SubtitleAnalyzerAdapterPipelineTests(unittest.TestCase):
|
||||
temperature=0.7,
|
||||
narration_language="简体中文(中国)",
|
||||
drama_genre="悬疑/犯罪",
|
||||
narration_word_count=1200,
|
||||
)
|
||||
|
||||
self.assertEqual("success", result["status"])
|
||||
self.assertIn("影视解说正文创作任务", call.call_args.kwargs["prompt"])
|
||||
self.assertIn("用户选择的影视类型", call.call_args.kwargs["prompt"])
|
||||
self.assertIn("1200", call.call_args.kwargs["prompt"])
|
||||
self.assertNotIn("350-750", call.call_args.kwargs["prompt"])
|
||||
self.assertNotIn("短剧解说正文创作任务", call.call_args.kwargs["prompt"])
|
||||
|
||||
def test_film_tv_script_prompts_exclude_intro_outro_and_ads(self):
|
||||
|
||||
@ -22,7 +22,14 @@ class NarrationCopyPrompt(ParameterizedPrompt):
|
||||
model_type=ModelType.TEXT,
|
||||
output_format=OutputFormat.TEXT,
|
||||
tags=["影视", "解说文案", "电影解说", "剧情承接", "用户审核"],
|
||||
parameters=["drama_name", "drama_genre", "plot_analysis", "subtitle_content", "narration_language"],
|
||||
parameters=[
|
||||
"drama_name",
|
||||
"drama_genre",
|
||||
"plot_analysis",
|
||||
"subtitle_content",
|
||||
"narration_language",
|
||||
"narration_word_count",
|
||||
],
|
||||
)
|
||||
super().__init__(metadata, required_parameters=["drama_name", "plot_analysis", "subtitle_content"])
|
||||
|
||||
@ -57,6 +64,11 @@ ${narration_language}
|
||||
${drama_genre}
|
||||
</drama_genre>
|
||||
|
||||
## 用户要求的文案字数
|
||||
<narration_word_count>
|
||||
${narration_word_count}
|
||||
</narration_word_count>
|
||||
|
||||
## 类型写作规则
|
||||
必须按用户选择的影视类型调整表达重点,不要自行改判类型:
|
||||
- 剧情/情感:突出人物选择、关系裂痕、命运压力和情绪余波。
|
||||
@ -81,7 +93,7 @@ ${drama_genre}
|
||||
4. 每句话只表达一个信息点,适合后续按句匹配画面。
|
||||
5. 句子尽量短,单句优先 15-35 字;信息复杂时拆成多句。
|
||||
6. 每 2-3 句要有明确承接,让观众知道为什么从上一幕来到下一幕。
|
||||
7. 总长度控制在 350-750 字;短素材取下限,长素材取上限。
|
||||
7. 总长度以 ${narration_word_count} 字为目标,允许上下浮动 10%;中日韩语言按非空白字符计数,其他语言按单词计数。不得再套用固定长度区间。
|
||||
8. 不要使用编号、项目符号、章节标题或括号说明。
|
||||
|
||||
## 输出要求
|
||||
|
||||
@ -22,7 +22,14 @@ class NarrationCopyPrompt(ParameterizedPrompt):
|
||||
model_type=ModelType.TEXT,
|
||||
output_format=OutputFormat.TEXT,
|
||||
tags=["短剧", "解说文案", "爆款开头", "叙事连续性", "用户审核"],
|
||||
parameters=["drama_name", "drama_genre", "plot_analysis", "subtitle_content", "narration_language"],
|
||||
parameters=[
|
||||
"drama_name",
|
||||
"drama_genre",
|
||||
"plot_analysis",
|
||||
"subtitle_content",
|
||||
"narration_language",
|
||||
"narration_word_count",
|
||||
],
|
||||
)
|
||||
super().__init__(metadata, required_parameters=["drama_name", "plot_analysis", "subtitle_content"])
|
||||
|
||||
@ -57,6 +64,11 @@ ${narration_language}
|
||||
${drama_genre}
|
||||
</drama_genre>
|
||||
|
||||
## 用户要求的文案字数
|
||||
<narration_word_count>
|
||||
${narration_word_count}
|
||||
</narration_word_count>
|
||||
|
||||
## 类型写作规则
|
||||
必须按用户选择的短剧类型调整表达重点,不要自行改判类型:
|
||||
- 霸总/甜宠:突出误会、身份差、暧昧拉扯、守护感和情绪反差。
|
||||
@ -81,7 +93,7 @@ ${drama_genre}
|
||||
4. 每句话只表达一个信息点,适合后续按句匹配画面。
|
||||
5. 句子尽量短,单句优先 15-35 字;信息复杂时拆成多句。
|
||||
6. 每 2-3 句要有明确因果承接,让观众知道为什么从上一幕来到下一幕。
|
||||
7. 总长度控制在 300-650 字;短素材取下限,长素材取上限。
|
||||
7. 总长度以 ${narration_word_count} 字为目标,允许上下浮动 10%;中日韩语言按非空白字符计数,其他语言按单词计数。不得再套用固定长度区间。
|
||||
8. 不要使用编号、项目符号、章节标题或括号说明。
|
||||
|
||||
## 输出要求
|
||||
|
||||
@ -1 +1 @@
|
||||
0.8.6
|
||||
0.8.7
|
||||
@ -97,6 +97,9 @@ FILM_TV_TYPE_VALUES = {
|
||||
"horror_thriller": "恐怖/惊悚",
|
||||
}
|
||||
SHORT_DRAMA_ORIGINAL_SOUND_RATIO_OPTIONS = list(range(0, 100, 10))
|
||||
DEFAULT_NARRATION_WORD_COUNT = 500
|
||||
MIN_NARRATION_WORD_COUNT = 100
|
||||
MAX_NARRATION_WORD_COUNT = 5000
|
||||
SUMMARY_MODE_CONFIGS = {
|
||||
MODE_FILM_SUMMARY: {
|
||||
"mode_label_key": "Film TV Narration",
|
||||
@ -1574,6 +1577,7 @@ def render_script_buttons(tr, params):
|
||||
type_option_key = _summary_state_key(summary_config, "type_option")
|
||||
custom_type_key = _summary_state_key(summary_config, "custom_type")
|
||||
original_sound_ratio_key = _summary_state_key(summary_config, "original_sound_ratio")
|
||||
narration_word_count_key = _summary_state_key(summary_config, "narration_word_count")
|
||||
language_option_key = _summary_state_key(summary_config, "narration_language_option")
|
||||
custom_language_key = _summary_state_key(summary_config, "custom_narration_language")
|
||||
narration_copy_key = _summary_state_key(summary_config, "narration_copy")
|
||||
@ -1592,7 +1596,7 @@ def render_script_buttons(tr, params):
|
||||
config_col_widths = [1.15]
|
||||
if show_custom_type:
|
||||
config_col_widths.append(1.15)
|
||||
config_col_widths.extend([0.9, 1.15])
|
||||
config_col_widths.extend([0.9, 0.9, 1.15])
|
||||
if show_custom_language:
|
||||
config_col_widths.append(1.15)
|
||||
|
||||
@ -1623,6 +1627,16 @@ def render_script_buttons(tr, params):
|
||||
key=original_sound_ratio_key,
|
||||
)
|
||||
config_col_index += 1
|
||||
with config_cols[config_col_index]:
|
||||
st.number_input(
|
||||
tr("文案字数"),
|
||||
min_value=MIN_NARRATION_WORD_COUNT,
|
||||
max_value=MAX_NARRATION_WORD_COUNT,
|
||||
value=DEFAULT_NARRATION_WORD_COUNT,
|
||||
step=50,
|
||||
key=narration_word_count_key,
|
||||
)
|
||||
config_col_index += 1
|
||||
with config_cols[config_col_index]:
|
||||
st.selectbox(
|
||||
tr("解说语言"),
|
||||
@ -1663,6 +1677,7 @@ def render_script_buttons(tr, params):
|
||||
type_option_key = _summary_state_key(summary_config, "type_option")
|
||||
custom_type_key = _summary_state_key(summary_config, "custom_type")
|
||||
original_sound_ratio_key = _summary_state_key(summary_config, "original_sound_ratio")
|
||||
narration_word_count_key = _summary_state_key(summary_config, "narration_word_count")
|
||||
language_option_key = _summary_state_key(summary_config, "narration_language_option")
|
||||
custom_language_key = _summary_state_key(summary_config, "custom_narration_language")
|
||||
narration_copy_key = _summary_state_key(summary_config, "narration_copy")
|
||||
@ -1674,6 +1689,9 @@ def render_script_buttons(tr, params):
|
||||
narration_language = _resolve_summary_narration_language(summary_config)
|
||||
drama_genre = _resolve_summary_type(summary_config)
|
||||
original_sound_ratio = int(st.session_state.get(original_sound_ratio_key, 30))
|
||||
narration_word_count = int(
|
||||
st.session_state.get(narration_word_count_key, DEFAULT_NARRATION_WORD_COUNT)
|
||||
)
|
||||
if (
|
||||
st.session_state.get(type_option_key) == "custom"
|
||||
and not str(st.session_state.get(custom_type_key, '') or '').strip()
|
||||
@ -1720,6 +1738,7 @@ def render_script_buttons(tr, params):
|
||||
video_paths=_selected_video_paths(),
|
||||
narration_language=narration_language,
|
||||
drama_genre=drama_genre,
|
||||
narration_word_count=narration_word_count,
|
||||
prompt_category=summary_config["prompt_category"],
|
||||
search_keywords=summary_config["search_keywords"],
|
||||
empty_title_message_key=summary_config["empty_title_message_key"],
|
||||
|
||||
@ -341,6 +341,7 @@
|
||||
"影视类型": "Film/TV Type",
|
||||
"自定义影视类型": "Custom Film/TV Type",
|
||||
"原片占比": "Original Footage Ratio",
|
||||
"文案字数": "Copy Length",
|
||||
"例如:豪门虐恋": "For example: billionaire angst romance",
|
||||
"例如:悬疑犯罪": "For example: suspense crime",
|
||||
"请输入自定义短剧类型": "Please enter a custom short drama type",
|
||||
|
||||
@ -703,6 +703,7 @@
|
||||
"影视类型": "影视类型",
|
||||
"自定义影视类型": "自定义影视类型",
|
||||
"原片占比": "原片占比",
|
||||
"文案字数": "文案字数",
|
||||
"例如:豪门虐恋": "例如:豪门虐恋",
|
||||
"例如:悬疑犯罪": "例如:悬疑犯罪",
|
||||
"请输入自定义短剧类型": "请输入自定义短剧类型",
|
||||
|
||||
@ -363,6 +363,7 @@ def generate_short_drama_narration_copy(
|
||||
search_keywords: str = SHORT_DRAMA_SEARCH_KEYWORDS,
|
||||
empty_title_message_key: str = "Please enter short drama name before web search",
|
||||
web_search_context_description: str = "短剧名称、人物关系、剧情背景和公开剧情梗概",
|
||||
narration_word_count: int = 500,
|
||||
):
|
||||
"""生成可由用户审核修改的短剧解说正文,不绑定时间戳。"""
|
||||
subtitle_paths = _normalize_paths(subtitle_path)
|
||||
@ -422,6 +423,7 @@ def generate_short_drama_narration_copy(
|
||||
temperature=temperature,
|
||||
narration_language=narration_language,
|
||||
drama_genre=drama_genre,
|
||||
narration_word_count=narration_word_count,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"使用新LLM服务生成文案失败,回退到旧实现: {str(e)}")
|
||||
@ -436,6 +438,7 @@ def generate_short_drama_narration_copy(
|
||||
provider=text_provider,
|
||||
narration_language=narration_language,
|
||||
drama_genre=drama_genre,
|
||||
narration_word_count=narration_word_count,
|
||||
prompt_category=prompt_category,
|
||||
)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user