refactor(tree): subtitle 迁入 L3Card/L2Card + 建树管线修正
- L3Card/L2Card 新增 subtitle: str 字段(L1Card 不加) - L3Node 移除 subtitle 字段(数据迁入 Card) - assign_subtitles_voronoi 改写 Card.subtitle + L2 聚合 - _collect_card_strings 增加 skip_fields 排除 subtitle - _node_full_text/_node_anchored_text 保持 字幕:/[sN] 语义 - get_subtitle 读 Card.subtitle(L2/L3) - verify.py/synthesizer.py: l3.subtitle → l3.card.subtitle - 迁移脚本 tools/migrate_subtitle_to_card.py(幂等,300 棵树已迁移) - 9→6 个测试文件适配(3 个无需改动) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+10
-4
@@ -82,6 +82,7 @@ class L3Card:
|
||||
visible_text: 画面中可见的文字列表。
|
||||
spatial_layout: 空间布局描述。
|
||||
visual_attributes: 视觉属性字典(如光照、色调等)。
|
||||
subtitle: 字幕文本(Voronoi 分配后填充,默认空)。
|
||||
"""
|
||||
|
||||
frame_summary: str
|
||||
@@ -90,6 +91,7 @@ class L3Card:
|
||||
visible_text: list[str]
|
||||
spatial_layout: str
|
||||
visual_attributes: dict[str, Any]
|
||||
subtitle: str = ""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -106,6 +108,7 @@ class L2Card:
|
||||
visible_text: 片段中可见的文字列表。
|
||||
spatial_relations: 空间关系描述。
|
||||
state_changes: 状态变化描述(可选)。
|
||||
subtitle: 子 L3 字幕聚合文本(Voronoi 分配后填充,默认空)。
|
||||
"""
|
||||
|
||||
event_description: str
|
||||
@@ -115,6 +118,7 @@ class L2Card:
|
||||
visible_text: list[str]
|
||||
spatial_relations: str
|
||||
state_changes: str | None
|
||||
subtitle: str = ""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -183,7 +187,6 @@ class L3Node:
|
||||
embedding: 文本嵌入向量,形状 [D],float32。
|
||||
timestamp: 对应的时间戳(秒,可选)。
|
||||
frame_path: 关联的帧图像路径(可选,仅视频模态)。
|
||||
subtitle: 该帧对应的字幕文本(可选)。
|
||||
"""
|
||||
|
||||
id: str
|
||||
@@ -191,7 +194,6 @@ class L3Node:
|
||||
embedding: np.ndarray | None = None
|
||||
timestamp: float | None = None
|
||||
frame_path: str | None = None
|
||||
subtitle: str | None = None
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
@@ -274,10 +276,10 @@ class L1Node:
|
||||
"visible_text": n.card.visible_text,
|
||||
"spatial_layout": n.card.spatial_layout,
|
||||
"visual_attributes": n.card.visual_attributes,
|
||||
"subtitle": n.card.subtitle,
|
||||
},
|
||||
"timestamp": n.timestamp,
|
||||
"frame_path": n.frame_path,
|
||||
"subtitle": n.subtitle,
|
||||
}
|
||||
if include_embedding:
|
||||
d["embedding"] = _embed_to_str(n.embedding)
|
||||
@@ -294,6 +296,7 @@ class L1Node:
|
||||
"visible_text": n.card.visible_text,
|
||||
"spatial_relations": n.card.spatial_relations,
|
||||
"state_changes": n.card.state_changes,
|
||||
"subtitle": n.card.subtitle,
|
||||
},
|
||||
"time_range": list(n.time_range) if n.time_range else None,
|
||||
"children": [l3_to_dict(c) for c in n.children],
|
||||
@@ -334,6 +337,8 @@ class L1Node:
|
||||
for l2d in d.get("children", []):
|
||||
l3_nodes: list[L3Node] = []
|
||||
for l3d in l2d.get("children", []):
|
||||
# 向后兼容:旧格式 subtitle 在节点级,新格式在 card 内
|
||||
l3_subtitle = l3d["card"].get("subtitle", "") or l3d.get("subtitle", "") or ""
|
||||
l3_card = L3Card(
|
||||
frame_summary=l3d["card"]["frame_summary"],
|
||||
visible_entities=l3d["card"]["visible_entities"],
|
||||
@@ -341,6 +346,7 @@ class L1Node:
|
||||
visible_text=l3d["card"]["visible_text"],
|
||||
spatial_layout=l3d["card"]["spatial_layout"],
|
||||
visual_attributes=l3d["card"]["visual_attributes"],
|
||||
subtitle=l3_subtitle,
|
||||
)
|
||||
l3_nodes.append(
|
||||
L3Node(
|
||||
@@ -349,7 +355,6 @@ class L1Node:
|
||||
embedding=_embed_from_str(l3d.get("embedding")),
|
||||
timestamp=l3d.get("timestamp"),
|
||||
frame_path=l3d.get("frame_path"),
|
||||
subtitle=l3d.get("subtitle"),
|
||||
)
|
||||
)
|
||||
l2_card = L2Card(
|
||||
@@ -360,6 +365,7 @@ class L1Node:
|
||||
visible_text=l2d["card"]["visible_text"],
|
||||
spatial_relations=l2d["card"]["spatial_relations"],
|
||||
state_changes=l2d["card"]["state_changes"],
|
||||
subtitle=l2d["card"].get("subtitle", ""),
|
||||
)
|
||||
tr2 = l2d.get("time_range")
|
||||
l2_nodes.append(
|
||||
|
||||
Reference in New Issue
Block a user