fix(core): 脚本 agent 再兼容 lines/caption 变体,补全旁白漏空

豆包不同型号吐不同 schema:Lite 用 dialogue(字符串)+subtitle;Pro 用
lines:[{role,content}]+caption。上一版只认 dialogue/subtitle,导致 Pro 变体
画面能填、旁白仍空。normalize_draft 再扩容:
- 口播来源并入 lines(每项 content/line/text)、voiceover;subtitle/caption 兜底
- 画面来源并入 visual_description
新增 lines/caption 变体回归用例,全套 22 测试通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-17 10:24:28 +08:00
co-authored by Claude Opus 4.8
parent a7f1b0da1e
commit 0280c2e192
2 changed files with 37 additions and 16 deletions
+17 -16
View File
@@ -288,28 +288,29 @@ def normalize_draft(raw_text: str, *, aspect_ratio: str, total_duration: int) ->
speaker = speaker if (speaker in valid_ids) else None
refs = [r for r in (seg.get("entity_refs") or []) if r in valid_ids]
# 对白(剧情向):[{speaker(合法 entity id 或 null=旁白), line}];默认空 = 纯口播。
# 模型变体里 dialogue 可能是字符串(整句口播)而非数组——只在数组时按结构解析
# 模型变体的口播字段五花八门:dialogue(字符串/数组)/ lines(数组)/ 每项 line|text|content
# 这里把任意数组形态归一成结构化对白,字符串形态留给下面当整句旁白。
raw_dialogue = seg.get("dialogue")
dialogue = []
if isinstance(raw_dialogue, list):
for d in raw_dialogue:
if not isinstance(d, dict):
continue
line = (d.get("line") or d.get("text") or "").strip()
if not line:
continue
sp = d.get("speaker")
dialogue.append({"speaker": sp if sp in valid_ids else None, "line": line})
# 旁白:narration > 字符串 dialogue(整句口播)> subtitle(字幕)> 结构化对白拼接
narration = (seg.get("narration") or "").strip()
dialogue_array = raw_dialogue if isinstance(raw_dialogue, list) else (seg.get("lines") if isinstance(seg.get("lines"), list) else [])
for d in dialogue_array:
if not isinstance(d, dict):
continue
line = (d.get("line") or d.get("text") or d.get("content") or "").strip()
if not line:
continue
sp = d.get("speaker")
dialogue.append({"speaker": sp if sp in valid_ids else None, "line": line})
# 旁白:narration > 字符串 dialogue(整句口播)> 结构化对白/lines 拼接 > subtitle/caption(字幕兜底)
narration = (seg.get("narration") or seg.get("voiceover") or "").strip()
if not narration and isinstance(raw_dialogue, str):
narration = raw_dialogue.strip()
if not narration:
narration = (seg.get("subtitle") or "").strip()
if not narration and dialogue:
narration = " ".join(d["line"] for d in dialogue) # 扁平拼接,兼容下游字幕/配音
# 画面:visual > visual_prompt > scene(模型变体常用 scene 描述画面)
visual = (seg.get("visual") or seg.get("visual_prompt") or seg.get("scene") or "").strip()
if not narration:
narration = (seg.get("subtitle") or seg.get("caption") or "").strip()
# 画面:visual > visual_prompt > scene > shot/visual_description(模型变体常用 scene 描述画面)
visual = (seg.get("visual") or seg.get("visual_prompt") or seg.get("scene") or seg.get("visual_description") or "").strip()
norm_segments.append(
{
"index": i,