chuk-tool-processor 0.15__tar.gz → 0.18__tar.gz
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.
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/PKG-INFO +229 -1
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/README.md +223 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/pyproject.toml +14 -1
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/__init__.py +186 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/config.py +576 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/core/__init__.py +15 -1
- chuk_tool_processor-0.18/src/chuk_tool_processor/core/exceptions.py +691 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/__init__.py +77 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/dynamic_provider.py +629 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/search.py +883 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/searchable.py +97 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/synonyms.py +752 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/strategies/subprocess_strategy.py +50 -5
- chuk_tool_processor-0.18/src/chuk_tool_processor/execution/wrappers/__init__.py +107 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/wrappers/circuit_breaker.py +3 -3
- chuk_tool_processor-0.18/src/chuk_tool_processor/execution/wrappers/factory.py +503 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/execution/wrappers/redis_circuit_breaker.py +634 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/execution/wrappers/redis_rate_limiting.py +328 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/wrappers/retry.py +10 -3
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/__init__.py +239 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/assumption_trace.py +445 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/base.py +140 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/budget.py +201 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/chain.py +275 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/concurrency.py +208 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/models.py +202 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/network_policy.py +306 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/output_size.py +290 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/per_tool.py +121 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/plan_shape.py +349 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/precondition.py +177 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/provenance.py +276 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/retry_safety.py +335 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/runaway.py +129 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/saturation.py +215 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/schema_strictness.py +350 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/sensitive_data.py +283 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/side_effect.py +273 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/timeout_budget.py +261 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/guards/unresolved.py +139 -0
- chuk_tool_processor-0.18/src/chuk_tool_processor/models/tool_result.py +300 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/decorators.py +3 -3
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/providers/__init__.py +24 -3
- chuk_tool_processor-0.18/src/chuk_tool_processor/registry/providers/redis.py +555 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/PKG-INFO +229 -1
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/SOURCES.txt +31 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/requires.txt +7 -0
- chuk_tool_processor-0.15/src/chuk_tool_processor/core/exceptions.py +0 -308
- chuk_tool_processor-0.15/src/chuk_tool_processor/execution/wrappers/__init__.py +0 -42
- chuk_tool_processor-0.15/src/chuk_tool_processor/models/tool_result.py +0 -120
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/setup.cfg +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/core/context.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/core/processor.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/bulkhead.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/code_sandbox.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/strategies/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/strategies/inprocess_strategy.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/tool_executor.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/wrappers/caching.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/wrappers/rate_limiting.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/context.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/formatter.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/helpers.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/metrics.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/mcp_tool.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/middleware.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/models.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/register_mcp_tools.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/setup_mcp_http_streamable.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/setup_mcp_sse.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/setup_mcp_stdio.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/stream_manager.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/base_transport.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/http_streamable_transport.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/models.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/sse_transport.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/stdio_transport.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/execution_strategy.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/return_order.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/streaming_tool.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/tool_call.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/tool_export_mixin.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/tool_spec.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/validated_tool.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/observability/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/observability/metrics.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/observability/setup.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/observability/tracing.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/discovery.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/base.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/function_call_tool.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/json_tool.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/openai_tool.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/xml_tool.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/py.typed +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/auto_register.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/interface.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/metadata.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/provider.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/providers/memory.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/tool_export.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/scheduling/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/scheduling/greedy_dag.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/scheduling/policy.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/scheduling/types.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/utils/__init__.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/utils/fast_json.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/utils/validation.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/dependency_links.txt +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/top_level.txt +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/tests/test_bulkhead.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/tests/test_execution_context.py +0 -0
- {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/tests/test_scoped_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chuk-tool-processor
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18
|
|
4
4
|
Summary: Async-native framework for registering, discovering, and executing tools referenced in LLM responses
|
|
5
5
|
Author-email: CHUK Team <chrishayuk@somejunkmailbox.com>
|
|
6
6
|
Maintainer-email: CHUK Team <chrishayuk@somejunkmailbox.com>
|
|
@@ -27,8 +27,13 @@ Requires-Dist: pydantic>=2.11.3
|
|
|
27
27
|
Requires-Dist: uuid>=1.30
|
|
28
28
|
Provides-Extra: fast-json
|
|
29
29
|
Requires-Dist: orjson<4,>=3.10.0; extra == "fast-json"
|
|
30
|
+
Provides-Extra: redis
|
|
31
|
+
Requires-Dist: redis[hiredis]<6,>=5.0.0; extra == "redis"
|
|
30
32
|
Provides-Extra: full
|
|
31
33
|
Requires-Dist: orjson<4,>=3.10.0; extra == "full"
|
|
34
|
+
Provides-Extra: all
|
|
35
|
+
Requires-Dist: orjson<4,>=3.10.0; extra == "all"
|
|
36
|
+
Requires-Dist: redis[hiredis]<6,>=5.0.0; extra == "all"
|
|
32
37
|
|
|
33
38
|
# CHUK Tool Processor — A Tool Execution Runtime for AI Systems
|
|
34
39
|
|
|
@@ -182,6 +187,7 @@ results = await processor.process(json_output)
|
|
|
182
187
|
| **Rate Limiting** | Global and per-tool rate limits with sliding windows |
|
|
183
188
|
| **Caching** | Result caching with TTL and SHA256-based idempotency keys |
|
|
184
189
|
| **Circuit Breakers** | Prevent cascading failures with automatic recovery |
|
|
190
|
+
| **Structured Errors** | Machine-readable error categories with retry hints for planners |
|
|
185
191
|
|
|
186
192
|
### Multi-Tenant & Isolation
|
|
187
193
|
|
|
@@ -192,6 +198,7 @@ results = await processor.process(json_output)
|
|
|
192
198
|
| **Scoped Registries** | Isolated registries for multi-tenant apps and testing |
|
|
193
199
|
| **ExecutionContext** | Request-scoped metadata propagation (user, tenant, tracing, deadlines) |
|
|
194
200
|
| **Isolated Strategy** | Subprocess execution for untrusted code (zero crash blast radius) |
|
|
201
|
+
| **Redis Registry** | Distributed tool registry for multi-process/multi-machine deployments |
|
|
195
202
|
|
|
196
203
|
### Advanced Scheduling
|
|
197
204
|
|
|
@@ -201,6 +208,32 @@ results = await processor.process(json_output)
|
|
|
201
208
|
| **SchedulerPolicy** | DAG-based scheduling with dependencies, deadlines, pool limits |
|
|
202
209
|
| **GreedyDagScheduler** | Built-in scheduler with topological sort and deadline-aware skipping |
|
|
203
210
|
|
|
211
|
+
### Runtime Guards (Constitution Layer)
|
|
212
|
+
|
|
213
|
+
| Guard | Description |
|
|
214
|
+
|-------|-------------|
|
|
215
|
+
| **SchemaStrictnessGuard** | Validates arguments against JSON schemas, optional type coercion |
|
|
216
|
+
| **SensitiveDataGuard** | Detects and blocks/redacts secrets (API keys, JWTs, private keys) |
|
|
217
|
+
| **NetworkPolicyGuard** | SSRF defense — blocks private IPs, metadata endpoints, enforces HTTPS |
|
|
218
|
+
| **SideEffectGuard** | Labels tools as read_only/write/destructive, enforces policies |
|
|
219
|
+
| **ConcurrencyGuard** | Limits simultaneous in-flight calls (global, per-tool, per-namespace) |
|
|
220
|
+
| **TimeoutBudgetGuard** | Enforces wall-clock time budgets with soft/hard limits |
|
|
221
|
+
| **OutputSizeGuard** | Prevents pathological payloads from blowing up context |
|
|
222
|
+
| **RetrySafetyGuard** | Guards retry behavior (backoff, idempotency keys, non-retryable errors) |
|
|
223
|
+
| **ProvenanceGuard** | Tracks output attribution and lineage |
|
|
224
|
+
| **PlanShapeGuard** | Detects pathological patterns (fan-out explosions, long chains) |
|
|
225
|
+
| **SaturationGuard** | Detects degenerate statistical outputs (extreme Z-scores, saturated CDFs) |
|
|
226
|
+
|
|
227
|
+
### Dynamic Tool Discovery
|
|
228
|
+
|
|
229
|
+
| Feature | Description |
|
|
230
|
+
|---------|-------------|
|
|
231
|
+
| **Intelligent Search** | Natural language queries find tools ("gaussian" → "normal_cdf") |
|
|
232
|
+
| **Synonym Expansion** | Built-in synonyms for math, statistics, file ops, networking |
|
|
233
|
+
| **Fuzzy Matching** | Typo tolerance ("multipley" finds "multiply") |
|
|
234
|
+
| **Session Boosting** | Recently used tools rank higher in search results |
|
|
235
|
+
| **Dynamic Provider** | Base class for LLM-driven tool discovery and execution |
|
|
236
|
+
|
|
204
237
|
### Integration & Observability
|
|
205
238
|
|
|
206
239
|
| Feature | Description |
|
|
@@ -365,6 +398,157 @@ result = await middleware.call_tool("notion.search", {"query": "docs"})
|
|
|
365
398
|
|
|
366
399
|
---
|
|
367
400
|
|
|
401
|
+
## Distributed Deployments (Redis)
|
|
402
|
+
|
|
403
|
+
For multi-process or multi-machine deployments, configure Redis backends via environment variables:
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
# Enable Redis for everything
|
|
407
|
+
export CHUK_REGISTRY_BACKEND=redis
|
|
408
|
+
export CHUK_RESILIENCE_BACKEND=redis
|
|
409
|
+
export CHUK_REDIS_URL=redis://localhost:6379/0
|
|
410
|
+
|
|
411
|
+
# Enable resilience features
|
|
412
|
+
export CHUK_CIRCUIT_BREAKER_ENABLED=true
|
|
413
|
+
export CHUK_RATE_LIMIT_ENABLED=true
|
|
414
|
+
export CHUK_RATE_LIMIT_GLOBAL=100
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
from chuk_tool_processor import ProcessorConfig
|
|
419
|
+
|
|
420
|
+
# Load from environment and create fully-configured processor
|
|
421
|
+
config = ProcessorConfig.from_env()
|
|
422
|
+
processor = await config.create_processor()
|
|
423
|
+
|
|
424
|
+
async with processor:
|
|
425
|
+
results = await processor.process(llm_output)
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Or configure programmatically:
|
|
429
|
+
|
|
430
|
+
```python
|
|
431
|
+
from chuk_tool_processor import ProcessorConfig, RegistryConfig, BackendType
|
|
432
|
+
from chuk_tool_processor.config import CircuitBreakerConfig, RateLimitConfig
|
|
433
|
+
|
|
434
|
+
config = ProcessorConfig(
|
|
435
|
+
# Registry and resilience use Redis
|
|
436
|
+
registry=RegistryConfig(backend=BackendType.REDIS),
|
|
437
|
+
resilience_backend=BackendType.REDIS,
|
|
438
|
+
redis_url="redis://localhost:6379/0",
|
|
439
|
+
|
|
440
|
+
# Enable features
|
|
441
|
+
circuit_breaker=CircuitBreakerConfig(enabled=True, failure_threshold=5),
|
|
442
|
+
rate_limit=RateLimitConfig(enabled=True, global_limit=100),
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
processor = await config.create_processor()
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Key features:**
|
|
449
|
+
- **Distributed registry**: Tool metadata shared across processes
|
|
450
|
+
- **Distributed circuit breaker**: Failure counts shared (prevents cascading failures across instances)
|
|
451
|
+
- **Distributed rate limiting**: Global limits enforced across all instances
|
|
452
|
+
- **Multi-tenant isolation**: Key prefixes isolate data per tenant
|
|
453
|
+
|
|
454
|
+
**Installation:**
|
|
455
|
+
```bash
|
|
456
|
+
pip install chuk-tool-processor[redis] # or: uv add chuk-tool-processor[redis]
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
See [examples/02_production_features/distributed_config_demo.py](examples/02_production_features/distributed_config_demo.py) for a complete example.
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Runtime Guards
|
|
464
|
+
|
|
465
|
+
Protect your tool execution with composable guards that enforce safety policies:
|
|
466
|
+
|
|
467
|
+
```python
|
|
468
|
+
from chuk_tool_processor.guards import (
|
|
469
|
+
GuardChain,
|
|
470
|
+
SchemaStrictnessGuard,
|
|
471
|
+
SensitiveDataGuard,
|
|
472
|
+
NetworkPolicyGuard,
|
|
473
|
+
ConcurrencyGuard,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
# Create individual guards
|
|
477
|
+
schema_guard = SchemaStrictnessGuard(get_schema=my_schema_getter)
|
|
478
|
+
sensitive_guard = SensitiveDataGuard() # Detects API keys, JWTs, etc.
|
|
479
|
+
network_guard = NetworkPolicyGuard(block_private_ips=True)
|
|
480
|
+
concurrency_guard = ConcurrencyGuard(global_max=50, per_tool_max={"heavy_api": 2})
|
|
481
|
+
|
|
482
|
+
# Compose into a chain
|
|
483
|
+
chain = GuardChain([schema_guard, sensitive_guard, network_guard, concurrency_guard])
|
|
484
|
+
|
|
485
|
+
# Check before execution
|
|
486
|
+
result = await chain.check_all_async("api.fetch", {"url": "https://example.com"})
|
|
487
|
+
if result.blocked:
|
|
488
|
+
print(f"Blocked by {result.stopped_at}: {result.reason}")
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**Key Guards:**
|
|
492
|
+
- **SchemaStrictnessGuard** — Validate args against JSON schemas, auto-coerce types
|
|
493
|
+
- **SensitiveDataGuard** — Block or redact secrets (API keys, JWTs, private keys)
|
|
494
|
+
- **NetworkPolicyGuard** — SSRF defense (block localhost, private IPs, metadata endpoints)
|
|
495
|
+
- **SideEffectGuard** — Enforce read-only mode, block destructive ops in production
|
|
496
|
+
- **ConcurrencyGuard** — Limit in-flight calls globally, per-tool, or per-namespace
|
|
497
|
+
- **TimeoutBudgetGuard** — Enforce wall-clock budgets with soft/hard limits
|
|
498
|
+
- **OutputSizeGuard** — Prevent pathological payloads (size, depth, array length)
|
|
499
|
+
- **SaturationGuard** — Detect degenerate statistical outputs (extreme Z-scores, saturated CDFs)
|
|
500
|
+
|
|
501
|
+
See [GUARDS.md](docs/GUARDS.md) for complete documentation and examples.
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## Dynamic Tool Discovery
|
|
506
|
+
|
|
507
|
+
When you have hundreds of tools, LLMs can't load all schemas upfront. The discovery module provides intelligent search and on-demand tool loading:
|
|
508
|
+
|
|
509
|
+
```python
|
|
510
|
+
from chuk_tool_processor.discovery import ToolSearchEngine, BaseDynamicToolProvider
|
|
511
|
+
|
|
512
|
+
# Create a search engine for your tools
|
|
513
|
+
engine = ToolSearchEngine()
|
|
514
|
+
engine.set_tools(my_tools)
|
|
515
|
+
|
|
516
|
+
# Natural language search with synonym expansion
|
|
517
|
+
results = engine.search("gaussian distribution") # Finds "normal_cdf"
|
|
518
|
+
results = engine.search("find the average") # Finds "calculate_mean"
|
|
519
|
+
results = engine.search("multipley") # Finds "multiply" (typo tolerance)
|
|
520
|
+
|
|
521
|
+
# Session boosting - recently used tools rank higher
|
|
522
|
+
engine.record_tool_use("calculate_mean", success=True)
|
|
523
|
+
engine.advance_turn()
|
|
524
|
+
results = engine.search("calculate") # "calculate_mean" now boosted
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**Dynamic Provider Pattern** — give LLMs meta-tools for discovery:
|
|
528
|
+
|
|
529
|
+
```python
|
|
530
|
+
class MyToolProvider(BaseDynamicToolProvider):
|
|
531
|
+
async def get_all_tools(self) -> list[Tool]:
|
|
532
|
+
return self._tools
|
|
533
|
+
|
|
534
|
+
async def execute_tool(self, name: str, args: dict) -> dict:
|
|
535
|
+
return await self._tools[name].execute(**args)
|
|
536
|
+
|
|
537
|
+
provider = MyToolProvider()
|
|
538
|
+
|
|
539
|
+
# LLM gets 4 meta-tools: list_tools, search_tools, get_tool_schema, call_tool
|
|
540
|
+
tools_for_llm = provider.get_dynamic_tools()
|
|
541
|
+
|
|
542
|
+
# LLM workflow: search → get schema → call
|
|
543
|
+
results = await provider.search_tools("calculate average")
|
|
544
|
+
schema = await provider.get_tool_schema("calculate_mean")
|
|
545
|
+
result = await provider.call_tool("calculate_mean", {"values": [1, 2, 3]})
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
See [DISCOVERY.md](docs/DISCOVERY.md) for complete documentation.
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
368
552
|
## Observability
|
|
369
553
|
|
|
370
554
|
One-line setup for production monitoring:
|
|
@@ -391,6 +575,30 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
|
|
|
391
575
|
|
|
392
576
|
---
|
|
393
577
|
|
|
578
|
+
## Structured Error Handling
|
|
579
|
+
|
|
580
|
+
Errors include machine-readable categories and retry hints for planner decision-making:
|
|
581
|
+
|
|
582
|
+
```python
|
|
583
|
+
from chuk_tool_processor.core.exceptions import ErrorCategory
|
|
584
|
+
|
|
585
|
+
results = await processor.process(llm_output)
|
|
586
|
+
for result in results:
|
|
587
|
+
if result.error_info:
|
|
588
|
+
match result.error_info.category:
|
|
589
|
+
case ErrorCategory.RATE_LIMIT:
|
|
590
|
+
await asyncio.sleep(result.retry_after_ms / 1000)
|
|
591
|
+
return await retry()
|
|
592
|
+
case ErrorCategory.CIRCUIT_OPEN:
|
|
593
|
+
return await use_fallback_tool()
|
|
594
|
+
case _ if not result.retryable:
|
|
595
|
+
return await report_permanent_failure()
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
See [ERRORS.md](docs/ERRORS.md) for complete error taxonomy.
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
394
602
|
## Documentation
|
|
395
603
|
|
|
396
604
|
| Document | Description |
|
|
@@ -398,6 +606,8 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
|
|
|
398
606
|
| [**GETTING_STARTED.md**](docs/GETTING_STARTED.md) | Creating tools, using the processor, ValidatedTool, StreamingTool |
|
|
399
607
|
| [**CORE_CONCEPTS.md**](docs/CORE_CONCEPTS.md) | Registry, strategies, wrappers, parsers, MCP overview |
|
|
400
608
|
| [**PRODUCTION_PATTERNS.md**](docs/PRODUCTION_PATTERNS.md) | Bulkheads, scoped registries, ExecutionContext, parallel execution |
|
|
609
|
+
| [**DISCOVERY.md**](docs/DISCOVERY.md) | Dynamic tool discovery, intelligent search, synonym expansion |
|
|
610
|
+
| [**GUARDS.md**](docs/GUARDS.md) | Runtime guards for safety, validation, and resource management |
|
|
401
611
|
| [**MCP_INTEGRATION.md**](docs/MCP_INTEGRATION.md) | HTTP Streamable, STDIO, SSE, OAuth, Middleware Stack |
|
|
402
612
|
| [**ADVANCED_TOPICS.md**](docs/ADVANCED_TOPICS.md) | Deferred loading, code sandbox, isolated strategy, testing |
|
|
403
613
|
| [**CONFIGURATION.md**](docs/CONFIGURATION.md) | All config options and environment variables |
|
|
@@ -412,6 +622,9 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
|
|
|
412
622
|
# Getting started
|
|
413
623
|
python examples/01_getting_started/hello_tool.py
|
|
414
624
|
|
|
625
|
+
# Dynamic tool discovery (search, synonyms, fuzzy matching)
|
|
626
|
+
python examples/07_discovery/dynamic_tools_demo.py
|
|
627
|
+
|
|
415
628
|
# Hero demo: 8 tools, 5-second deadline, 3 pools (DAG + bulkheads + context)
|
|
416
629
|
python examples/02_production_features/hero_runtime_demo.py
|
|
417
630
|
|
|
@@ -421,6 +634,18 @@ python examples/02_production_features/production_patterns_demo.py
|
|
|
421
634
|
# Runtime features (return order, pattern bulkheads, scheduling)
|
|
422
635
|
python examples/02_production_features/runtime_features_demo.py
|
|
423
636
|
|
|
637
|
+
# Structured error handling for planners
|
|
638
|
+
python examples/02_production_features/structured_errors_demo.py
|
|
639
|
+
|
|
640
|
+
# Runtime guards (validation, security, resource limits)
|
|
641
|
+
python examples/guards_demo.py
|
|
642
|
+
|
|
643
|
+
# Redis registry for distributed deployments
|
|
644
|
+
python examples/02_production_features/redis_registry_demo.py
|
|
645
|
+
|
|
646
|
+
# Distributed configuration (Redis registry + resilience)
|
|
647
|
+
python examples/02_production_features/distributed_config_demo.py
|
|
648
|
+
|
|
424
649
|
# Observability demo
|
|
425
650
|
python examples/02_production_features/observability_demo.py
|
|
426
651
|
|
|
@@ -458,6 +683,9 @@ pip install chuk-tool-processor[observability]
|
|
|
458
683
|
# With MCP support
|
|
459
684
|
pip install chuk-tool-processor[mcp]
|
|
460
685
|
|
|
686
|
+
# With Redis registry (distributed deployments)
|
|
687
|
+
pip install chuk-tool-processor[redis]
|
|
688
|
+
|
|
461
689
|
# With fast JSON (2-3x faster with orjson)
|
|
462
690
|
pip install chuk-tool-processor[fast-json]
|
|
463
691
|
|
|
@@ -150,6 +150,7 @@ results = await processor.process(json_output)
|
|
|
150
150
|
| **Rate Limiting** | Global and per-tool rate limits with sliding windows |
|
|
151
151
|
| **Caching** | Result caching with TTL and SHA256-based idempotency keys |
|
|
152
152
|
| **Circuit Breakers** | Prevent cascading failures with automatic recovery |
|
|
153
|
+
| **Structured Errors** | Machine-readable error categories with retry hints for planners |
|
|
153
154
|
|
|
154
155
|
### Multi-Tenant & Isolation
|
|
155
156
|
|
|
@@ -160,6 +161,7 @@ results = await processor.process(json_output)
|
|
|
160
161
|
| **Scoped Registries** | Isolated registries for multi-tenant apps and testing |
|
|
161
162
|
| **ExecutionContext** | Request-scoped metadata propagation (user, tenant, tracing, deadlines) |
|
|
162
163
|
| **Isolated Strategy** | Subprocess execution for untrusted code (zero crash blast radius) |
|
|
164
|
+
| **Redis Registry** | Distributed tool registry for multi-process/multi-machine deployments |
|
|
163
165
|
|
|
164
166
|
### Advanced Scheduling
|
|
165
167
|
|
|
@@ -169,6 +171,32 @@ results = await processor.process(json_output)
|
|
|
169
171
|
| **SchedulerPolicy** | DAG-based scheduling with dependencies, deadlines, pool limits |
|
|
170
172
|
| **GreedyDagScheduler** | Built-in scheduler with topological sort and deadline-aware skipping |
|
|
171
173
|
|
|
174
|
+
### Runtime Guards (Constitution Layer)
|
|
175
|
+
|
|
176
|
+
| Guard | Description |
|
|
177
|
+
|-------|-------------|
|
|
178
|
+
| **SchemaStrictnessGuard** | Validates arguments against JSON schemas, optional type coercion |
|
|
179
|
+
| **SensitiveDataGuard** | Detects and blocks/redacts secrets (API keys, JWTs, private keys) |
|
|
180
|
+
| **NetworkPolicyGuard** | SSRF defense — blocks private IPs, metadata endpoints, enforces HTTPS |
|
|
181
|
+
| **SideEffectGuard** | Labels tools as read_only/write/destructive, enforces policies |
|
|
182
|
+
| **ConcurrencyGuard** | Limits simultaneous in-flight calls (global, per-tool, per-namespace) |
|
|
183
|
+
| **TimeoutBudgetGuard** | Enforces wall-clock time budgets with soft/hard limits |
|
|
184
|
+
| **OutputSizeGuard** | Prevents pathological payloads from blowing up context |
|
|
185
|
+
| **RetrySafetyGuard** | Guards retry behavior (backoff, idempotency keys, non-retryable errors) |
|
|
186
|
+
| **ProvenanceGuard** | Tracks output attribution and lineage |
|
|
187
|
+
| **PlanShapeGuard** | Detects pathological patterns (fan-out explosions, long chains) |
|
|
188
|
+
| **SaturationGuard** | Detects degenerate statistical outputs (extreme Z-scores, saturated CDFs) |
|
|
189
|
+
|
|
190
|
+
### Dynamic Tool Discovery
|
|
191
|
+
|
|
192
|
+
| Feature | Description |
|
|
193
|
+
|---------|-------------|
|
|
194
|
+
| **Intelligent Search** | Natural language queries find tools ("gaussian" → "normal_cdf") |
|
|
195
|
+
| **Synonym Expansion** | Built-in synonyms for math, statistics, file ops, networking |
|
|
196
|
+
| **Fuzzy Matching** | Typo tolerance ("multipley" finds "multiply") |
|
|
197
|
+
| **Session Boosting** | Recently used tools rank higher in search results |
|
|
198
|
+
| **Dynamic Provider** | Base class for LLM-driven tool discovery and execution |
|
|
199
|
+
|
|
172
200
|
### Integration & Observability
|
|
173
201
|
|
|
174
202
|
| Feature | Description |
|
|
@@ -333,6 +361,157 @@ result = await middleware.call_tool("notion.search", {"query": "docs"})
|
|
|
333
361
|
|
|
334
362
|
---
|
|
335
363
|
|
|
364
|
+
## Distributed Deployments (Redis)
|
|
365
|
+
|
|
366
|
+
For multi-process or multi-machine deployments, configure Redis backends via environment variables:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
# Enable Redis for everything
|
|
370
|
+
export CHUK_REGISTRY_BACKEND=redis
|
|
371
|
+
export CHUK_RESILIENCE_BACKEND=redis
|
|
372
|
+
export CHUK_REDIS_URL=redis://localhost:6379/0
|
|
373
|
+
|
|
374
|
+
# Enable resilience features
|
|
375
|
+
export CHUK_CIRCUIT_BREAKER_ENABLED=true
|
|
376
|
+
export CHUK_RATE_LIMIT_ENABLED=true
|
|
377
|
+
export CHUK_RATE_LIMIT_GLOBAL=100
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
from chuk_tool_processor import ProcessorConfig
|
|
382
|
+
|
|
383
|
+
# Load from environment and create fully-configured processor
|
|
384
|
+
config = ProcessorConfig.from_env()
|
|
385
|
+
processor = await config.create_processor()
|
|
386
|
+
|
|
387
|
+
async with processor:
|
|
388
|
+
results = await processor.process(llm_output)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Or configure programmatically:
|
|
392
|
+
|
|
393
|
+
```python
|
|
394
|
+
from chuk_tool_processor import ProcessorConfig, RegistryConfig, BackendType
|
|
395
|
+
from chuk_tool_processor.config import CircuitBreakerConfig, RateLimitConfig
|
|
396
|
+
|
|
397
|
+
config = ProcessorConfig(
|
|
398
|
+
# Registry and resilience use Redis
|
|
399
|
+
registry=RegistryConfig(backend=BackendType.REDIS),
|
|
400
|
+
resilience_backend=BackendType.REDIS,
|
|
401
|
+
redis_url="redis://localhost:6379/0",
|
|
402
|
+
|
|
403
|
+
# Enable features
|
|
404
|
+
circuit_breaker=CircuitBreakerConfig(enabled=True, failure_threshold=5),
|
|
405
|
+
rate_limit=RateLimitConfig(enabled=True, global_limit=100),
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
processor = await config.create_processor()
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Key features:**
|
|
412
|
+
- **Distributed registry**: Tool metadata shared across processes
|
|
413
|
+
- **Distributed circuit breaker**: Failure counts shared (prevents cascading failures across instances)
|
|
414
|
+
- **Distributed rate limiting**: Global limits enforced across all instances
|
|
415
|
+
- **Multi-tenant isolation**: Key prefixes isolate data per tenant
|
|
416
|
+
|
|
417
|
+
**Installation:**
|
|
418
|
+
```bash
|
|
419
|
+
pip install chuk-tool-processor[redis] # or: uv add chuk-tool-processor[redis]
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
See [examples/02_production_features/distributed_config_demo.py](examples/02_production_features/distributed_config_demo.py) for a complete example.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Runtime Guards
|
|
427
|
+
|
|
428
|
+
Protect your tool execution with composable guards that enforce safety policies:
|
|
429
|
+
|
|
430
|
+
```python
|
|
431
|
+
from chuk_tool_processor.guards import (
|
|
432
|
+
GuardChain,
|
|
433
|
+
SchemaStrictnessGuard,
|
|
434
|
+
SensitiveDataGuard,
|
|
435
|
+
NetworkPolicyGuard,
|
|
436
|
+
ConcurrencyGuard,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
# Create individual guards
|
|
440
|
+
schema_guard = SchemaStrictnessGuard(get_schema=my_schema_getter)
|
|
441
|
+
sensitive_guard = SensitiveDataGuard() # Detects API keys, JWTs, etc.
|
|
442
|
+
network_guard = NetworkPolicyGuard(block_private_ips=True)
|
|
443
|
+
concurrency_guard = ConcurrencyGuard(global_max=50, per_tool_max={"heavy_api": 2})
|
|
444
|
+
|
|
445
|
+
# Compose into a chain
|
|
446
|
+
chain = GuardChain([schema_guard, sensitive_guard, network_guard, concurrency_guard])
|
|
447
|
+
|
|
448
|
+
# Check before execution
|
|
449
|
+
result = await chain.check_all_async("api.fetch", {"url": "https://example.com"})
|
|
450
|
+
if result.blocked:
|
|
451
|
+
print(f"Blocked by {result.stopped_at}: {result.reason}")
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**Key Guards:**
|
|
455
|
+
- **SchemaStrictnessGuard** — Validate args against JSON schemas, auto-coerce types
|
|
456
|
+
- **SensitiveDataGuard** — Block or redact secrets (API keys, JWTs, private keys)
|
|
457
|
+
- **NetworkPolicyGuard** — SSRF defense (block localhost, private IPs, metadata endpoints)
|
|
458
|
+
- **SideEffectGuard** — Enforce read-only mode, block destructive ops in production
|
|
459
|
+
- **ConcurrencyGuard** — Limit in-flight calls globally, per-tool, or per-namespace
|
|
460
|
+
- **TimeoutBudgetGuard** — Enforce wall-clock budgets with soft/hard limits
|
|
461
|
+
- **OutputSizeGuard** — Prevent pathological payloads (size, depth, array length)
|
|
462
|
+
- **SaturationGuard** — Detect degenerate statistical outputs (extreme Z-scores, saturated CDFs)
|
|
463
|
+
|
|
464
|
+
See [GUARDS.md](docs/GUARDS.md) for complete documentation and examples.
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Dynamic Tool Discovery
|
|
469
|
+
|
|
470
|
+
When you have hundreds of tools, LLMs can't load all schemas upfront. The discovery module provides intelligent search and on-demand tool loading:
|
|
471
|
+
|
|
472
|
+
```python
|
|
473
|
+
from chuk_tool_processor.discovery import ToolSearchEngine, BaseDynamicToolProvider
|
|
474
|
+
|
|
475
|
+
# Create a search engine for your tools
|
|
476
|
+
engine = ToolSearchEngine()
|
|
477
|
+
engine.set_tools(my_tools)
|
|
478
|
+
|
|
479
|
+
# Natural language search with synonym expansion
|
|
480
|
+
results = engine.search("gaussian distribution") # Finds "normal_cdf"
|
|
481
|
+
results = engine.search("find the average") # Finds "calculate_mean"
|
|
482
|
+
results = engine.search("multipley") # Finds "multiply" (typo tolerance)
|
|
483
|
+
|
|
484
|
+
# Session boosting - recently used tools rank higher
|
|
485
|
+
engine.record_tool_use("calculate_mean", success=True)
|
|
486
|
+
engine.advance_turn()
|
|
487
|
+
results = engine.search("calculate") # "calculate_mean" now boosted
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Dynamic Provider Pattern** — give LLMs meta-tools for discovery:
|
|
491
|
+
|
|
492
|
+
```python
|
|
493
|
+
class MyToolProvider(BaseDynamicToolProvider):
|
|
494
|
+
async def get_all_tools(self) -> list[Tool]:
|
|
495
|
+
return self._tools
|
|
496
|
+
|
|
497
|
+
async def execute_tool(self, name: str, args: dict) -> dict:
|
|
498
|
+
return await self._tools[name].execute(**args)
|
|
499
|
+
|
|
500
|
+
provider = MyToolProvider()
|
|
501
|
+
|
|
502
|
+
# LLM gets 4 meta-tools: list_tools, search_tools, get_tool_schema, call_tool
|
|
503
|
+
tools_for_llm = provider.get_dynamic_tools()
|
|
504
|
+
|
|
505
|
+
# LLM workflow: search → get schema → call
|
|
506
|
+
results = await provider.search_tools("calculate average")
|
|
507
|
+
schema = await provider.get_tool_schema("calculate_mean")
|
|
508
|
+
result = await provider.call_tool("calculate_mean", {"values": [1, 2, 3]})
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
See [DISCOVERY.md](docs/DISCOVERY.md) for complete documentation.
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
336
515
|
## Observability
|
|
337
516
|
|
|
338
517
|
One-line setup for production monitoring:
|
|
@@ -359,6 +538,30 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
|
|
|
359
538
|
|
|
360
539
|
---
|
|
361
540
|
|
|
541
|
+
## Structured Error Handling
|
|
542
|
+
|
|
543
|
+
Errors include machine-readable categories and retry hints for planner decision-making:
|
|
544
|
+
|
|
545
|
+
```python
|
|
546
|
+
from chuk_tool_processor.core.exceptions import ErrorCategory
|
|
547
|
+
|
|
548
|
+
results = await processor.process(llm_output)
|
|
549
|
+
for result in results:
|
|
550
|
+
if result.error_info:
|
|
551
|
+
match result.error_info.category:
|
|
552
|
+
case ErrorCategory.RATE_LIMIT:
|
|
553
|
+
await asyncio.sleep(result.retry_after_ms / 1000)
|
|
554
|
+
return await retry()
|
|
555
|
+
case ErrorCategory.CIRCUIT_OPEN:
|
|
556
|
+
return await use_fallback_tool()
|
|
557
|
+
case _ if not result.retryable:
|
|
558
|
+
return await report_permanent_failure()
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
See [ERRORS.md](docs/ERRORS.md) for complete error taxonomy.
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
362
565
|
## Documentation
|
|
363
566
|
|
|
364
567
|
| Document | Description |
|
|
@@ -366,6 +569,8 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
|
|
|
366
569
|
| [**GETTING_STARTED.md**](docs/GETTING_STARTED.md) | Creating tools, using the processor, ValidatedTool, StreamingTool |
|
|
367
570
|
| [**CORE_CONCEPTS.md**](docs/CORE_CONCEPTS.md) | Registry, strategies, wrappers, parsers, MCP overview |
|
|
368
571
|
| [**PRODUCTION_PATTERNS.md**](docs/PRODUCTION_PATTERNS.md) | Bulkheads, scoped registries, ExecutionContext, parallel execution |
|
|
572
|
+
| [**DISCOVERY.md**](docs/DISCOVERY.md) | Dynamic tool discovery, intelligent search, synonym expansion |
|
|
573
|
+
| [**GUARDS.md**](docs/GUARDS.md) | Runtime guards for safety, validation, and resource management |
|
|
369
574
|
| [**MCP_INTEGRATION.md**](docs/MCP_INTEGRATION.md) | HTTP Streamable, STDIO, SSE, OAuth, Middleware Stack |
|
|
370
575
|
| [**ADVANCED_TOPICS.md**](docs/ADVANCED_TOPICS.md) | Deferred loading, code sandbox, isolated strategy, testing |
|
|
371
576
|
| [**CONFIGURATION.md**](docs/CONFIGURATION.md) | All config options and environment variables |
|
|
@@ -380,6 +585,9 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
|
|
|
380
585
|
# Getting started
|
|
381
586
|
python examples/01_getting_started/hello_tool.py
|
|
382
587
|
|
|
588
|
+
# Dynamic tool discovery (search, synonyms, fuzzy matching)
|
|
589
|
+
python examples/07_discovery/dynamic_tools_demo.py
|
|
590
|
+
|
|
383
591
|
# Hero demo: 8 tools, 5-second deadline, 3 pools (DAG + bulkheads + context)
|
|
384
592
|
python examples/02_production_features/hero_runtime_demo.py
|
|
385
593
|
|
|
@@ -389,6 +597,18 @@ python examples/02_production_features/production_patterns_demo.py
|
|
|
389
597
|
# Runtime features (return order, pattern bulkheads, scheduling)
|
|
390
598
|
python examples/02_production_features/runtime_features_demo.py
|
|
391
599
|
|
|
600
|
+
# Structured error handling for planners
|
|
601
|
+
python examples/02_production_features/structured_errors_demo.py
|
|
602
|
+
|
|
603
|
+
# Runtime guards (validation, security, resource limits)
|
|
604
|
+
python examples/guards_demo.py
|
|
605
|
+
|
|
606
|
+
# Redis registry for distributed deployments
|
|
607
|
+
python examples/02_production_features/redis_registry_demo.py
|
|
608
|
+
|
|
609
|
+
# Distributed configuration (Redis registry + resilience)
|
|
610
|
+
python examples/02_production_features/distributed_config_demo.py
|
|
611
|
+
|
|
392
612
|
# Observability demo
|
|
393
613
|
python examples/02_production_features/observability_demo.py
|
|
394
614
|
|
|
@@ -426,6 +646,9 @@ pip install chuk-tool-processor[observability]
|
|
|
426
646
|
# With MCP support
|
|
427
647
|
pip install chuk-tool-processor[mcp]
|
|
428
648
|
|
|
649
|
+
# With Redis registry (distributed deployments)
|
|
650
|
+
pip install chuk-tool-processor[redis]
|
|
651
|
+
|
|
429
652
|
# With fast JSON (2-3x faster with orjson)
|
|
430
653
|
pip install chuk-tool-processor[fast-json]
|
|
431
654
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "chuk-tool-processor"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.18"
|
|
8
8
|
description = "Async-native framework for registering, discovering, and executing tools referenced in LLM responses"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -55,11 +55,22 @@ fast-json = [
|
|
|
55
55
|
"orjson>=3.10.0,<4",
|
|
56
56
|
]
|
|
57
57
|
|
|
58
|
+
# Redis registry for distributed deployments
|
|
59
|
+
redis = [
|
|
60
|
+
"redis[hiredis]>=5.0.0,<6",
|
|
61
|
+
]
|
|
62
|
+
|
|
58
63
|
# Full feature set with performance optimizations
|
|
59
64
|
full = [
|
|
60
65
|
"orjson>=3.10.0,<4",
|
|
61
66
|
]
|
|
62
67
|
|
|
68
|
+
# All extras combined
|
|
69
|
+
all = [
|
|
70
|
+
"orjson>=3.10.0,<4",
|
|
71
|
+
"redis[hiredis]>=5.0.0,<6",
|
|
72
|
+
]
|
|
73
|
+
|
|
63
74
|
# Tell setuptools to look in src/ for your a2a package
|
|
64
75
|
[tool.setuptools.packages.find]
|
|
65
76
|
where = ["src"]
|
|
@@ -93,6 +104,8 @@ dev = [
|
|
|
93
104
|
"pre-commit>=3.8.0",
|
|
94
105
|
"coverage[toml]>=7.6.0",
|
|
95
106
|
"orjson>=3.10.0",
|
|
107
|
+
"redis[hiredis]>=5.0.0,<6",
|
|
108
|
+
"fakeredis[lua]>=2.32.1",
|
|
96
109
|
]
|
|
97
110
|
|
|
98
111
|
observability = [
|