196 lines
6.1 KiB
Python
196 lines
6.1 KiB
Python
"""ASV benchmarks for ToolCallRouter operations.
|
|
|
|
Measures the performance of:
|
|
- Provider format detection
|
|
- Payload normalization
|
|
- Stable ID generation
|
|
- Tool call routing (OpenAI, Anthropic, LangChain)
|
|
- Batch routing
|
|
- Schema normalization
|
|
- Error classification
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
try:
|
|
from cleveragents.tool.registry import ToolRegistry
|
|
from cleveragents.tool.router import (
|
|
ProviderFormat,
|
|
ToolCallRouter,
|
|
classify_tool_error,
|
|
detect_provider_format,
|
|
generate_tool_call_id,
|
|
normalize_tool_call,
|
|
normalize_tool_schema_for_provider,
|
|
)
|
|
from cleveragents.tool.runner import ToolRunner
|
|
from cleveragents.tool.runtime import ToolSpec
|
|
except ModuleNotFoundError:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
from cleveragents.tool.registry import ToolRegistry
|
|
from cleveragents.tool.router import (
|
|
ProviderFormat,
|
|
ToolCallRouter,
|
|
classify_tool_error,
|
|
detect_provider_format,
|
|
generate_tool_call_id,
|
|
normalize_tool_call,
|
|
normalize_tool_schema_for_provider,
|
|
)
|
|
from cleveragents.tool.runner import ToolRunner
|
|
from cleveragents.tool.runtime import ToolSpec
|
|
|
|
|
|
def _echo(inputs: dict[str, Any]) -> dict[str, Any]:
|
|
return dict(inputs)
|
|
|
|
|
|
class FormatDetectionSuite:
|
|
"""Benchmark provider format detection."""
|
|
|
|
def setup(self) -> None:
|
|
"""Prepare payloads."""
|
|
self.openai_payload = {"name": "bench/echo", "arguments": '{"a": 1}'}
|
|
self.anthropic_payload = {"name": "bench/echo", "input": {"a": 1}}
|
|
self.langchain_payload = {
|
|
"name": "bench/echo",
|
|
"type": "tool_call",
|
|
"args": {"a": 1},
|
|
}
|
|
|
|
def time_detect_openai(self) -> None:
|
|
"""Benchmark OpenAI format detection."""
|
|
detect_provider_format(self.openai_payload)
|
|
|
|
def time_detect_anthropic(self) -> None:
|
|
"""Benchmark Anthropic format detection."""
|
|
detect_provider_format(self.anthropic_payload)
|
|
|
|
def time_detect_langchain(self) -> None:
|
|
"""Benchmark LangChain format detection."""
|
|
detect_provider_format(self.langchain_payload)
|
|
|
|
|
|
class NormalizationSuite:
|
|
"""Benchmark payload normalization."""
|
|
|
|
def setup(self) -> None:
|
|
"""Prepare payloads."""
|
|
self.openai_payload = {"name": "bench/echo", "arguments": '{"a": 1}'}
|
|
self.anthropic_payload = {"name": "bench/echo", "input": {"a": 1}}
|
|
|
|
def time_normalize_openai(self) -> None:
|
|
"""Benchmark OpenAI payload normalization."""
|
|
normalize_tool_call(self.openai_payload)
|
|
|
|
def time_normalize_anthropic(self) -> None:
|
|
"""Benchmark Anthropic payload normalization."""
|
|
normalize_tool_call(self.anthropic_payload)
|
|
|
|
|
|
class IDGenerationSuite:
|
|
"""Benchmark stable ID generation."""
|
|
|
|
def time_generate_id(self) -> None:
|
|
"""Benchmark single ID generation."""
|
|
generate_tool_call_id("plan-bench", 42)
|
|
|
|
def time_generate_100_ids(self) -> None:
|
|
"""Benchmark generating 100 IDs."""
|
|
for i in range(100):
|
|
generate_tool_call_id("plan-bench", i)
|
|
|
|
|
|
class RoutingSuite:
|
|
"""Benchmark tool call routing."""
|
|
|
|
def setup(self) -> None:
|
|
"""Prepare registry and router."""
|
|
self.registry = ToolRegistry()
|
|
spec = ToolSpec(
|
|
name="bench/echo",
|
|
description="Benchmark echo tool",
|
|
handler=_echo,
|
|
)
|
|
self.registry.register(spec)
|
|
self.runner = ToolRunner(self.registry)
|
|
self.router = ToolCallRouter(
|
|
registry=self.registry,
|
|
runner=self.runner,
|
|
plan_id="plan-bench",
|
|
)
|
|
self.openai_payload = {
|
|
"name": "bench/echo",
|
|
"arguments": '{"msg": "hello"}',
|
|
}
|
|
self.anthropic_payload = {
|
|
"name": "bench/echo",
|
|
"input": {"msg": "hello"},
|
|
}
|
|
self.langchain_payload = {
|
|
"name": "bench/echo",
|
|
"type": "tool_call",
|
|
"args": {"msg": "hello"},
|
|
}
|
|
|
|
def time_route_openai(self) -> None:
|
|
"""Benchmark routing an OpenAI tool call."""
|
|
self.router.route(self.openai_payload)
|
|
|
|
def time_route_anthropic(self) -> None:
|
|
"""Benchmark routing an Anthropic tool call."""
|
|
self.router.route(self.anthropic_payload)
|
|
|
|
def time_route_langchain(self) -> None:
|
|
"""Benchmark routing a LangChain tool call."""
|
|
self.router.route(self.langchain_payload)
|
|
|
|
def time_route_batch_10(self) -> None:
|
|
"""Benchmark routing a batch of 10 tool calls."""
|
|
payloads = [
|
|
{"name": "bench/echo", "arguments": json.dumps({"i": i})} for i in range(10)
|
|
]
|
|
self.router.route_batch(payloads)
|
|
|
|
|
|
class SchemaSuite:
|
|
"""Benchmark schema normalization."""
|
|
|
|
def setup(self) -> None:
|
|
"""Prepare tool spec."""
|
|
self.spec = ToolSpec(
|
|
name="bench/echo",
|
|
description="Benchmark echo tool",
|
|
handler=_echo,
|
|
input_schema={"type": "object", "properties": {"a": {"type": "integer"}}},
|
|
)
|
|
|
|
def time_normalize_openai_schema(self) -> None:
|
|
"""Benchmark schema normalization for OpenAI."""
|
|
normalize_tool_schema_for_provider(self.spec, ProviderFormat.OPENAI)
|
|
|
|
def time_normalize_anthropic_schema(self) -> None:
|
|
"""Benchmark schema normalization for Anthropic."""
|
|
normalize_tool_schema_for_provider(self.spec, ProviderFormat.ANTHROPIC)
|
|
|
|
|
|
class ErrorClassificationSuite:
|
|
"""Benchmark error classification."""
|
|
|
|
def time_classify_timeout(self) -> None:
|
|
"""Benchmark classifying timeout error."""
|
|
classify_tool_error("Operation timed out after 30s")
|
|
|
|
def time_classify_permission(self) -> None:
|
|
"""Benchmark classifying permission error."""
|
|
classify_tool_error("Permission denied: cannot write")
|
|
|
|
def time_classify_generic(self) -> None:
|
|
"""Benchmark classifying generic error."""
|
|
classify_tool_error("Something unexpected happened")
|