Files
cleveragents-core/benchmarks/tool_lifecycle_bench.py
freemo 2d345ae31c
CI / build (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / security (push) Waiting to run
CI / quality (push) Waiting to run
CI / unit_tests (push) Waiting to run
CI / integration_tests (push) Waiting to run
CI / coverage (push) Blocked by required conditions
CI / docker (push) Blocked by required conditions
CI / lint (pull_request) Failing after 14s
CI / typecheck (pull_request) Successful in 27s
CI / coverage (pull_request) Has been skipped
CI / security (pull_request) Successful in 22s
CI / quality (pull_request) Successful in 15s
CI / integration_tests (pull_request) Failing after 4m37s
CI / build (pull_request) Successful in 16s
CI / unit_tests (pull_request) Successful in 10m3s
CI / docker (pull_request) Has been skipped
feat(tool): add tool lifecycle runtime
Implement the four-stage tool lifecycle (discover/activate/execute/deactivate)
with capability enforcement, per-plan activation caching, JSON Schema validation,
execution tracing, and cancellation propagation.

New modules:
- tool/context.py: ToolExecutionContext, BoundResource, Change, CancellationToken
- tool/lifecycle.py: ToolRuntime, ToolInstance protocol, ToolLifecycleCache
- tool/schema_validator.py: JSON Schema input/output validation

Tests: 57 Behave scenarios, 6 Robot smoke tests, 5 ASV benchmarks
Coverage: context 100%, schema_validator 100%, lifecycle 94%, overall 97%
2026-02-14 18:55:48 +00:00

166 lines
4.5 KiB
Python

"""ASV benchmarks for tool lifecycle runtime."""
from __future__ import annotations
from typing import Any
from cleveragents.tool.context import (
BoundResource,
CancellationToken,
Change,
ChangeOperation,
ToolExecutionContext,
)
from cleveragents.tool.lifecycle import (
ToolDescriptor,
ToolLifecycleCache,
ToolResult,
)
from cleveragents.tool.schema_validator import validate_tool_input
class _DummyInstance:
"""Minimal mock for benchmarks."""
def discover(self) -> ToolDescriptor:
return ToolDescriptor(name="bench/tool", description="bench", source="builtin")
def activate(self, ctx: Any) -> None:
pass
def execute(self, params: Any, ctx: Any) -> ToolResult:
return ToolResult()
def deactivate(self, ctx: Any) -> None:
pass
class TimeToolExecutionContext:
"""Benchmark ToolExecutionContext creation and operations."""
def time_create_context(self) -> None:
ToolExecutionContext(plan_id="plan-bench-001")
def time_create_context_with_resources(self) -> None:
resources = {
f"slot_{i}": BoundResource(
slot_name=f"slot_{i}",
resource_id=f"res-{i:03d}",
resource_type="git-checkout",
access="read_write",
)
for i in range(10)
}
ToolExecutionContext(
plan_id="plan-bench-002",
resources=resources,
)
def time_record_change(self) -> None:
ctx = ToolExecutionContext(plan_id="plan-bench-003")
for i in range(100):
ctx.record_change(
Change(
operation=ChangeOperation.MODIFY,
resource_id=f"res-{i:03d}",
)
)
def time_context_summary(self) -> None:
ctx = ToolExecutionContext(plan_id="plan-bench-004")
for _ in range(50):
ctx.as_summary()
class TimeCancellationToken:
"""Benchmark CancellationToken operations."""
def time_create_token(self) -> None:
CancellationToken()
def time_check_not_cancelled(self) -> None:
token = CancellationToken()
for _ in range(1000):
_ = token.is_cancelled
def time_cancel_and_check(self) -> None:
token = CancellationToken()
token.cancel()
for _ in range(1000):
_ = token.is_cancelled
class TimeToolLifecycleCache:
"""Benchmark ToolLifecycleCache operations."""
def time_put_and_get(self) -> None:
cache = ToolLifecycleCache()
dummy = _DummyInstance()
for i in range(100):
cache.put(f"plan-{i}", f"tool/t-{i}", dummy)
for i in range(100):
cache.get(f"plan-{i}", f"tool/t-{i}")
def time_remove_plan(self) -> None:
cache = ToolLifecycleCache()
dummy = _DummyInstance()
for i in range(50):
cache.put("plan-001", f"tool/t-{i}", dummy)
cache.remove_plan("plan-001")
def time_get_stats(self) -> None:
cache = ToolLifecycleCache()
dummy = _DummyInstance()
for i in range(20):
cache.put("plan-001", f"tool/t-{i}", dummy)
for _ in range(50):
cache.get_stats("plan-001")
class TimeSchemaValidation:
"""Benchmark JSON Schema validation."""
def setup(self) -> None:
self.schema: dict[str, Any] = {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"},
"mode": {"type": "integer"},
},
"required": ["path"],
}
def time_validate_simple(self) -> None:
validate_tool_input({"path": "test.txt"}, self.schema)
def time_validate_full(self) -> None:
validate_tool_input(
{"path": "test.txt", "content": "hello", "mode": 644},
self.schema,
)
class TimeToolDescriptor:
"""Benchmark ToolDescriptor creation."""
def time_create_descriptor(self) -> None:
for i in range(100):
ToolDescriptor(
name=f"ns/tool-{i}",
description=f"Tool {i}",
)
def time_create_descriptor_with_schema(self) -> None:
schema: dict[str, Any] = {
"type": "object",
"properties": {"path": {"type": "string"}},
}
for i in range(100):
ToolDescriptor(
name=f"ns/tool-{i}",
description=f"Tool {i}",
input_schema=schema,
output_schema=schema,
)