From 8182cb86b1791f15b7da736d72d3e6238c60b5a1 Mon Sep 17 00:00:00 2001 From: iomgaa Date: Thu, 9 Jul 2026 00:12:55 -0400 Subject: [PATCH] =?UTF-8?q?feat(detector):=20=E6=89=A9=E5=B1=95=E7=A9=BA?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=A3=80=E6=B5=8B=E5=88=B0=20L2=20event=5Fde?= =?UTF-8?q?scription=20/=20L1=20scene=5Fsummary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 断点续跑判据需要 L2/L1 层的 empty_field 检测。零 LLM 成本。 --- app/tree/repair/detector.py | 24 ++++++++++++++ tests/unit/test_repair_detector.py | 52 +++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/app/tree/repair/detector.py b/app/tree/repair/detector.py index ce62d99..aba8bb6 100644 --- a/app/tree/repair/detector.py +++ b/app/tree/repair/detector.py @@ -42,8 +42,10 @@ def detect_issues( 检查项: - L3: card 必填字段为空(frame_summary / visible_entities / ongoing_actions / spatial_layout) - L3: frame_path 对应文件不存在(需提供 frames_dir) + - L2: event_description 为空 - L2/L1: children 列表为空 - L2: 相邻 clips 时间范围不连续(gap > 1秒) + - L1: scene_summary 为空 参数: index: 待检测的 TreeIndex。 @@ -55,6 +57,17 @@ def detect_issues( issues: list[NodeIssue] = [] for l1 in index.roots: + # L1: scene_summary 不为空 + if not l1.card.scene_summary: + issues.append( + NodeIssue( + node_id=l1.id, + level=1, + issue_type="empty_field", + details="L1 节点字段为空: scene_summary", + ) + ) + # L1: children 不为空 if not l1.children: issues.append( @@ -71,6 +84,17 @@ def detect_issues( _check_time_gaps(l1.children, issues) for l2 in l1.children: + # L2: event_description 不为空 + if not l2.card.event_description: + issues.append( + NodeIssue( + node_id=l2.id, + level=2, + issue_type="empty_field", + details="L2 节点字段为空: event_description", + ) + ) + # L2: children 不为空 if not l2.children: issues.append( diff --git a/tests/unit/test_repair_detector.py b/tests/unit/test_repair_detector.py index 50bb7b0..1780255 100644 --- a/tests/unit/test_repair_detector.py +++ b/tests/unit/test_repair_detector.py @@ -14,7 +14,7 @@ from app.tree.index import ( L3Node, TreeIndex, ) -from app.tree.repair.detector import detect_issues +from app.tree.repair.detector import NodeIssue, detect_issues if TYPE_CHECKING: from pathlib import Path @@ -185,3 +185,53 @@ class TestDetectIssues: index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1]) issues = detect_issues(index) assert any(i.issue_type == "time_gap" for i in issues) + + def test_detects_l2_empty_event_description(self) -> None: + """L2 event_description 为空应报 empty_field。""" + l3 = L3Node( + id="l1_0_l2_0_l3_0", + card=L3Card("正常帧", ["实体"], ["动作"], [], "居中", {}), + timestamp=1.0, + ) + l2 = L2Node( + id="l1_0_l2_0", + card=L2Card("", [], [], [], [], "", None), + time_range=(0.0, 10.0), + children=[l3], + ) + l1 = L1Node( + id="l1_0", + card=L1Card("正常场景", "", [], [], [], [], ""), + time_range=(0.0, 10.0), + children=[l2], + ) + index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1]) + issues = detect_issues(index) + l2_empties = [i for i in issues if i.level == 2 and i.issue_type == "empty_field"] + assert len(l2_empties) == 1 + assert "event_description" in l2_empties[0].details + + def test_detects_l1_empty_scene_summary(self) -> None: + """L1 scene_summary 为空应报 empty_field。""" + l3 = L3Node( + id="l1_0_l2_0_l3_0", + card=L3Card("正常帧", ["实体"], ["动作"], [], "居中", {}), + timestamp=1.0, + ) + l2 = L2Node( + id="l1_0_l2_0", + card=L2Card("正常事件", [], [], [], [], "", None), + time_range=(0.0, 10.0), + children=[l3], + ) + l1 = L1Node( + id="l1_0", + card=L1Card("", "", [], [], [], [], ""), + time_range=(0.0, 10.0), + children=[l2], + ) + index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1]) + issues = detect_issues(index) + l1_empties = [i for i in issues if i.level == 1 and i.issue_type == "empty_field"] + assert len(l1_empties) == 1 + assert "scene_summary" in l1_empties[0].details