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.
Files changed (121) hide show
  1. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/PKG-INFO +229 -1
  2. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/README.md +223 -0
  3. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/pyproject.toml +14 -1
  4. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/__init__.py +186 -0
  5. chuk_tool_processor-0.18/src/chuk_tool_processor/config.py +576 -0
  6. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/core/__init__.py +15 -1
  7. chuk_tool_processor-0.18/src/chuk_tool_processor/core/exceptions.py +691 -0
  8. chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/__init__.py +77 -0
  9. chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/dynamic_provider.py +629 -0
  10. chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/search.py +883 -0
  11. chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/searchable.py +97 -0
  12. chuk_tool_processor-0.18/src/chuk_tool_processor/discovery/synonyms.py +752 -0
  13. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/strategies/subprocess_strategy.py +50 -5
  14. chuk_tool_processor-0.18/src/chuk_tool_processor/execution/wrappers/__init__.py +107 -0
  15. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/wrappers/circuit_breaker.py +3 -3
  16. chuk_tool_processor-0.18/src/chuk_tool_processor/execution/wrappers/factory.py +503 -0
  17. chuk_tool_processor-0.18/src/chuk_tool_processor/execution/wrappers/redis_circuit_breaker.py +634 -0
  18. chuk_tool_processor-0.18/src/chuk_tool_processor/execution/wrappers/redis_rate_limiting.py +328 -0
  19. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/wrappers/retry.py +10 -3
  20. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/__init__.py +239 -0
  21. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/assumption_trace.py +445 -0
  22. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/base.py +140 -0
  23. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/budget.py +201 -0
  24. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/chain.py +275 -0
  25. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/concurrency.py +208 -0
  26. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/models.py +202 -0
  27. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/network_policy.py +306 -0
  28. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/output_size.py +290 -0
  29. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/per_tool.py +121 -0
  30. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/plan_shape.py +349 -0
  31. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/precondition.py +177 -0
  32. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/provenance.py +276 -0
  33. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/retry_safety.py +335 -0
  34. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/runaway.py +129 -0
  35. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/saturation.py +215 -0
  36. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/schema_strictness.py +350 -0
  37. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/sensitive_data.py +283 -0
  38. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/side_effect.py +273 -0
  39. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/timeout_budget.py +261 -0
  40. chuk_tool_processor-0.18/src/chuk_tool_processor/guards/unresolved.py +139 -0
  41. chuk_tool_processor-0.18/src/chuk_tool_processor/models/tool_result.py +300 -0
  42. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/decorators.py +3 -3
  43. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/providers/__init__.py +24 -3
  44. chuk_tool_processor-0.18/src/chuk_tool_processor/registry/providers/redis.py +555 -0
  45. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/PKG-INFO +229 -1
  46. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/SOURCES.txt +31 -0
  47. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/requires.txt +7 -0
  48. chuk_tool_processor-0.15/src/chuk_tool_processor/core/exceptions.py +0 -308
  49. chuk_tool_processor-0.15/src/chuk_tool_processor/execution/wrappers/__init__.py +0 -42
  50. chuk_tool_processor-0.15/src/chuk_tool_processor/models/tool_result.py +0 -120
  51. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/setup.cfg +0 -0
  52. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/core/context.py +0 -0
  53. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/core/processor.py +0 -0
  54. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/__init__.py +0 -0
  55. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/bulkhead.py +0 -0
  56. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/code_sandbox.py +0 -0
  57. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/strategies/__init__.py +0 -0
  58. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/strategies/inprocess_strategy.py +0 -0
  59. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/tool_executor.py +0 -0
  60. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/wrappers/caching.py +0 -0
  61. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/execution/wrappers/rate_limiting.py +0 -0
  62. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/__init__.py +0 -0
  63. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/context.py +0 -0
  64. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/formatter.py +0 -0
  65. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/helpers.py +0 -0
  66. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/logging/metrics.py +0 -0
  67. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/__init__.py +0 -0
  68. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/mcp_tool.py +0 -0
  69. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/middleware.py +0 -0
  70. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/models.py +0 -0
  71. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/register_mcp_tools.py +0 -0
  72. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/setup_mcp_http_streamable.py +0 -0
  73. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/setup_mcp_sse.py +0 -0
  74. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/setup_mcp_stdio.py +0 -0
  75. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/stream_manager.py +0 -0
  76. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/__init__.py +0 -0
  77. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/base_transport.py +0 -0
  78. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/http_streamable_transport.py +0 -0
  79. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/models.py +0 -0
  80. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/sse_transport.py +0 -0
  81. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/mcp/transport/stdio_transport.py +0 -0
  82. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/__init__.py +0 -0
  83. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/execution_strategy.py +0 -0
  84. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/return_order.py +0 -0
  85. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/streaming_tool.py +0 -0
  86. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/tool_call.py +0 -0
  87. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/tool_export_mixin.py +0 -0
  88. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/tool_spec.py +0 -0
  89. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/models/validated_tool.py +0 -0
  90. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/observability/__init__.py +0 -0
  91. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/observability/metrics.py +0 -0
  92. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/observability/setup.py +0 -0
  93. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/observability/tracing.py +0 -0
  94. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/__init__.py +0 -0
  95. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/discovery.py +0 -0
  96. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/__init__.py +0 -0
  97. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/base.py +0 -0
  98. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/function_call_tool.py +0 -0
  99. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/json_tool.py +0 -0
  100. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/openai_tool.py +0 -0
  101. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/plugins/parsers/xml_tool.py +0 -0
  102. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/py.typed +0 -0
  103. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/__init__.py +0 -0
  104. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/auto_register.py +0 -0
  105. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/interface.py +0 -0
  106. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/metadata.py +0 -0
  107. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/provider.py +0 -0
  108. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/providers/memory.py +0 -0
  109. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/registry/tool_export.py +0 -0
  110. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/scheduling/__init__.py +0 -0
  111. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/scheduling/greedy_dag.py +0 -0
  112. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/scheduling/policy.py +0 -0
  113. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/scheduling/types.py +0 -0
  114. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/utils/__init__.py +0 -0
  115. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/utils/fast_json.py +0 -0
  116. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor/utils/validation.py +0 -0
  117. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/dependency_links.txt +0 -0
  118. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/src/chuk_tool_processor.egg-info/top_level.txt +0 -0
  119. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/tests/test_bulkhead.py +0 -0
  120. {chuk_tool_processor-0.15 → chuk_tool_processor-0.18}/tests/test_execution_context.py +0 -0
  121. {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.15
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.15"
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 = [