chuk-tool-processor 0.6.4__py3-none-any.whl → 0.9.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of chuk-tool-processor might be problematic. Click here for more details.
- chuk_tool_processor/core/__init__.py +32 -1
- chuk_tool_processor/core/exceptions.py +225 -13
- chuk_tool_processor/core/processor.py +135 -104
- chuk_tool_processor/execution/strategies/__init__.py +6 -0
- chuk_tool_processor/execution/strategies/inprocess_strategy.py +142 -150
- chuk_tool_processor/execution/strategies/subprocess_strategy.py +202 -206
- chuk_tool_processor/execution/tool_executor.py +82 -84
- chuk_tool_processor/execution/wrappers/__init__.py +42 -0
- chuk_tool_processor/execution/wrappers/caching.py +150 -116
- chuk_tool_processor/execution/wrappers/circuit_breaker.py +370 -0
- chuk_tool_processor/execution/wrappers/rate_limiting.py +76 -43
- chuk_tool_processor/execution/wrappers/retry.py +116 -78
- chuk_tool_processor/logging/__init__.py +23 -17
- chuk_tool_processor/logging/context.py +40 -45
- chuk_tool_processor/logging/formatter.py +22 -21
- chuk_tool_processor/logging/helpers.py +28 -42
- chuk_tool_processor/logging/metrics.py +13 -15
- chuk_tool_processor/mcp/__init__.py +8 -12
- chuk_tool_processor/mcp/mcp_tool.py +158 -114
- chuk_tool_processor/mcp/register_mcp_tools.py +22 -22
- chuk_tool_processor/mcp/setup_mcp_http_streamable.py +57 -17
- chuk_tool_processor/mcp/setup_mcp_sse.py +57 -17
- chuk_tool_processor/mcp/setup_mcp_stdio.py +11 -11
- chuk_tool_processor/mcp/stream_manager.py +333 -276
- chuk_tool_processor/mcp/transport/__init__.py +22 -29
- chuk_tool_processor/mcp/transport/base_transport.py +180 -44
- chuk_tool_processor/mcp/transport/http_streamable_transport.py +505 -325
- chuk_tool_processor/mcp/transport/models.py +100 -0
- chuk_tool_processor/mcp/transport/sse_transport.py +607 -276
- chuk_tool_processor/mcp/transport/stdio_transport.py +597 -116
- chuk_tool_processor/models/__init__.py +21 -1
- chuk_tool_processor/models/execution_strategy.py +16 -21
- chuk_tool_processor/models/streaming_tool.py +28 -25
- chuk_tool_processor/models/tool_call.py +49 -31
- chuk_tool_processor/models/tool_export_mixin.py +22 -8
- chuk_tool_processor/models/tool_result.py +40 -77
- chuk_tool_processor/models/tool_spec.py +350 -0
- chuk_tool_processor/models/validated_tool.py +36 -18
- chuk_tool_processor/observability/__init__.py +30 -0
- chuk_tool_processor/observability/metrics.py +312 -0
- chuk_tool_processor/observability/setup.py +105 -0
- chuk_tool_processor/observability/tracing.py +345 -0
- chuk_tool_processor/plugins/__init__.py +1 -1
- chuk_tool_processor/plugins/discovery.py +11 -11
- chuk_tool_processor/plugins/parsers/__init__.py +1 -1
- chuk_tool_processor/plugins/parsers/base.py +1 -2
- chuk_tool_processor/plugins/parsers/function_call_tool.py +13 -8
- chuk_tool_processor/plugins/parsers/json_tool.py +4 -3
- chuk_tool_processor/plugins/parsers/openai_tool.py +12 -7
- chuk_tool_processor/plugins/parsers/xml_tool.py +4 -4
- chuk_tool_processor/registry/__init__.py +12 -12
- chuk_tool_processor/registry/auto_register.py +22 -30
- chuk_tool_processor/registry/decorators.py +127 -129
- chuk_tool_processor/registry/interface.py +26 -23
- chuk_tool_processor/registry/metadata.py +27 -22
- chuk_tool_processor/registry/provider.py +17 -18
- chuk_tool_processor/registry/providers/__init__.py +16 -19
- chuk_tool_processor/registry/providers/memory.py +18 -25
- chuk_tool_processor/registry/tool_export.py +42 -51
- chuk_tool_processor/utils/validation.py +15 -16
- chuk_tool_processor-0.9.7.dist-info/METADATA +1813 -0
- chuk_tool_processor-0.9.7.dist-info/RECORD +67 -0
- chuk_tool_processor-0.6.4.dist-info/METADATA +0 -697
- chuk_tool_processor-0.6.4.dist-info/RECORD +0 -60
- {chuk_tool_processor-0.6.4.dist-info → chuk_tool_processor-0.9.7.dist-info}/WHEEL +0 -0
- {chuk_tool_processor-0.6.4.dist-info → chuk_tool_processor-0.9.7.dist-info}/top_level.txt +0 -0
|
@@ -1,697 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: chuk-tool-processor
|
|
3
|
-
Version: 0.6.4
|
|
4
|
-
Summary: Async-native framework for registering, discovering, and executing tools referenced in LLM responses
|
|
5
|
-
Author-email: CHUK Team <chrishayuk@somejunkmailbox.com>
|
|
6
|
-
Maintainer-email: CHUK Team <chrishayuk@somejunkmailbox.com>
|
|
7
|
-
License: MIT
|
|
8
|
-
Keywords: llm,tools,async,ai,openai,mcp,model-context-protocol,tool-calling,function-calling
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
-
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
-
Classifier: Framework :: AsyncIO
|
|
20
|
-
Classifier: Typing :: Typed
|
|
21
|
-
Requires-Python: >=3.11
|
|
22
|
-
Description-Content-Type: text/markdown
|
|
23
|
-
Requires-Dist: chuk-mcp>=0.5.1
|
|
24
|
-
Requires-Dist: dotenv>=0.9.9
|
|
25
|
-
Requires-Dist: pydantic>=2.11.3
|
|
26
|
-
Requires-Dist: uuid>=1.30
|
|
27
|
-
|
|
28
|
-
# CHUK Tool Processor - Architectural Analysis
|
|
29
|
-
|
|
30
|
-
## Overview
|
|
31
|
-
The CHUK Tool Processor is a sophisticated async-native framework for registering, discovering, and executing tools referenced in LLM responses. Built from the ground up for production use with comprehensive error handling, monitoring, and scalability features. It features a modular architecture with multiple transport mechanisms, execution strategies, and comprehensive tooling for production use.
|
|
32
|
-
|
|
33
|
-
## Quick Start Example
|
|
34
|
-
```python
|
|
35
|
-
import asyncio
|
|
36
|
-
from chuk_tool_processor import ToolProcessor, register_tool, initialize
|
|
37
|
-
|
|
38
|
-
# 1. Create a tool
|
|
39
|
-
@register_tool(name="calculator", description="Perform basic math operations")
|
|
40
|
-
class Calculator:
|
|
41
|
-
async def execute(self, operation: str, a: float, b: float) -> dict:
|
|
42
|
-
operations = {
|
|
43
|
-
"add": a + b,
|
|
44
|
-
"subtract": a - b,
|
|
45
|
-
"multiply": a * b,
|
|
46
|
-
"divide": a / b if b != 0 else None
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if operation not in operations:
|
|
50
|
-
raise ValueError(f"Unknown operation: {operation}")
|
|
51
|
-
|
|
52
|
-
result = operations[operation]
|
|
53
|
-
if result is None:
|
|
54
|
-
raise ValueError("Cannot divide by zero")
|
|
55
|
-
|
|
56
|
-
return {"operation": operation, "operands": [a, b], "result": result}
|
|
57
|
-
|
|
58
|
-
async def main():
|
|
59
|
-
# 2. Initialize the system
|
|
60
|
-
await initialize()
|
|
61
|
-
|
|
62
|
-
# 3. Process LLM output containing tool calls
|
|
63
|
-
processor = ToolProcessor()
|
|
64
|
-
results = await processor.process('''
|
|
65
|
-
<tool name="calculator" args='{"operation": "multiply", "a": 15, "b": 23}'/>
|
|
66
|
-
''')
|
|
67
|
-
|
|
68
|
-
# 4. Handle results
|
|
69
|
-
for result in results:
|
|
70
|
-
if result.error:
|
|
71
|
-
print(f"❌ Tool '{result.tool}' failed: {result.error}")
|
|
72
|
-
else:
|
|
73
|
-
print(f"✅ Tool '{result.tool}' result: {result.result}")
|
|
74
|
-
|
|
75
|
-
asyncio.run(main())
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Key Features & Benefits
|
|
79
|
-
|
|
80
|
-
- **🔄 Async-Native**: Built for `async/await` from the ground up for optimal performance
|
|
81
|
-
- **🛡️ Production Ready**: Comprehensive error handling, timeouts, retries, and monitoring
|
|
82
|
-
- **📦 Multiple Execution Strategies**: In-process for speed, subprocess for isolation
|
|
83
|
-
- **🚀 High Performance**: Built-in caching, rate limiting, and concurrency control
|
|
84
|
-
- **📊 Observability**: Structured logging, metrics collection, and request tracing
|
|
85
|
-
- **🔗 MCP Integration**: Full Model Context Protocol support (STDIO, SSE, HTTP Streamable)
|
|
86
|
-
- **📡 Streaming Support**: Real-time incremental results for long-running operations
|
|
87
|
-
- **🔧 Extensible Architecture**: Plugin system for custom parsers and execution strategies
|
|
88
|
-
- **🎯 Multiple Input Formats**: XML tags, OpenAI tool_calls, JSON, function_call formats
|
|
89
|
-
- **⚡ Zero-Config Start**: Works out of the box with sensible defaults
|
|
90
|
-
|
|
91
|
-
## Core Architecture
|
|
92
|
-
|
|
93
|
-
### Installation & Setup
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
# From source (recommended for development)
|
|
97
|
-
git clone https://github.com/chrishayuk/chuk-tool-processor.git
|
|
98
|
-
cd chuk-tool-processor
|
|
99
|
-
pip install -e .
|
|
100
|
-
|
|
101
|
-
# Or install from PyPI (when available)
|
|
102
|
-
pip install chuk-tool-processor
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Environment Configuration
|
|
106
|
-
```bash
|
|
107
|
-
# Optional: Registry provider (default: memory)
|
|
108
|
-
export CHUK_TOOL_REGISTRY_PROVIDER=memory
|
|
109
|
-
|
|
110
|
-
# Optional: Default timeout (default: 30.0)
|
|
111
|
-
export CHUK_DEFAULT_TIMEOUT=30.0
|
|
112
|
-
|
|
113
|
-
# Optional: Enable structured JSON logging
|
|
114
|
-
export CHUK_STRUCTURED_LOGGING=true
|
|
115
|
-
|
|
116
|
-
# MCP Integration (if using external MCP servers)
|
|
117
|
-
export MCP_BEARER_TOKEN=your_bearer_token_here
|
|
118
|
-
```
|
|
119
|
-
### 1. Registry System
|
|
120
|
-
- **Interface-driven**: `ToolRegistryInterface` protocol defines the contract
|
|
121
|
-
- **Async-native**: All registry operations are async
|
|
122
|
-
- **Namespace support**: Tools are organized into namespaces (default: "default")
|
|
123
|
-
- **Metadata tracking**: Rich metadata with `ToolMetadata` model
|
|
124
|
-
- **Provider pattern**: `ToolRegistryProvider` for singleton management
|
|
125
|
-
|
|
126
|
-
```python
|
|
127
|
-
# Example registry usage
|
|
128
|
-
registry = await ToolRegistryProvider.get_registry()
|
|
129
|
-
await registry.register_tool(MyTool(), name="my_tool", namespace="custom")
|
|
130
|
-
tool = await registry.get_tool("my_tool", "custom")
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### 2. Tool Development Patterns
|
|
134
|
-
|
|
135
|
-
#### Simple Function-Based Tools
|
|
136
|
-
```python
|
|
137
|
-
from chuk_tool_processor.registry.auto_register import register_fn_tool
|
|
138
|
-
|
|
139
|
-
async def get_current_time(timezone: str = "UTC") -> str:
|
|
140
|
-
"""Get the current time in the specified timezone."""
|
|
141
|
-
from datetime import datetime
|
|
142
|
-
import pytz
|
|
143
|
-
|
|
144
|
-
tz = pytz.timezone(timezone)
|
|
145
|
-
current_time = datetime.now(tz)
|
|
146
|
-
return current_time.strftime("%Y-%m-%d %H:%M:%S %Z")
|
|
147
|
-
|
|
148
|
-
# Register the function as a tool
|
|
149
|
-
await register_fn_tool(get_current_time, namespace="utilities")
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
#### ValidatedTool (Declarative with Pydantic)
|
|
153
|
-
```python
|
|
154
|
-
@register_tool(name="weather", namespace="api")
|
|
155
|
-
class WeatherTool(ValidatedTool):
|
|
156
|
-
class Arguments(BaseModel):
|
|
157
|
-
location: str = Field(..., description="City name or coordinates")
|
|
158
|
-
units: str = Field("metric", description="Temperature units")
|
|
159
|
-
|
|
160
|
-
class Result(BaseModel):
|
|
161
|
-
location: str
|
|
162
|
-
temperature: float
|
|
163
|
-
conditions: str
|
|
164
|
-
|
|
165
|
-
async def _execute(self, location: str, units: str) -> Result:
|
|
166
|
-
# Implementation here
|
|
167
|
-
return self.Result(location=location, temperature=22.5, conditions="Sunny")
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
#### StreamingTool (Real-time Results)
|
|
171
|
-
```python
|
|
172
|
-
@register_tool(name="file_processor")
|
|
173
|
-
class FileProcessorTool(StreamingTool):
|
|
174
|
-
class Arguments(BaseModel):
|
|
175
|
-
file_path: str
|
|
176
|
-
operation: str = "count_lines"
|
|
177
|
-
|
|
178
|
-
class Result(BaseModel):
|
|
179
|
-
line_number: int
|
|
180
|
-
content: str
|
|
181
|
-
|
|
182
|
-
async def _stream_execute(self, file_path: str, operation: str):
|
|
183
|
-
"""Stream results as each line is processed."""
|
|
184
|
-
for i in range(1, 100):
|
|
185
|
-
await asyncio.sleep(0.01) # Simulate processing
|
|
186
|
-
yield self.Result(line_number=i, content=f"Processed line {i}")
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### 3. Processing LLM Responses
|
|
190
|
-
|
|
191
|
-
The processor automatically detects and parses multiple input formats:
|
|
192
|
-
|
|
193
|
-
```python
|
|
194
|
-
processor = ToolProcessor()
|
|
195
|
-
|
|
196
|
-
# 1. XML Tool Tags (most common)
|
|
197
|
-
xml_response = """
|
|
198
|
-
<tool name="search" args='{"query": "Python programming", "limit": 5}'/>
|
|
199
|
-
<tool name="get_current_time" args='{"timezone": "UTC"}'/>
|
|
200
|
-
"""
|
|
201
|
-
|
|
202
|
-
# 2. OpenAI Chat Completions Format
|
|
203
|
-
openai_response = {
|
|
204
|
-
"tool_calls": [
|
|
205
|
-
{
|
|
206
|
-
"id": "call_123",
|
|
207
|
-
"type": "function",
|
|
208
|
-
"function": {
|
|
209
|
-
"name": "search",
|
|
210
|
-
"arguments": '{"query": "Python programming", "limit": 5}'
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
]
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
# 3. Direct ToolCall objects
|
|
217
|
-
tool_calls = [
|
|
218
|
-
{"tool": "search", "arguments": {"query": "Python programming", "limit": 5}},
|
|
219
|
-
{"tool": "get_current_time", "arguments": {"timezone": "UTC"}}
|
|
220
|
-
]
|
|
221
|
-
|
|
222
|
-
# Process any format
|
|
223
|
-
results1 = await processor.process(xml_response)
|
|
224
|
-
results2 = await processor.process(openai_response)
|
|
225
|
-
results3 = await processor.process(tool_calls)
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### 4. Execution Strategies
|
|
229
|
-
|
|
230
|
-
#### InProcessStrategy (Default - Fast & Efficient)
|
|
231
|
-
- **Concurrent execution**: Uses asyncio for parallelism within the same process
|
|
232
|
-
- **Semaphore-based limiting**: Optional max_concurrency control
|
|
233
|
-
- **True streaming support**: Direct access to `stream_execute` methods
|
|
234
|
-
- **Enhanced tool resolution**: Namespace fallback logic with fuzzy matching
|
|
235
|
-
- **Proper timeout handling**: Always applies concrete timeouts
|
|
236
|
-
|
|
237
|
-
#### SubprocessStrategy (Isolation & Safety)
|
|
238
|
-
- **Process isolation**: Each tool runs in separate OS process for safety
|
|
239
|
-
- **Serialization support**: Handles complex objects and Pydantic models properly
|
|
240
|
-
- **Worker pool management**: Concurrent futures with automatic cleanup
|
|
241
|
-
- **Enhanced error handling**: Broken pool recovery and restart
|
|
242
|
-
- **Timeout coordination**: Safety timeouts prevent worker hangs
|
|
243
|
-
|
|
244
|
-
```python
|
|
245
|
-
# Configure execution strategy
|
|
246
|
-
from chuk_tool_processor.execution.strategies.subprocess_strategy import SubprocessStrategy
|
|
247
|
-
|
|
248
|
-
processor = ToolProcessor(
|
|
249
|
-
strategy=SubprocessStrategy(
|
|
250
|
-
registry=await get_default_registry(),
|
|
251
|
-
max_workers=4,
|
|
252
|
-
default_timeout=30.0
|
|
253
|
-
)
|
|
254
|
-
)
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### 5. Production Features & Wrappers
|
|
258
|
-
|
|
259
|
-
#### Caching for Performance
|
|
260
|
-
```python
|
|
261
|
-
from chuk_tool_processor.execution.wrappers.caching import cacheable
|
|
262
|
-
|
|
263
|
-
@cacheable(ttl=600) # Cache for 10 minutes
|
|
264
|
-
@register_tool(name="expensive_api")
|
|
265
|
-
class ExpensiveApiTool(ValidatedTool):
|
|
266
|
-
# Tool implementation
|
|
267
|
-
pass
|
|
268
|
-
|
|
269
|
-
# Or configure at processor level
|
|
270
|
-
processor = ToolProcessor(
|
|
271
|
-
enable_caching=True,
|
|
272
|
-
cache_ttl=300 # 5 minutes default
|
|
273
|
-
)
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
#### Rate Limiting
|
|
277
|
-
```python
|
|
278
|
-
from chuk_tool_processor.execution.wrappers.rate_limiting import rate_limited
|
|
279
|
-
|
|
280
|
-
@rate_limited(limit=20, period=60.0) # 20 calls per minute
|
|
281
|
-
@register_tool(name="api_tool")
|
|
282
|
-
class ApiTool(ValidatedTool):
|
|
283
|
-
# Tool implementation
|
|
284
|
-
pass
|
|
285
|
-
|
|
286
|
-
# Or processor-level configuration
|
|
287
|
-
processor = ToolProcessor(
|
|
288
|
-
enable_rate_limiting=True,
|
|
289
|
-
global_rate_limit=100, # 100 requests per minute globally
|
|
290
|
-
tool_rate_limits={
|
|
291
|
-
"expensive_api": (10, 60), # 10 per minute for specific tool
|
|
292
|
-
}
|
|
293
|
-
)
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
#### Automatic Retries
|
|
297
|
-
```python
|
|
298
|
-
from chuk_tool_processor.execution.wrappers.retry import retryable
|
|
299
|
-
|
|
300
|
-
@retryable(max_retries=3, base_delay=1.0)
|
|
301
|
-
@register_tool(name="unreliable_api")
|
|
302
|
-
class UnreliableApiTool(ValidatedTool):
|
|
303
|
-
# Tool implementation
|
|
304
|
-
pass
|
|
305
|
-
|
|
306
|
-
# Processor-level retry configuration
|
|
307
|
-
processor = ToolProcessor(
|
|
308
|
-
enable_retries=True,
|
|
309
|
-
max_retries=3
|
|
310
|
-
)
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### 6. MCP (Model Context Protocol) Integration
|
|
314
|
-
|
|
315
|
-
Connect to external tool servers using multiple transport protocols:
|
|
316
|
-
|
|
317
|
-
#### Quick MCP Setup with SSE (Server-Sent Events)
|
|
318
|
-
```python
|
|
319
|
-
from chuk_tool_processor.mcp import setup_mcp_sse
|
|
320
|
-
|
|
321
|
-
# Configure external MCP servers
|
|
322
|
-
servers = [
|
|
323
|
-
{
|
|
324
|
-
"name": "weather-service",
|
|
325
|
-
"url": "https://weather-mcp.example.com",
|
|
326
|
-
"api_key": "your_weather_api_key"
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
"name": "database-service",
|
|
330
|
-
"url": "https://db-mcp.example.com",
|
|
331
|
-
"api_key": "your_db_api_key"
|
|
332
|
-
}
|
|
333
|
-
]
|
|
334
|
-
|
|
335
|
-
# Initialize with full production configuration
|
|
336
|
-
processor, stream_manager = await setup_mcp_sse(
|
|
337
|
-
servers=servers,
|
|
338
|
-
namespace="mcp", # Tools available as mcp.tool_name
|
|
339
|
-
default_timeout=30.0,
|
|
340
|
-
enable_caching=True,
|
|
341
|
-
enable_retries=True
|
|
342
|
-
)
|
|
343
|
-
|
|
344
|
-
# Use external tools through MCP
|
|
345
|
-
results = await processor.process('''
|
|
346
|
-
<tool name="mcp.weather" args='{"location": "London"}'/>
|
|
347
|
-
<tool name="mcp.database_query" args='{"sql": "SELECT COUNT(*) FROM users"}'/>
|
|
348
|
-
''')
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
#### STDIO Transport (Process-based)
|
|
352
|
-
```python
|
|
353
|
-
from chuk_tool_processor.mcp import setup_mcp_stdio
|
|
354
|
-
|
|
355
|
-
# Create MCP config for local processes
|
|
356
|
-
mcp_config = {
|
|
357
|
-
"weather": {
|
|
358
|
-
"command": "python",
|
|
359
|
-
"args": ["-m", "weather_mcp_server"],
|
|
360
|
-
"env": {"API_KEY": "your_weather_key"}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
processor, stream_manager = await setup_mcp_stdio(
|
|
365
|
-
config_file="mcp_config.json",
|
|
366
|
-
servers=["weather"],
|
|
367
|
-
namespace="tools"
|
|
368
|
-
)
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
#### Supported Transports
|
|
372
|
-
- **STDIO**: Process-based communication for local MCP servers
|
|
373
|
-
- **SSE**: Server-Sent Events for cloud-based MCP services
|
|
374
|
-
- **HTTP Streamable**: Modern HTTP-based transport (spec 2025-03-26)
|
|
375
|
-
|
|
376
|
-
### 7. Monitoring & Observability
|
|
377
|
-
|
|
378
|
-
#### Structured Logging
|
|
379
|
-
```python
|
|
380
|
-
from chuk_tool_processor.logging import setup_logging, get_logger, log_context_span
|
|
381
|
-
|
|
382
|
-
# Setup structured logging
|
|
383
|
-
await setup_logging(
|
|
384
|
-
level=logging.INFO,
|
|
385
|
-
structured=True, # JSON output for production
|
|
386
|
-
log_file="tool_processor.log"
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
# Use contextual logging
|
|
390
|
-
logger = get_logger("my_app")
|
|
391
|
-
|
|
392
|
-
async def process_user_request(user_id: str, request: str):
|
|
393
|
-
async with log_context_span("user_request", {"user_id": user_id}):
|
|
394
|
-
logger.info("Processing user request", extra={
|
|
395
|
-
"request_length": len(request),
|
|
396
|
-
"user_id": user_id
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
results = await processor.process(request)
|
|
400
|
-
|
|
401
|
-
logger.info("Request processed successfully", extra={
|
|
402
|
-
"num_tools": len(results),
|
|
403
|
-
"success_rate": sum(1 for r in results if not r.error) / len(results)
|
|
404
|
-
})
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
#### Automatic Metrics Collection
|
|
408
|
-
```python
|
|
409
|
-
# Metrics are automatically collected for:
|
|
410
|
-
# - Tool execution success/failure rates
|
|
411
|
-
# - Execution durations and performance
|
|
412
|
-
# - Cache hit/miss rates and efficiency
|
|
413
|
-
# - Parser performance and accuracy
|
|
414
|
-
# - Registry operations and health
|
|
415
|
-
|
|
416
|
-
# Access programmatic metrics
|
|
417
|
-
from chuk_tool_processor.logging import metrics
|
|
418
|
-
|
|
419
|
-
# Custom metrics
|
|
420
|
-
await metrics.log_tool_execution(
|
|
421
|
-
tool="custom_metric",
|
|
422
|
-
success=True,
|
|
423
|
-
duration=1.5,
|
|
424
|
-
cached=False,
|
|
425
|
-
attempts=1
|
|
426
|
-
)
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
### 8. Error Handling & Best Practices
|
|
430
|
-
|
|
431
|
-
#### Robust Error Handling
|
|
432
|
-
```python
|
|
433
|
-
async def robust_tool_processing(llm_response: str):
|
|
434
|
-
"""Example of production-ready error handling."""
|
|
435
|
-
processor = ToolProcessor(
|
|
436
|
-
default_timeout=30.0,
|
|
437
|
-
enable_retries=True,
|
|
438
|
-
max_retries=3
|
|
439
|
-
)
|
|
440
|
-
|
|
441
|
-
try:
|
|
442
|
-
results = await processor.process(llm_response, timeout=60.0)
|
|
443
|
-
|
|
444
|
-
successful_results = []
|
|
445
|
-
failed_results = []
|
|
446
|
-
|
|
447
|
-
for result in results:
|
|
448
|
-
if result.error:
|
|
449
|
-
failed_results.append(result)
|
|
450
|
-
logger.error(f"Tool {result.tool} failed: {result.error}", extra={
|
|
451
|
-
"tool": result.tool,
|
|
452
|
-
"duration": result.duration,
|
|
453
|
-
"attempts": getattr(result, "attempts", 1)
|
|
454
|
-
})
|
|
455
|
-
else:
|
|
456
|
-
successful_results.append(result)
|
|
457
|
-
logger.info(f"Tool {result.tool} succeeded", extra={
|
|
458
|
-
"tool": result.tool,
|
|
459
|
-
"duration": result.duration,
|
|
460
|
-
"cached": getattr(result, "cached", False)
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
return {
|
|
464
|
-
"successful": successful_results,
|
|
465
|
-
"failed": failed_results,
|
|
466
|
-
"success_rate": len(successful_results) / len(results) if results else 0
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
except Exception as e:
|
|
470
|
-
logger.exception("Failed to process LLM response")
|
|
471
|
-
raise
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
#### Testing Your Tools
|
|
475
|
-
```python
|
|
476
|
-
import pytest
|
|
477
|
-
from chuk_tool_processor import ToolProcessor, initialize
|
|
478
|
-
|
|
479
|
-
@pytest.mark.asyncio
|
|
480
|
-
async def test_calculator_tool():
|
|
481
|
-
await initialize()
|
|
482
|
-
processor = ToolProcessor()
|
|
483
|
-
|
|
484
|
-
results = await processor.process(
|
|
485
|
-
'<tool name="calculator" args=\'{"operation": "add", "a": 5, "b": 3}\'/>'
|
|
486
|
-
)
|
|
487
|
-
|
|
488
|
-
assert len(results) == 1
|
|
489
|
-
result = results[0]
|
|
490
|
-
assert result.error is None
|
|
491
|
-
assert result.result["result"] == 8
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
## Advanced Configuration
|
|
495
|
-
|
|
496
|
-
### Production-Ready Setup
|
|
497
|
-
```python
|
|
498
|
-
from chuk_tool_processor import ToolProcessor
|
|
499
|
-
from chuk_tool_processor.execution.strategies.subprocess_strategy import SubprocessStrategy
|
|
500
|
-
|
|
501
|
-
async def create_production_processor():
|
|
502
|
-
"""Configure processor for high-throughput production use."""
|
|
503
|
-
|
|
504
|
-
processor = ToolProcessor(
|
|
505
|
-
# Execution settings
|
|
506
|
-
default_timeout=30.0,
|
|
507
|
-
max_concurrency=20, # Allow 20 concurrent executions
|
|
508
|
-
|
|
509
|
-
# Use subprocess strategy for isolation
|
|
510
|
-
strategy=SubprocessStrategy(
|
|
511
|
-
registry=await get_default_registry(),
|
|
512
|
-
max_workers=8, # 8 worker processes
|
|
513
|
-
default_timeout=30.0
|
|
514
|
-
),
|
|
515
|
-
|
|
516
|
-
# Performance optimizations
|
|
517
|
-
enable_caching=True,
|
|
518
|
-
cache_ttl=900, # 15-minute cache
|
|
519
|
-
|
|
520
|
-
# Rate limiting to prevent abuse
|
|
521
|
-
enable_rate_limiting=True,
|
|
522
|
-
global_rate_limit=500, # 500 requests per minute globally
|
|
523
|
-
tool_rate_limits={
|
|
524
|
-
"expensive_api": (10, 60), # 10 per minute
|
|
525
|
-
"file_processor": (5, 60), # 5 per minute
|
|
526
|
-
},
|
|
527
|
-
|
|
528
|
-
# Reliability features
|
|
529
|
-
enable_retries=True,
|
|
530
|
-
max_retries=3,
|
|
531
|
-
|
|
532
|
-
# Input parsing
|
|
533
|
-
parser_plugins=["xml_tool", "openai_tool", "json_tool"]
|
|
534
|
-
)
|
|
535
|
-
|
|
536
|
-
await processor.initialize()
|
|
537
|
-
return processor
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
### Performance Optimization
|
|
541
|
-
```python
|
|
542
|
-
# Concurrent batch processing
|
|
543
|
-
async def process_batch(requests: list[str]):
|
|
544
|
-
"""Process multiple LLM responses concurrently."""
|
|
545
|
-
processor = await create_production_processor()
|
|
546
|
-
|
|
547
|
-
tasks = [processor.process(request) for request in requests]
|
|
548
|
-
all_results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
549
|
-
|
|
550
|
-
successful = []
|
|
551
|
-
failed = []
|
|
552
|
-
|
|
553
|
-
for i, result in enumerate(all_results):
|
|
554
|
-
if isinstance(result, Exception):
|
|
555
|
-
failed.append({"request_index": i, "error": str(result)})
|
|
556
|
-
else:
|
|
557
|
-
successful.append({"request_index": i, "results": result})
|
|
558
|
-
|
|
559
|
-
return {"successful": successful, "failed": failed}
|
|
560
|
-
|
|
561
|
-
# Memory management for long-running applications
|
|
562
|
-
async def maintenance_task():
|
|
563
|
-
"""Periodic maintenance for production deployments."""
|
|
564
|
-
while True:
|
|
565
|
-
await asyncio.sleep(3600) # Every hour
|
|
566
|
-
|
|
567
|
-
# Clear old cache entries
|
|
568
|
-
if hasattr(processor.executor, 'cache'):
|
|
569
|
-
await processor.executor.cache.clear()
|
|
570
|
-
logger.info("Cache cleared for memory management")
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
## Key Design Patterns
|
|
574
|
-
|
|
575
|
-
1. **Async-First Design**: All core operations use async/await with proper timeout handling, graceful cancellation support, and comprehensive resource cleanup via context managers.
|
|
576
|
-
|
|
577
|
-
2. **Strategy Pattern**: Pluggable execution strategies (InProcess vs Subprocess), composable wrapper chains, and interface-driven design for maximum flexibility.
|
|
578
|
-
|
|
579
|
-
3. **Registry Pattern**: Centralized tool management with namespace isolation, rich metadata tracking, and lazy initialization for optimal resource usage.
|
|
580
|
-
|
|
581
|
-
4. **Plugin Architecture**: Discoverable parsers for different input formats, transport abstractions for MCP integration, and extensible validation systems.
|
|
582
|
-
|
|
583
|
-
5. **Producer-Consumer**: Queue-based streaming architecture for real-time results, with proper backpressure handling and timeout coordination.
|
|
584
|
-
|
|
585
|
-
6. **Decorator Pattern**: Composable execution wrappers (caching, retries, rate limiting) that can be stacked and configured independently.
|
|
586
|
-
|
|
587
|
-
## Configuration Reference
|
|
588
|
-
|
|
589
|
-
### Environment Variables
|
|
590
|
-
| Variable | Default | Description |
|
|
591
|
-
|----------|---------|-------------|
|
|
592
|
-
| `CHUK_TOOL_REGISTRY_PROVIDER` | `memory` | Registry backend (memory, redis, etc.) |
|
|
593
|
-
| `CHUK_DEFAULT_TIMEOUT` | `30.0` | Default tool execution timeout (seconds) |
|
|
594
|
-
| `CHUK_LOG_LEVEL` | `INFO` | Logging level (DEBUG, INFO, WARNING, ERROR) |
|
|
595
|
-
| `CHUK_STRUCTURED_LOGGING` | `true` | Enable JSON structured logging |
|
|
596
|
-
| `CHUK_MAX_CONCURRENCY` | `10` | Default max concurrent executions |
|
|
597
|
-
| `MCP_BEARER_TOKEN` | - | Bearer token for MCP SSE authentication |
|
|
598
|
-
|
|
599
|
-
### ToolProcessor Options
|
|
600
|
-
```python
|
|
601
|
-
processor = ToolProcessor(
|
|
602
|
-
# Core execution
|
|
603
|
-
default_timeout=30.0, # Default timeout per tool
|
|
604
|
-
max_concurrency=10, # Max concurrent executions
|
|
605
|
-
|
|
606
|
-
# Strategy selection
|
|
607
|
-
strategy=InProcessStrategy(...), # Fast, shared memory
|
|
608
|
-
# strategy=SubprocessStrategy(...), # Isolated, safer for untrusted code
|
|
609
|
-
|
|
610
|
-
# Performance features
|
|
611
|
-
enable_caching=True, # Result caching
|
|
612
|
-
cache_ttl=300, # Cache TTL in seconds
|
|
613
|
-
enable_rate_limiting=False, # Rate limiting
|
|
614
|
-
enable_retries=True, # Automatic retries
|
|
615
|
-
max_retries=3, # Max retry attempts
|
|
616
|
-
|
|
617
|
-
# Input processing
|
|
618
|
-
parser_plugins=["xml_tool", "openai_tool", "json_tool"]
|
|
619
|
-
)
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
## Why Choose CHUK Tool Processor?
|
|
623
|
-
|
|
624
|
-
### Built for Production
|
|
625
|
-
- **Battle-tested**: Comprehensive error handling, timeout management, and resource cleanup
|
|
626
|
-
- **Scalable**: Support for high-throughput concurrent execution with configurable limits
|
|
627
|
-
- **Observable**: Built-in structured logging, metrics collection, and request tracing
|
|
628
|
-
- **Reliable**: Automatic retries, circuit breakers, and graceful degradation
|
|
629
|
-
|
|
630
|
-
### Developer Experience
|
|
631
|
-
- **Zero-config start**: Works out of the box with sensible defaults
|
|
632
|
-
- **Type-safe**: Full Pydantic integration for argument and result validation
|
|
633
|
-
- **Multiple paradigms**: Support for functions, classes, and streaming tools
|
|
634
|
-
- **Flexible inputs**: Handles XML tags, OpenAI format, JSON, and direct objects
|
|
635
|
-
|
|
636
|
-
### Enterprise Ready
|
|
637
|
-
- **Process isolation**: Subprocess strategy for running untrusted code safely
|
|
638
|
-
- **Rate limiting**: Global and per-tool rate limiting with sliding window algorithm
|
|
639
|
-
- **Caching layer**: Intelligent caching with TTL and invalidation strategies
|
|
640
|
-
- **MCP integration**: Connect to external tool servers using industry standards
|
|
641
|
-
|
|
642
|
-
### Performance Optimized
|
|
643
|
-
- **Async-native**: Built from ground up for `async/await` with proper concurrency
|
|
644
|
-
- **Streaming support**: Real-time incremental results for long-running operations
|
|
645
|
-
- **Resource efficient**: Lazy initialization, connection pooling, and memory management
|
|
646
|
-
- **Configurable strategies**: Choose between speed (in-process) and safety (subprocess)
|
|
647
|
-
|
|
648
|
-
## Getting Started
|
|
649
|
-
|
|
650
|
-
### 1. Installation
|
|
651
|
-
```bash
|
|
652
|
-
# From source (recommended for development)
|
|
653
|
-
git clone https://github.com/chrishayuk/chuk-tool-processor.git
|
|
654
|
-
cd chuk-tool-processor
|
|
655
|
-
pip install -e .
|
|
656
|
-
|
|
657
|
-
# Or install from PyPI (when available)
|
|
658
|
-
pip install chuk-tool-processor
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
### 2. Quick Example
|
|
662
|
-
```python
|
|
663
|
-
import asyncio
|
|
664
|
-
from chuk_tool_processor import ToolProcessor, register_tool, initialize
|
|
665
|
-
|
|
666
|
-
@register_tool(name="hello")
|
|
667
|
-
class HelloTool:
|
|
668
|
-
async def execute(self, name: str) -> str:
|
|
669
|
-
return f"Hello, {name}!"
|
|
670
|
-
|
|
671
|
-
async def main():
|
|
672
|
-
await initialize()
|
|
673
|
-
processor = ToolProcessor()
|
|
674
|
-
|
|
675
|
-
results = await processor.process(
|
|
676
|
-
'<tool name="hello" args=\'{"name": "World"}\'/>'
|
|
677
|
-
)
|
|
678
|
-
|
|
679
|
-
print(results[0].result) # Output: Hello, World!
|
|
680
|
-
|
|
681
|
-
asyncio.run(main())
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
### 3. Next Steps
|
|
685
|
-
- Review the [Architecture Guide](docs/architecture.md) for deeper understanding
|
|
686
|
-
- Check out [Tool Development Guide](docs/tools.md) for advanced patterns
|
|
687
|
-
- Explore [MCP Integration](docs/mcp.md) for external tool servers
|
|
688
|
-
- See [Production Deployment](docs/deployment.md) for scaling considerations
|
|
689
|
-
|
|
690
|
-
## Contributing & Support
|
|
691
|
-
|
|
692
|
-
- **GitHub**: [chrishayuk/chuk-tool-processor](https://github.com/chrishayuk/chuk-tool-processor)
|
|
693
|
-
- **Issues**: [Report bugs and request features](https://github.com/chrishayuk/chuk-tool-processor/issues)
|
|
694
|
-
- **Discussions**: [Community discussions](https://github.com/chrishayuk/chuk-tool-processor/discussions)
|
|
695
|
-
- **License**: MIT - see [LICENSE](LICENSE) file for details
|
|
696
|
-
|
|
697
|
-
Built with ❤️ by the CHUK AI team for the LLM tool integration community.
|