from __future__ import annotations from decode_content import gates from decode_content.framing import normalize_knowledge_shapes from decode_content.scoping import apply_scopes, split_scope_items, strip_verb_tail def test_creation_gate_uses_admit_refute_tiebreak(): calls = [] def fake_chat(system, user, timeout=None): calls.append(system) if system == gates.GATE_ADMIT: return {"in_scope": True} if system == gates.GATE_REFUTE: return {"out_of_scope": True} if system == gates.GATE_TIEBREAK: return {"in_scope": False} raise AssertionError(system) result = gates.creation_gate("读懂内容", chat_json_fn=fake_chat) assert result.passed is False assert "裁决=False" in result.reason assert calls == [gates.GATE_ADMIT, gates.GATE_REFUTE, gates.GATE_TIEBREAK] def test_normalize_knowledge_shapes_guards_stage_kind_and_parent(): knowledges = [ { "id": "w1", "type": "what", "role": "组件", "parent": {"how_id": "missing", "step": 1}, "title": "构成", "kind": "集合", "业务阶段": ["脚本", "错误"], }, { "id": "h1", "type": "how", "title": "流程", "业务阶段": ["选题"], "steps": [{"创作阶段": "定稿"}], }, ] out = normalize_knowledge_shapes(knowledges) assert out[0]["kind"] == "子集" assert out[0]["业务阶段"] == ["脚本"] assert out[0]["role"] == "主" assert out[0]["parent"] is None assert out[1]["steps"][0]["创作阶段"] is None def test_scoping_split_strip_and_apply_without_linker(): assert strip_verb_tail("寻找爆款选题") == "爆款选题" assert split_scope_items([{"scope_type": "form", "value": "脚本结构与标题结构"}]) == [ {"scope_type": "form", "value": "脚本结构"}, {"scope_type": "form", "value": "标题结构"}, ] knowledges = [{"id": "h1", "type": "how", "steps": [{"id": "s1"}]}] apply_scopes( knowledges, [ { "knowledge_id": "h1", "step_id": "s1", "items": [{"scope_type": "intent", "value": "吸引注意"}], } ], linker=None, ) assert knowledges[0]["steps"][0]["作用域"][0]["link"] == "候选"