Files
freemo a2da043cbd
CI / lint (pull_request) Successful in 25s
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 21s
CI / typecheck (pull_request) Successful in 39s
CI / security (pull_request) Successful in 37s
CI / build (pull_request) Successful in 26s
CI / integration_tests (pull_request) Successful in 4m43s
CI / unit_tests (pull_request) Successful in 11m2s
CI / docker (pull_request) Successful in 58s
CI / benchmark-regression (pull_request) Successful in 18m58s
CI / coverage (pull_request) Successful in 24m24s
feat(M1.2): PlanExecutionContext, RuntimeExecuteActor, and runtime mode
Adds PlanExecutionContext carrying plan metadata and delegating
changeset ops to ChangeSetStore.  RuntimeExecuteResult captures
execution output (changeset_id, tool_call_count, sandbox_refs,
decision_ids_processed, execution_duration_ms).

RuntimeExecuteActor dispatches StrategyDecision lists through
ToolRunner with full changeset capture and optional streaming
callbacks.  PlanExecutor gains execution_context param with
has_runtime / changeset_store / execution_context properties
and _run_execute_with_runtime / _run_execute_with_stub split.

31 Behave scenarios, 5 Robot smoke tests, ASV benchmark suite,
and reference documentation.

Ref: Day-14 Rebaseline – M1.2 Plan-execute runtime wiring [Jeff]
2026-02-22 15:13:43 +00:00

216 lines
6.9 KiB
Python

"""Airspeed Velocity benchmarks for plan execute runtime integration.
Measures construction, changeset operations, RuntimeExecuteActor
dispatch, and PlanExecutor mode selection overhead.
"""
from __future__ import annotations
from unittest.mock import MagicMock
from ulid import ULID
from cleveragents.application.services.plan_execution_context import (
PlanExecutionContext,
RuntimeExecuteActor,
RuntimeExecuteResult,
)
from cleveragents.application.services.plan_executor import (
PlanExecutor,
StrategyDecision,
)
from cleveragents.domain.models.core.change import (
ChangeEntry,
ChangeOperation,
InMemoryChangeSetStore,
)
from cleveragents.tool.registry import ToolRegistry
from cleveragents.tool.runner import ToolRunner
_PLAN_ID = "01HGZ6FE0AQDYTR4BXVQZ6EA00"
_RESOURCE_ID = "01HGZ6FE0AQDYTR4BXVQZ6EB00"
def _make_runner() -> ToolRunner:
return ToolRunner(registry=ToolRegistry())
def _make_decisions(count: int) -> list[StrategyDecision]:
root_id = str(ULID())
return [
StrategyDecision(
decision_id=root_id if i == 0 else str(ULID()),
step_text=f"Step {i + 1}",
sequence=i,
parent_id=root_id if i > 0 else None,
)
for i in range(count)
]
def _make_entry(plan_id: str, idx: int = 0) -> ChangeEntry:
return ChangeEntry(
plan_id=plan_id,
resource_id=_RESOURCE_ID,
tool_name="bench/tool",
operation=ChangeOperation.MODIFY,
path=f"src/bench_{idx}.py",
)
class PlanExecutionContextSuite:
"""Benchmark PlanExecutionContext construction and operations."""
def time_minimal_context_creation(self) -> None:
"""Time creating context with only plan_id."""
PlanExecutionContext(plan_id=_PLAN_ID)
def time_full_context_creation(self) -> None:
"""Time creating context with all optional fields."""
PlanExecutionContext(
plan_id=_PLAN_ID,
decision_root_id=_RESOURCE_ID,
sandbox_root="/tmp/sandbox",
automation_profile="trusted",
project_resources={"repo": {"path": "/code"}},
changeset_store=InMemoryChangeSetStore(),
)
def time_start_changeset(self) -> None:
"""Time starting a new changeset."""
ctx = PlanExecutionContext(plan_id=_PLAN_ID)
ctx.start_changeset()
def time_record_change_single(self) -> None:
"""Time recording a single change entry."""
ctx = PlanExecutionContext(plan_id=_PLAN_ID)
ctx.start_changeset()
ctx.record_change(_make_entry(_PLAN_ID))
def time_record_change_batch_10(self) -> None:
"""Time recording 10 change entries."""
ctx = PlanExecutionContext(plan_id=_PLAN_ID)
ctx.start_changeset()
for i in range(10):
ctx.record_change(_make_entry(_PLAN_ID, i))
def time_summarize_empty(self) -> None:
"""Time summarize with no changesets."""
ctx = PlanExecutionContext(plan_id=_PLAN_ID)
ctx.summarize()
def time_summarize_with_changeset(self) -> None:
"""Time summarize with one changeset and 5 entries."""
ctx = PlanExecutionContext(plan_id=_PLAN_ID)
ctx.start_changeset()
for i in range(5):
ctx.record_change(_make_entry(_PLAN_ID, i))
ctx.summarize()
class RuntimeExecuteResultSuite:
"""Benchmark RuntimeExecuteResult model creation."""
def time_result_creation_minimal(self) -> None:
"""Time creating result with only required fields."""
RuntimeExecuteResult(changeset_id=str(ULID()))
def time_result_creation_full(self) -> None:
"""Time creating result with all fields populated."""
RuntimeExecuteResult(
changeset_id=str(ULID()),
tool_call_count=42,
sandbox_refs=["/tmp/sb1", "/tmp/sb2"],
decision_ids_processed=[str(ULID()) for _ in range(5)],
execution_duration_ms=1500.0,
)
class RuntimeExecuteActorSuite:
"""Benchmark RuntimeExecuteActor execution."""
def setup(self) -> None:
self.store = InMemoryChangeSetStore()
self.runner = _make_runner()
def time_single_decision_execution(self) -> None:
"""Time executing a single decision."""
ctx = PlanExecutionContext(
plan_id=_PLAN_ID,
changeset_store=self.store,
)
actor = RuntimeExecuteActor(
tool_runner=self.runner,
execution_context=ctx,
)
actor.execute(decisions=_make_decisions(1))
def time_five_decision_execution(self) -> None:
"""Time executing five decisions."""
ctx = PlanExecutionContext(
plan_id=_PLAN_ID,
changeset_store=self.store,
)
actor = RuntimeExecuteActor(
tool_runner=self.runner,
execution_context=ctx,
)
actor.execute(decisions=_make_decisions(5))
def time_execution_with_callback(self) -> None:
"""Time executing with a stream callback."""
ctx = PlanExecutionContext(
plan_id=_PLAN_ID,
changeset_store=self.store,
)
actor = RuntimeExecuteActor(
tool_runner=self.runner,
execution_context=ctx,
)
events: list[tuple[str, dict]] = []
actor.execute(
decisions=_make_decisions(3),
stream_callback=lambda t, d: events.append((t, d)),
)
class PlanExecutorModeSuite:
"""Benchmark PlanExecutor construction with different modes."""
def time_stub_mode_construction(self) -> None:
"""Time constructing PlanExecutor in stub mode."""
PlanExecutor(lifecycle_service=MagicMock())
def time_runtime_mode_construction(self) -> None:
"""Time constructing PlanExecutor in runtime mode."""
ctx = PlanExecutionContext(plan_id=_PLAN_ID)
PlanExecutor(
lifecycle_service=MagicMock(),
tool_runner=_make_runner(),
execution_context=ctx,
)
def time_has_runtime_check_stub(self) -> None:
"""Time has_runtime property check in stub mode."""
executor = PlanExecutor(lifecycle_service=MagicMock())
_ = executor.has_runtime
def time_has_runtime_check_runtime(self) -> None:
"""Time has_runtime property check in runtime mode."""
ctx = PlanExecutionContext(plan_id=_PLAN_ID)
executor = PlanExecutor(
lifecycle_service=MagicMock(),
execution_context=ctx,
)
_ = executor.has_runtime
def time_changeset_store_access(self) -> None:
"""Time changeset_store property access in runtime mode."""
store = InMemoryChangeSetStore()
ctx = PlanExecutionContext(plan_id=_PLAN_ID, changeset_store=store)
executor = PlanExecutor(
lifecycle_service=MagicMock(),
execution_context=ctx,
)
_ = executor.changeset_store