feat(detector): 扩展空字段检测到 L2 event_description / L1 scene_summary
断点续跑判据需要 L2/L1 层的 empty_field 检测。零 LLM 成本。
This commit is contained in:
@@ -42,8 +42,10 @@ def detect_issues(
|
|||||||
检查项:
|
检查项:
|
||||||
- L3: card 必填字段为空(frame_summary / visible_entities / ongoing_actions / spatial_layout)
|
- L3: card 必填字段为空(frame_summary / visible_entities / ongoing_actions / spatial_layout)
|
||||||
- L3: frame_path 对应文件不存在(需提供 frames_dir)
|
- L3: frame_path 对应文件不存在(需提供 frames_dir)
|
||||||
|
- L2: event_description 为空
|
||||||
- L2/L1: children 列表为空
|
- L2/L1: children 列表为空
|
||||||
- L2: 相邻 clips 时间范围不连续(gap > 1秒)
|
- L2: 相邻 clips 时间范围不连续(gap > 1秒)
|
||||||
|
- L1: scene_summary 为空
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
index: 待检测的 TreeIndex。
|
index: 待检测的 TreeIndex。
|
||||||
@@ -55,6 +57,17 @@ def detect_issues(
|
|||||||
issues: list[NodeIssue] = []
|
issues: list[NodeIssue] = []
|
||||||
|
|
||||||
for l1 in index.roots:
|
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 不为空
|
# L1: children 不为空
|
||||||
if not l1.children:
|
if not l1.children:
|
||||||
issues.append(
|
issues.append(
|
||||||
@@ -71,6 +84,17 @@ def detect_issues(
|
|||||||
_check_time_gaps(l1.children, issues)
|
_check_time_gaps(l1.children, issues)
|
||||||
|
|
||||||
for l2 in l1.children:
|
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 不为空
|
# L2: children 不为空
|
||||||
if not l2.children:
|
if not l2.children:
|
||||||
issues.append(
|
issues.append(
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from app.tree.index import (
|
|||||||
L3Node,
|
L3Node,
|
||||||
TreeIndex,
|
TreeIndex,
|
||||||
)
|
)
|
||||||
from app.tree.repair.detector import detect_issues
|
from app.tree.repair.detector import NodeIssue, detect_issues
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -185,3 +185,53 @@ class TestDetectIssues:
|
|||||||
index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1])
|
index = TreeIndex(metadata=IndexMeta("/t.mp4", "video"), roots=[l1])
|
||||||
issues = detect_issues(index)
|
issues = detect_issues(index)
|
||||||
assert any(i.issue_type == "time_gap" for i in issues)
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user