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.

Files changed (66) hide show
  1. chuk_tool_processor/core/__init__.py +32 -1
  2. chuk_tool_processor/core/exceptions.py +225 -13
  3. chuk_tool_processor/core/processor.py +135 -104
  4. chuk_tool_processor/execution/strategies/__init__.py +6 -0
  5. chuk_tool_processor/execution/strategies/inprocess_strategy.py +142 -150
  6. chuk_tool_processor/execution/strategies/subprocess_strategy.py +202 -206
  7. chuk_tool_processor/execution/tool_executor.py +82 -84
  8. chuk_tool_processor/execution/wrappers/__init__.py +42 -0
  9. chuk_tool_processor/execution/wrappers/caching.py +150 -116
  10. chuk_tool_processor/execution/wrappers/circuit_breaker.py +370 -0
  11. chuk_tool_processor/execution/wrappers/rate_limiting.py +76 -43
  12. chuk_tool_processor/execution/wrappers/retry.py +116 -78
  13. chuk_tool_processor/logging/__init__.py +23 -17
  14. chuk_tool_processor/logging/context.py +40 -45
  15. chuk_tool_processor/logging/formatter.py +22 -21
  16. chuk_tool_processor/logging/helpers.py +28 -42
  17. chuk_tool_processor/logging/metrics.py +13 -15
  18. chuk_tool_processor/mcp/__init__.py +8 -12
  19. chuk_tool_processor/mcp/mcp_tool.py +158 -114
  20. chuk_tool_processor/mcp/register_mcp_tools.py +22 -22
  21. chuk_tool_processor/mcp/setup_mcp_http_streamable.py +57 -17
  22. chuk_tool_processor/mcp/setup_mcp_sse.py +57 -17
  23. chuk_tool_processor/mcp/setup_mcp_stdio.py +11 -11
  24. chuk_tool_processor/mcp/stream_manager.py +333 -276
  25. chuk_tool_processor/mcp/transport/__init__.py +22 -29
  26. chuk_tool_processor/mcp/transport/base_transport.py +180 -44
  27. chuk_tool_processor/mcp/transport/http_streamable_transport.py +505 -325
  28. chuk_tool_processor/mcp/transport/models.py +100 -0
  29. chuk_tool_processor/mcp/transport/sse_transport.py +607 -276
  30. chuk_tool_processor/mcp/transport/stdio_transport.py +597 -116
  31. chuk_tool_processor/models/__init__.py +21 -1
  32. chuk_tool_processor/models/execution_strategy.py +16 -21
  33. chuk_tool_processor/models/streaming_tool.py +28 -25
  34. chuk_tool_processor/models/tool_call.py +49 -31
  35. chuk_tool_processor/models/tool_export_mixin.py +22 -8
  36. chuk_tool_processor/models/tool_result.py +40 -77
  37. chuk_tool_processor/models/tool_spec.py +350 -0
  38. chuk_tool_processor/models/validated_tool.py +36 -18
  39. chuk_tool_processor/observability/__init__.py +30 -0
  40. chuk_tool_processor/observability/metrics.py +312 -0
  41. chuk_tool_processor/observability/setup.py +105 -0
  42. chuk_tool_processor/observability/tracing.py +345 -0
  43. chuk_tool_processor/plugins/__init__.py +1 -1
  44. chuk_tool_processor/plugins/discovery.py +11 -11
  45. chuk_tool_processor/plugins/parsers/__init__.py +1 -1
  46. chuk_tool_processor/plugins/parsers/base.py +1 -2
  47. chuk_tool_processor/plugins/parsers/function_call_tool.py +13 -8
  48. chuk_tool_processor/plugins/parsers/json_tool.py +4 -3
  49. chuk_tool_processor/plugins/parsers/openai_tool.py +12 -7
  50. chuk_tool_processor/plugins/parsers/xml_tool.py +4 -4
  51. chuk_tool_processor/registry/__init__.py +12 -12
  52. chuk_tool_processor/registry/auto_register.py +22 -30
  53. chuk_tool_processor/registry/decorators.py +127 -129
  54. chuk_tool_processor/registry/interface.py +26 -23
  55. chuk_tool_processor/registry/metadata.py +27 -22
  56. chuk_tool_processor/registry/provider.py +17 -18
  57. chuk_tool_processor/registry/providers/__init__.py +16 -19
  58. chuk_tool_processor/registry/providers/memory.py +18 -25
  59. chuk_tool_processor/registry/tool_export.py +42 -51
  60. chuk_tool_processor/utils/validation.py +15 -16
  61. chuk_tool_processor-0.9.7.dist-info/METADATA +1813 -0
  62. chuk_tool_processor-0.9.7.dist-info/RECORD +67 -0
  63. chuk_tool_processor-0.6.4.dist-info/METADATA +0 -697
  64. chuk_tool_processor-0.6.4.dist-info/RECORD +0 -60
  65. {chuk_tool_processor-0.6.4.dist-info → chuk_tool_processor-0.9.7.dist-info}/WHEEL +0 -0
  66. {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.