"""Airspeed Velocity benchmarks for decision correction flows. Measures BFS subtree traversal, impact analysis, dry-run report generation, and revert execution overhead across varying tree sizes. """ from __future__ import annotations from cleveragents.application.services.correction_service import CorrectionService from cleveragents.domain.models.core.correction import ( CorrectionMode, CorrectionStatus, ) # --------------------------------------------------------------------------- # Tree generators # --------------------------------------------------------------------------- _PLAN_ID = "01HGZ6FE0AQDYTR4BXVQZ6EA00" def _linear_tree(root: str, depth: int) -> dict[str, list[str]]: """Build a linear chain of *depth* nodes starting at *root*.""" tree: dict[str, list[str]] = {} current = root for i in range(1, depth): child = f"{root}_c{i}" tree[current] = [child] current = child return tree def _binary_tree(root: str, depth: int) -> dict[str, list[str]]: """Build a complete binary tree of *depth* levels rooted at *root*.""" tree: dict[str, list[str]] = {} level = [root] counter = 0 for _ in range(depth - 1): next_level: list[str] = [] for node in level: left = f"{root}_n{counter}" right = f"{root}_n{counter + 1}" counter += 2 tree[node] = [left, right] next_level.extend([left, right]) level = next_level return tree # --------------------------------------------------------------------------- # BFS traversal benchmarks # --------------------------------------------------------------------------- class BfsTraversalSuite: """Benchmark BFS subtree computation at varying tree sizes.""" def setup(self) -> None: """Prepare trees and service instances.""" self.svc = CorrectionService() self.small_tree = _linear_tree("S", 5) # 5 nodes self.medium_tree = _binary_tree("M", 5) # 31 nodes self.large_tree = _binary_tree("L", 8) # 255 nodes def time_bfs_small_tree(self) -> None: """BFS on a 5-node linear chain.""" self.svc._compute_affected_subtree("S", self.small_tree) def time_bfs_medium_tree(self) -> None: """BFS on a 31-node binary tree (depth 5).""" self.svc._compute_affected_subtree("M", self.medium_tree) def time_bfs_large_tree(self) -> None: """BFS on a 255-node binary tree (depth 8).""" self.svc._compute_affected_subtree("L", self.large_tree) # --------------------------------------------------------------------------- # Impact analysis benchmarks # --------------------------------------------------------------------------- class ImpactAnalysisSuite: """Benchmark full impact analysis including risk classification.""" def setup(self) -> None: """Prepare service and correction requests.""" self.svc = CorrectionService() self.small_tree = _linear_tree("S", 3) self.medium_tree = _binary_tree("M", 4) self.large_tree = _binary_tree("L", 6) self.req_small = self.svc.request_correction( _PLAN_ID, "S", CorrectionMode.REVERT ) self.req_medium = self.svc.request_correction( _PLAN_ID, "M", CorrectionMode.REVERT ) self.req_large = self.svc.request_correction( _PLAN_ID, "L", CorrectionMode.REVERT ) def time_impact_small(self) -> None: """Impact analysis on a 3-node chain.""" # Reset status so analyze_impact can run again self.svc._corrections[ self.req_small.correction_id ].status = CorrectionStatus.PENDING self.svc.analyze_impact(self.req_small.correction_id, self.small_tree) def time_impact_medium(self) -> None: """Impact analysis on a 15-node binary tree.""" self.svc._corrections[ self.req_medium.correction_id ].status = CorrectionStatus.PENDING self.svc.analyze_impact(self.req_medium.correction_id, self.medium_tree) def time_impact_large(self) -> None: """Impact analysis on a 63-node binary tree.""" self.svc._corrections[ self.req_large.correction_id ].status = CorrectionStatus.PENDING self.svc.analyze_impact(self.req_large.correction_id, self.large_tree) # --------------------------------------------------------------------------- # Dry-run report benchmarks # --------------------------------------------------------------------------- class DryRunReportSuite: """Benchmark dry-run report generation.""" def setup(self) -> None: """Prepare service and requests.""" self.svc = CorrectionService() self.tree = _binary_tree("DR", 5) self.req = self.svc.request_correction(_PLAN_ID, "DR", CorrectionMode.REVERT) def time_dry_run_report(self) -> None: """Generate dry-run report for a 31-node tree.""" self.svc._corrections[self.req.correction_id].status = CorrectionStatus.PENDING self.svc.generate_dry_run_report(self.req.correction_id, self.tree) # --------------------------------------------------------------------------- # Revert execution benchmarks # --------------------------------------------------------------------------- class RevertExecutionSuite: """Benchmark revert execution overhead.""" def _fresh_request(self, target: str) -> str: """Create a fresh correction request and return its ID.""" req = self.svc.request_correction(_PLAN_ID, target, CorrectionMode.REVERT) return req.correction_id def setup(self) -> None: """Prepare service and trees.""" self.svc = CorrectionService() self.small_tree = _linear_tree("RS", 3) self.medium_tree = _binary_tree("RM", 4) def time_revert_small(self) -> None: """Revert execution on a 3-node chain.""" cid = self._fresh_request("RS") self.svc.execute_revert(cid, self.small_tree) def time_revert_medium(self) -> None: """Revert execution on a 15-node binary tree.""" cid = self._fresh_request("RM") self.svc.execute_revert(cid, self.medium_tree)