fix: [bug] step2 IR extraction 生成缺少 section 字段的 source - 来自 #18 e2e - Closes #53 #55

Merged
pzhang_zywl merged 1 commits from dev/issue-53-fix-source-section into main 2026-06-02 15:47:51 +08:00
2 changed files with 67 additions and 0 deletions
@@ -169,6 +169,27 @@ def _normalize_rule(rule: dict) -> dict:
"value": "active"
}]
# Ensure table/text sources have a section field (defensive against LLM omission)
sources = rule.get("sources", [])
if sources:
# try to infer a default section from sibling sources or the rule path
default_section = ""
for s in sources:
sec = s.get("section", "")
if sec and sec.strip():
default_section = sec.strip()
break
if not default_section:
path = rule.get("path", "")
if path:
default_section = path.split(" > ")[0] if " > " in path else path
for src in sources:
stype = src.get("type", "")
if stype in ("table", "text"):
if not src.get("section"):
src["section"] = default_section
return rule
@@ -465,3 +465,49 @@ class TestNormalizeRule:
normalized = _normalize_rule(rule)
assert normalized["trigger"]["operator"] == "AND"
assert normalized["trigger"]["conditions"][0]["operator"] == ">="
def test_normalize_source_missing_section_from_sibling(self):
"""Table/text sources without section get it from sibling sources."""
rule = {
"trigger": {"conditions": [{"signal": "x", "operator": "==", "value": "1"}]},
"sources": [
{"type": "table", "section": "3.1.1 系统限制", "row": 1},
{"type": "text", "text_snippet": "missing section"},
],
}
normalized = _normalize_rule(rule)
assert normalized["sources"][1]["section"] == "3.1.1 系统限制"
def test_normalize_source_missing_section_from_path(self):
"""Table/text sources without section and no sibling fall back to rule path."""
rule = {
"trigger": {"conditions": [{"signal": "x", "operator": "==", "value": "1"}]},
"path": "4.2 关闭流程 > decision_speed > action_disable",
"sources": [
{"type": "table", "row": 3, "text_snippet": "no section anywhere"},
],
}
normalized = _normalize_rule(rule)
assert normalized["sources"][0]["section"] == "4.2 关闭流程"
def test_normalize_source_keeps_existing_section(self):
"""Sources that already have section are not modified."""
rule = {
"trigger": {"conditions": [{"signal": "x", "operator": "==", "value": "1"}]},
"sources": [
{"type": "table", "section": "1.0 概述", "row": 1},
],
}
normalized = _normalize_rule(rule)
assert normalized["sources"][0]["section"] == "1.0 概述"
def test_normalize_source_skips_logic_tree(self):
"""Logic tree sources are not touched (don't need section)."""
rule = {
"trigger": {"conditions": [{"signal": "x", "operator": "==", "value": "1"}]},
"sources": [
{"type": "logic_tree", "image_id": "img1", "node_ids": ["n1"]},
],
}
normalized = _normalize_rule(rule)
assert "section" not in normalized["sources"][0]