"""ASV benchmarks for DecisionRepository persistence operations. Measures create, get, get_by_plan, get_tree, and list_by_type performance. """ from __future__ import annotations import sys from datetime import UTC, datetime from pathlib import Path try: from cleveragents.domain.models.core.action import Action, ActionState from cleveragents.domain.models.core.decision import ( Decision, DecisionType, ) from cleveragents.domain.models.core.plan import ( NamespacedName, Plan, PlanIdentity, PlanPhase, PlanTimestamps, ProcessingState, ) from cleveragents.infrastructure.database.models import Base from cleveragents.infrastructure.database.repositories import ( ActionRepository, DecisionRepository, LifecyclePlanRepository, ) except ModuleNotFoundError: sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) from cleveragents.domain.models.core.action import Action, ActionState from cleveragents.domain.models.core.decision import ( Decision, DecisionType, ) from cleveragents.domain.models.core.plan import ( NamespacedName, Plan, PlanIdentity, PlanPhase, PlanTimestamps, ProcessingState, ) from cleveragents.infrastructure.database.models import Base from cleveragents.infrastructure.database.repositories import ( ActionRepository, DecisionRepository, LifecyclePlanRepository, ) from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker _PLAN_ID = "01HV00000000000000BENCH001" def _make_db(): """Create an in-memory SQLite DB with all tables and prerequisites.""" engine = create_engine("sqlite:///:memory:", echo=False) Base.metadata.create_all(engine) sm = sessionmaker(bind=engine) session = sm() factory = lambda: session # noqa: E731 # Action prerequisite action_repo = ActionRepository(session_factory=factory) action = Action( namespaced_name=NamespacedName.parse("local/bench-decision-action"), description="Benchmark action", definition_of_done="Done", strategy_actor="local/s", execution_actor="local/e", state=ActionState.AVAILABLE, created_at=datetime(2026, 1, 1, tzinfo=UTC), updated_at=datetime(2026, 1, 1, tzinfo=UTC), ) action_repo.create(action) session.commit() # Plan prerequisite plan_repo = LifecyclePlanRepository(session_factory=factory) now = datetime(2026, 3, 1, tzinfo=UTC) plan = Plan( identity=PlanIdentity(plan_id=_PLAN_ID, attempt=1), namespaced_name=NamespacedName(namespace="local", name="bench-plan"), action_name="local/bench-decision-action", description="Benchmark plan", definition_of_done="Done", phase=PlanPhase.STRATEGIZE, processing_state=ProcessingState.PROCESSING, strategy_actor="local/s", execution_actor="local/e", timestamps=PlanTimestamps(created_at=now, updated_at=now), created_by="bench", tags=[], reusable=True, read_only=False, ) plan_repo.create(plan) session.commit() return session, factory def _make_decision(seq=0, parent_id=None, dtype=DecisionType.PROMPT_DEFINITION): return Decision( plan_id=_PLAN_ID, parent_decision_id=parent_id, sequence_number=seq, decision_type=dtype, question="Benchmark question?", chosen_option="Benchmark option", ) class TimeDecisionCreate: """Benchmark decision creation.""" timeout = 60 def setup(self): self.session, self.factory = _make_db() self.repo = DecisionRepository(session_factory=self.factory) self._seq = 100 def time_create_single(self): self._seq += 1 d = _make_decision(seq=self._seq, dtype=DecisionType.STRATEGY_CHOICE) self.repo.create(d) self.session.commit() class TimeDecisionGet: """Benchmark decision retrieval.""" timeout = 60 def setup(self): self.session, self.factory = _make_db() self.repo = DecisionRepository(session_factory=self.factory) d = _make_decision() self.repo.create(d) self.session.commit() self.decision_id = d.decision_id def time_get_by_id(self): self.repo.get(self.decision_id) class TimeDecisionGetByPlan: """Benchmark get_by_plan with 50 pre-seeded decisions.""" timeout = 60 def setup(self): self.session, self.factory = _make_db() self.repo = DecisionRepository(session_factory=self.factory) root = _make_decision(seq=0) self.repo.create(root) root_id = root.decision_id for i in range(1, 50): d = _make_decision( seq=i, parent_id=root_id, dtype=DecisionType.STRATEGY_CHOICE, ) self.repo.create(d) self.session.commit() def time_get_by_plan(self): self.repo.get_by_plan(_PLAN_ID) class TimeDecisionTree: """Benchmark get_tree with a 3-level tree.""" timeout = 60 def setup(self): self.session, self.factory = _make_db() self.repo = DecisionRepository(session_factory=self.factory) root = _make_decision(seq=0) self.repo.create(root) self.root_id = root.decision_id seq = 1 for _ in range(3): child = _make_decision( seq=seq, parent_id=root.decision_id, dtype=DecisionType.STRATEGY_CHOICE, ) self.repo.create(child) for _ in range(2): seq += 1 gc = _make_decision( seq=seq, parent_id=child.decision_id, dtype=DecisionType.IMPLEMENTATION_CHOICE, ) self.repo.create(gc) seq += 1 self.session.commit() def time_get_tree(self): self.repo.get_tree(self.root_id) class TimeDecisionListByType: """Benchmark list_by_type with pre-seeded typed decisions.""" timeout = 60 def setup(self): self.session, self.factory = _make_db() self.repo = DecisionRepository(session_factory=self.factory) root = _make_decision(seq=0) self.repo.create(root) root_id = root.decision_id for i in range(1, 20): d = _make_decision( seq=i, parent_id=root_id, dtype=DecisionType.STRATEGY_CHOICE, ) self.repo.create(d) for i in range(20, 30): d = _make_decision( seq=i, parent_id=root_id, dtype=DecisionType.IMPLEMENTATION_CHOICE, ) self.repo.create(d) self.session.commit() def time_list_strategy_choices(self): self.repo.list_by_type(_PLAN_ID, "strategy_choice") def time_list_implementation_choices(self): self.repo.list_by_type(_PLAN_ID, "implementation_choice")