Files
cleveragents-core/benchmarks/cross_plan_correction_bench.py
T
freemo 583e6b7ea2
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 17s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 54s
CI / unit_tests (pull_request) Successful in 2m39s
CI / integration_tests (pull_request) Successful in 3m10s
CI / docker (pull_request) Successful in 40s
CI / coverage (pull_request) Successful in 5m0s
CI / lint (push) Successful in 14s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 16s
CI / security (push) Successful in 34s
CI / typecheck (push) Successful in 35s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m23s
CI / docker (push) Successful in 38s
CI / integration_tests (push) Successful in 3m8s
CI / coverage (push) Successful in 5m10s
CI / benchmark-publish (push) Successful in 16m26s
CI / benchmark-regression (pull_request) Successful in 30m12s
feat(correcting-plans): implement Predictive Error Prevention (Layer 4) with Error Pattern Database
Implement spec-mandated Layer 4 Predictive Error Prevention system:

- ErrorPattern domain model with pattern text, historical failures,
  preventive checks, frequency tracking, and keyword-based matching.
- ErrorPatternRepository with in-memory CRUD + context-matching query.
- ErrorPatternService with record_failure(), match_patterns(), and
  get_statistics() methods.
- Wire into plan execution via plan_lifecycle_service pre-execution hook.
- Add error pattern statistics to CLI diagnostics output.

Behave BDD: 11 scenarios covering recording, matching, formatting, stats.
Robot Framework: 3 integration smoke tests.
ASV benchmarks: pattern matching performance.

ISSUES CLOSED: #571
2026-03-08 21:53:21 -04:00

159 lines
5.1 KiB
Python

"""ASV benchmarks for cross-plan correction cascading performance.
Measures cascade throughput with varying numbers of child plans in
different states: cancel-only, cancel-with-rollback, and mixed states.
"""
from __future__ import annotations
import importlib
import sys
from pathlib import Path
from typing import ClassVar
# Ensure the local *source* tree is importable even when ASV has an
# older build of the package installed.
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
import cleveragents # noqa: E402
importlib.reload(cleveragents)
from cleveragents.application.services.cross_plan_correction_service import ( # noqa: E402
CrossPlanCorrectionService,
)
from cleveragents.domain.models.core.correction import ( # noqa: E402
ChildPlanState,
CorrectionImpact,
CorrectionMode,
)
# ---------------------------------------------------------------------------
# Mock implementations (minimal, for benchmarking)
# ---------------------------------------------------------------------------
class _BenchPlanLookup:
def __init__(self, states: dict[str, ChildPlanState]) -> None:
self._states = states
def get_child_plan_state(self, child_plan_id: str) -> ChildPlanState:
return self._states[child_plan_id]
class _BenchPlanCanceller:
def cancel_child_plan(self, child_plan_id: str) -> None:
pass
class _BenchSandboxRollbacker:
def rollback_child_plan_sandbox(self, child_plan_id: str) -> None:
pass
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _make_service(
child_count: int, state: ChildPlanState
) -> tuple[CrossPlanCorrectionService, list[str]]:
plan_ids = [f"CP{i}" for i in range(child_count)]
states = {pid: state for pid in plan_ids}
lookup = _BenchPlanLookup(states)
canceller = _BenchPlanCanceller()
rollbacker = _BenchSandboxRollbacker()
svc = CrossPlanCorrectionService(
plan_lookup=lookup,
plan_canceller=canceller,
sandbox_rollbacker=rollbacker,
)
return svc, plan_ids
# ---------------------------------------------------------------------------
# Benchmark suites
# ---------------------------------------------------------------------------
class CascadeCancelSuite:
"""Benchmark cascade with cancel-only (not-started) child plans."""
params: ClassVar[list[int]] = [1, 5, 10, 50, 100]
param_names: ClassVar[list[str]] = ["child_plan_count"]
def setup(self, child_plan_count: int) -> None:
self.svc, self.plan_ids = _make_service(
child_plan_count, ChildPlanState.NOT_STARTED
)
def time_execute_cascade_cancel(self, child_plan_count: int) -> None:
self.svc.execute_cascade("C1", self.plan_ids)
def time_evaluate_cascade_cancel(self, child_plan_count: int) -> None:
self.svc.evaluate_cascade("C1", self.plan_ids)
class CascadeRollbackSuite:
"""Benchmark cascade with cancel+rollback (in-progress) child plans."""
params: ClassVar[list[int]] = [1, 5, 10, 50, 100]
param_names: ClassVar[list[str]] = ["child_plan_count"]
def setup(self, child_plan_count: int) -> None:
self.svc, self.plan_ids = _make_service(
child_plan_count, ChildPlanState.IN_PROGRESS
)
def time_execute_cascade_rollback(self, child_plan_count: int) -> None:
self.svc.execute_cascade("C1", self.plan_ids)
class CascadeRejectionSuite:
"""Benchmark cascade rejection (applied child plans)."""
params: ClassVar[list[int]] = [1, 5, 10, 50, 100]
param_names: ClassVar[list[str]] = ["child_plan_count"]
def setup(self, child_plan_count: int) -> None:
self.svc, self.plan_ids = _make_service(
child_plan_count, ChildPlanState.APPLIED
)
def time_execute_cascade_rejection(self, child_plan_count: int) -> None:
self.svc.execute_cascade("C1", self.plan_ids)
class CorrectionWithCascadeSuite:
"""Benchmark full execute_correction_with_cascade flow."""
params: ClassVar[list[int]] = [0, 5, 20, 50]
param_names: ClassVar[list[str]] = ["child_plan_count"]
def setup(self, child_plan_count: int) -> None:
plan_ids = [f"CP{i}" for i in range(child_plan_count)]
states = {pid: ChildPlanState.NOT_STARTED for pid in plan_ids}
lookup = _BenchPlanLookup(states)
canceller = _BenchPlanCanceller()
rollbacker = _BenchSandboxRollbacker()
self.svc = CrossPlanCorrectionService(
plan_lookup=lookup,
plan_canceller=canceller,
sandbox_rollbacker=rollbacker,
)
self.impact = CorrectionImpact(
affected_decisions=["D1"],
affected_child_plans=plan_ids,
risk_level="low",
rollback_tier="full",
artifacts_to_archive=["D1.artifact"],
)
def time_correction_with_cascade(self, child_plan_count: int) -> None:
self.svc.execute_correction_with_cascade(
"C1", self.impact, CorrectionMode.REVERT
)