"""Robot Framework helper for ACMS pipeline Phase 2 integration tests. Provides a CLI-style interface for Robot to invoke Phase 2 pipeline components and verify the results. Exit code 0 = success, 1 = failure. Usage: python robot/helper_acms_pipeline_phase2.py dedup-basic python robot/helper_acms_pipeline_phase2.py depth-resolve python robot/helper_acms_pipeline_phase2.py scorer-default python robot/helper_acms_pipeline_phase2.py packer-budget python robot/helper_acms_pipeline_phase2.py scored-fragment python robot/helper_acms_pipeline_phase2.py pipeline-integration """ from __future__ import annotations import sys from collections.abc import Callable from pathlib import Path _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) from cleveragents.application.services.acms_phase2 import ( # noqa: E402 ContentHashDeduplicator, GreedyKnapsackPacker, MaxDepthResolver, WeightedCompositeScorer, ) from cleveragents.application.services.acms_service import ACMSPipeline # noqa: E402 from cleveragents.domain.models.core.context_fragment import ( # noqa: E402 ContextBudget, ContextFragment, FragmentProvenance, ScoredFragment, ) _DEFAULT_PROV = FragmentProvenance(resource_uri="test://robot-phase2") _PLAN_ID = "01JQTESTPN00000000000000CC" def _frag( uko_node: str = "test://default", content: str = "test content", score: float = 0.5, tokens: int = 10, depth: int = 0, ) -> ContextFragment: return ContextFragment( uko_node=uko_node, content=content, relevance_score=score, token_count=tokens, detail_depth=depth, provenance=_DEFAULT_PROV, ) def _cmd_dedup_basic() -> int: """Deduplicate identical fragments and keep highest scored.""" dedup = ContentHashDeduplicator() fragments = [ _frag(uko_node="project://a.py", content="hello", score=0.5), _frag(uko_node="project://a.py", content="hello", score=0.9), _frag(uko_node="project://b.py", content="hello", score=0.7), ] result = dedup.deduplicate(fragments) assert len(result) == 2, f"Expected 2, got {len(result)}" # Find the fragment for a.py — should be the higher-scored one a_frags = [f for f in result if f.uko_node == "project://a.py"] assert len(a_frags) == 1 assert a_frags[0].relevance_score == 0.9 print("phase2-dedup-ok") return 0 def _cmd_depth_resolve() -> int: """Resolve depth conflicts to max depth per node.""" resolver = MaxDepthResolver() fragments = [ _frag(uko_node="project://a.py", content="brief", depth=2, tokens=10), _frag(uko_node="project://a.py", content="full", depth=9, tokens=100), _frag(uko_node="project://b.py", content="b", depth=5, tokens=50), ] result = resolver.resolve(fragments) assert len(result) == 2, f"Expected 2, got {len(result)}" a_frags = [f for f in result if f.uko_node == "project://a.py"] assert a_frags[0].detail_depth == 9 print("phase2-depth-ok") return 0 def _cmd_scorer_default() -> int: """Score fragments with default weights.""" scorer = WeightedCompositeScorer() fragments = [ _frag(uko_node="project://a.py", content="hello", score=0.8), _frag(uko_node="project://b.py", content="world", score=0.4), ] result = scorer.score(fragments) assert len(result) == 2 for f in result: assert 0.0 <= f.relevance_score <= 1.0 assert "_original_relevance" in f.metadata print("phase2-scorer-ok") return 0 def _cmd_packer_budget() -> int: """Pack fragments into budget.""" packer = GreedyKnapsackPacker() fragments = [ _frag(uko_node="project://a.py", content="alpha", score=0.9, tokens=100), _frag(uko_node="project://b.py", content="beta", score=0.7, tokens=100), _frag(uko_node="project://c.py", content="gamma", score=0.5, tokens=100), ] budget = ContextBudget(max_tokens=250, reserved_tokens=0) result = packer.pack(fragments, budget) assert len(result) == 2, f"Expected 2, got {len(result)}" total = sum(f.token_count for f in result) assert total <= 250, f"Total {total} exceeds budget 250" print("phase2-packer-ok") return 0 def _cmd_scored_fragment() -> int: """Create and verify ScoredFragment model.""" frag = _frag(uko_node="project://a.py", content="test", score=0.8) scored = ScoredFragment( fragment=frag, composite_score=0.85, score_components={"relevance": 0.9, "hierarchy": 0.8}, ) assert scored.composite_score == 0.85 assert scored.score_components["relevance"] == 0.9 assert scored.fragment is frag print("phase2-scored-fragment-ok") return 0 def _cmd_pipeline_integration() -> int: """Inject Phase 2 components into ACMSPipeline.""" pipeline = ACMSPipeline( deduplicator=ContentHashDeduplicator(), depth_resolver=MaxDepthResolver(), scorer=WeightedCompositeScorer(), packer=GreedyKnapsackPacker(), ) fragments = [ _frag(uko_node="project://a.py", content="hello", score=0.9, tokens=50), _frag(uko_node="project://a.py", content="hello", score=0.5, tokens=50), _frag(uko_node="project://b.py", content="world", score=0.7, tokens=50), ] budget = ContextBudget(max_tokens=200, reserved_tokens=0) payload = pipeline.assemble( plan_id=_PLAN_ID, fragments=fragments, budget=budget, ) assert len(payload.fragments) >= 1 assert payload.total_tokens <= 200 print("phase2-pipeline-ok") return 0 _COMMANDS: dict[str, Callable[[], int]] = { "dedup-basic": _cmd_dedup_basic, "depth-resolve": _cmd_depth_resolve, "scorer-default": _cmd_scorer_default, "packer-budget": _cmd_packer_budget, "scored-fragment": _cmd_scored_fragment, "pipeline-integration": _cmd_pipeline_integration, } if __name__ == "__main__": if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS: print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>", file=sys.stderr) sys.exit(1) sys.exit(_COMMANDS[sys.argv[1]]())