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
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%
166 lines
4.5 KiB
Python
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,
|
|
)
|