142 lines
4.0 KiB
Markdown
142 lines
4.0 KiB
Markdown
# Tool Call Router
|
|
|
|
The **ToolCallRouter** translates between LLM provider-specific tool call formats and the internal `ToolRunner`. It supports OpenAI, Anthropic, and LangChain formats, producing normalized `NormalizedToolCallResult` objects regardless of the input format.
|
|
|
|
## Provider Format Mapping
|
|
|
|
| Provider | Tool Call Shape | Arguments Key | Arguments Type |
|
|
|------------|-----------------|---------------|----------------|
|
|
| **OpenAI** | `{"name": "...", "arguments": "..."}` | `arguments` | JSON string |
|
|
| **Anthropic** | `{"name": "...", "input": {...}}` | `input` | dict |
|
|
| **LangChain** | `{"name": "...", "type": "tool_call", "args": {...}}` | `args` | dict |
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Provider (OpenAI/Anthropic/LangChain)
|
|
|
|
|
v
|
|
ToolCallRouter.route(payload)
|
|
|-- detect_provider_format(payload)
|
|
|-- normalize_tool_call(payload) -> ToolCallRequest
|
|
|-- generate_tool_call_id(plan_id, sequence)
|
|
|-- ToolRunner.execute(name, args)
|
|
|-- classify_tool_error(error) [if failed]
|
|
|-- _check_is_validation(spec) [validation surfacing]
|
|
v
|
|
NormalizedToolCallResult
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Basic Routing
|
|
|
|
```python
|
|
from cleveragents.tool.registry import ToolRegistry
|
|
from cleveragents.tool.runner import ToolRunner
|
|
from cleveragents.tool.router import ToolCallRouter
|
|
|
|
registry = ToolRegistry()
|
|
# ... register tools ...
|
|
runner = ToolRunner(registry)
|
|
router = ToolCallRouter(registry=registry, runner=runner, plan_id="plan-001")
|
|
|
|
# Route an OpenAI-format tool call
|
|
result = router.route({
|
|
"name": "my-ns/my-tool",
|
|
"arguments": '{"key": "value"}'
|
|
})
|
|
print(result.result.success)
|
|
print(result.tool_call_id)
|
|
print(result.provider_format) # "openai"
|
|
```
|
|
|
|
### Batch Routing
|
|
|
|
```python
|
|
results = router.route_batch([
|
|
{"name": "ns/tool-a", "arguments": '{"x": 1}'},
|
|
{"name": "ns/tool-b", "input": {"y": 2}},
|
|
])
|
|
for r in results:
|
|
print(f"{r.tool_name}: success={r.result.success}")
|
|
```
|
|
|
|
### Streaming Execution
|
|
|
|
```python
|
|
for update in router.route_streaming(payload):
|
|
if isinstance(update, StreamingToolUpdate):
|
|
print(f"Status: {update.status}")
|
|
elif isinstance(update, NormalizedToolCallResult):
|
|
print(f"Final: success={update.result.success}")
|
|
```
|
|
|
|
### Schema Export
|
|
|
|
```python
|
|
from cleveragents.tool.router import ProviderFormat
|
|
|
|
schemas = router.export_schemas(ProviderFormat.OPENAI)
|
|
# Returns provider-normalized schemas for all registered tools
|
|
```
|
|
|
|
## Stable ID Generation
|
|
|
|
Tool call IDs are generated deterministically from `plan_id + sequence`:
|
|
|
|
```python
|
|
from cleveragents.tool.router import generate_tool_call_id
|
|
|
|
call_id = generate_tool_call_id("plan-001", 0)
|
|
# Returns: "tc_<24-char-hex>"
|
|
```
|
|
|
|
The same inputs always produce the same ID, enabling reproducible tracking.
|
|
|
|
## Error Classification
|
|
|
|
Errors are classified into structured categories:
|
|
|
|
| Category | Matches |
|
|
|----------|---------|
|
|
| `timeout` | "timeout", "timed out" |
|
|
| `permission` | "permission", "access denied", "forbidden" |
|
|
| `not_found` | "not found" |
|
|
| `resource` | "resource", "memory", "disk" |
|
|
| `schema` | "schema", "validation", "json" |
|
|
| `parse` | "parse", "decode", "deserializ" |
|
|
| `execution` | Everything else |
|
|
| `unknown` | Empty error messages |
|
|
|
|
## Validation Surfacing
|
|
|
|
When a tool is detected as a validation (read-only tool with "valid" in its name), the router:
|
|
|
|
1. Sets `is_validation=True` on the result
|
|
2. Extracts `passed` from the tool output
|
|
3. Includes the `validation_mode` if available in the output schema
|
|
|
|
## Provider Metadata
|
|
|
|
The router captures provider-level metadata passed during routing:
|
|
|
|
```python
|
|
result = router.route(payload, provider_metadata={
|
|
"model": "gpt-4",
|
|
"provider_id": "openai",
|
|
"latency_ms": 150,
|
|
})
|
|
print(result.provider_metadata["model"]) # "gpt-4"
|
|
```
|
|
|
|
## Schema Normalization
|
|
|
|
Schemas are normalized per provider with field pruning and description truncation:
|
|
|
|
| Provider | Schema Key | Default Max Description |
|
|
|----------|-----------|------------------------|
|
|
| OpenAI | `parameters` | 1024 chars |
|
|
| Anthropic | `input_schema` | 1024 chars |
|
|
| LangChain | `args_schema` | 1024 chars |
|