chuk-tool-processor 0.14__tar.gz → 0.17__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 (96) hide show
  1. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/PKG-INFO +249 -13
  2. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/README.md +243 -12
  3. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/pyproject.toml +14 -1
  4. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/__init__.py +33 -0
  5. chuk_tool_processor-0.17/src/chuk_tool_processor/config.py +576 -0
  6. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/core/__init__.py +15 -1
  7. chuk_tool_processor-0.17/src/chuk_tool_processor/core/exceptions.py +691 -0
  8. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/core/processor.py +12 -1
  9. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/bulkhead.py +70 -3
  10. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/strategies/inprocess_strategy.py +51 -15
  11. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/strategies/subprocess_strategy.py +50 -5
  12. chuk_tool_processor-0.17/src/chuk_tool_processor/execution/wrappers/__init__.py +107 -0
  13. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/wrappers/circuit_breaker.py +3 -3
  14. chuk_tool_processor-0.17/src/chuk_tool_processor/execution/wrappers/factory.py +503 -0
  15. chuk_tool_processor-0.17/src/chuk_tool_processor/execution/wrappers/redis_circuit_breaker.py +634 -0
  16. chuk_tool_processor-0.17/src/chuk_tool_processor/execution/wrappers/redis_rate_limiting.py +328 -0
  17. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/wrappers/retry.py +10 -3
  18. chuk_tool_processor-0.17/src/chuk_tool_processor/mcp/__init__.py +93 -0
  19. chuk_tool_processor-0.17/src/chuk_tool_processor/mcp/middleware.py +490 -0
  20. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/stream_manager.py +104 -3
  21. chuk_tool_processor-0.17/src/chuk_tool_processor/models/return_order.py +19 -0
  22. chuk_tool_processor-0.17/src/chuk_tool_processor/models/tool_result.py +300 -0
  23. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/decorators.py +42 -15
  24. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/providers/__init__.py +24 -3
  25. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/providers/memory.py +21 -3
  26. chuk_tool_processor-0.17/src/chuk_tool_processor/registry/providers/redis.py +555 -0
  27. chuk_tool_processor-0.17/src/chuk_tool_processor/scheduling/__init__.py +48 -0
  28. chuk_tool_processor-0.17/src/chuk_tool_processor/scheduling/greedy_dag.py +473 -0
  29. chuk_tool_processor-0.17/src/chuk_tool_processor/scheduling/policy.py +66 -0
  30. chuk_tool_processor-0.17/src/chuk_tool_processor/scheduling/types.py +220 -0
  31. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/PKG-INFO +249 -13
  32. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/SOURCES.txt +11 -0
  33. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/requires.txt +7 -0
  34. chuk_tool_processor-0.14/src/chuk_tool_processor/core/exceptions.py +0 -308
  35. chuk_tool_processor-0.14/src/chuk_tool_processor/execution/wrappers/__init__.py +0 -42
  36. chuk_tool_processor-0.14/src/chuk_tool_processor/mcp/__init__.py +0 -34
  37. chuk_tool_processor-0.14/src/chuk_tool_processor/models/tool_result.py +0 -118
  38. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/setup.cfg +0 -0
  39. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/core/context.py +0 -0
  40. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/__init__.py +0 -0
  41. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/code_sandbox.py +0 -0
  42. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/strategies/__init__.py +0 -0
  43. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/tool_executor.py +0 -0
  44. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/wrappers/caching.py +0 -0
  45. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/wrappers/rate_limiting.py +0 -0
  46. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/__init__.py +0 -0
  47. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/context.py +0 -0
  48. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/formatter.py +0 -0
  49. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/helpers.py +0 -0
  50. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/metrics.py +0 -0
  51. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/mcp_tool.py +0 -0
  52. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/models.py +0 -0
  53. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/register_mcp_tools.py +0 -0
  54. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/setup_mcp_http_streamable.py +0 -0
  55. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/setup_mcp_sse.py +0 -0
  56. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/setup_mcp_stdio.py +0 -0
  57. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/__init__.py +0 -0
  58. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/base_transport.py +0 -0
  59. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/http_streamable_transport.py +0 -0
  60. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/models.py +0 -0
  61. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/sse_transport.py +0 -0
  62. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/stdio_transport.py +0 -0
  63. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/__init__.py +0 -0
  64. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/execution_strategy.py +0 -0
  65. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/streaming_tool.py +0 -0
  66. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/tool_call.py +0 -0
  67. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/tool_export_mixin.py +0 -0
  68. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/tool_spec.py +0 -0
  69. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/validated_tool.py +0 -0
  70. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/observability/__init__.py +0 -0
  71. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/observability/metrics.py +0 -0
  72. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/observability/setup.py +0 -0
  73. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/observability/tracing.py +0 -0
  74. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/__init__.py +0 -0
  75. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/discovery.py +0 -0
  76. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/__init__.py +0 -0
  77. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/base.py +0 -0
  78. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/function_call_tool.py +0 -0
  79. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/json_tool.py +0 -0
  80. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/openai_tool.py +0 -0
  81. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/xml_tool.py +0 -0
  82. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/py.typed +0 -0
  83. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/__init__.py +0 -0
  84. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/auto_register.py +0 -0
  85. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/interface.py +0 -0
  86. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/metadata.py +0 -0
  87. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/provider.py +0 -0
  88. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/tool_export.py +0 -0
  89. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/utils/__init__.py +0 -0
  90. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/utils/fast_json.py +0 -0
  91. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/utils/validation.py +0 -0
  92. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/dependency_links.txt +0 -0
  93. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/top_level.txt +0 -0
  94. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/tests/test_bulkhead.py +0 -0
  95. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/tests/test_execution_context.py +0 -0
  96. {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/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.14
3
+ Version: 0.17
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,10 +27,15 @@ 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
- # CHUK Tool Processor — Production-grade execution for LLM tool calls
38
+ # CHUK Tool Processor — A Tool Execution Runtime for AI Systems
34
39
 
35
40
  [![PyPI](https://img.shields.io/pypi/v/chuk-tool-processor.svg)](https://pypi.org/project/chuk-tool-processor/)
36
41
  [![Python](https://img.shields.io/pypi/pyversions/chuk-tool-processor.svg)](https://pypi.org/project/chuk-tool-processor/)
@@ -43,16 +48,18 @@ Requires-Dist: orjson<4,>=3.10.0; extra == "full"
43
48
 
44
49
  ---
45
50
 
46
- ## The Missing Layer for Reliable Tool Execution
51
+ ## The Missing Runtime Layer
47
52
 
48
- LLMs are good at *calling* tools. The hard part is **executing** those tools reliably.
53
+ LLMs are good at *deciding which tools to call*. The hard part is **executing** those tools reliably.
49
54
 
50
- **CHUK Tool Processor:**
55
+ **CHUK Tool Processor** is a **tool execution runtime** — it doesn't plan workflows or decide which tools to call. It executes tool calls reliably, under constraints, as directed by higher-level planners (your agent, LangChain, LlamaIndex, or a custom orchestrator).
56
+
57
+ **What it does:**
51
58
  - Parses tool calls from any model (Anthropic XML, OpenAI `tool_calls`, JSON)
52
59
  - Executes them with **timeouts, retries, caching, rate limits, circuit breaker, observability**
53
60
  - Runs tools locally, in **isolated subprocesses**, or **remote via MCP**
54
61
 
55
- Works with OpenAI, Anthropic, local models (Ollama/MLX/vLLM), and any framework (LangChain, LlamaIndex, custom).
62
+ Works with OpenAI, Anthropic, local models (Ollama/MLX/vLLM), and any framework.
56
63
 
57
64
  ---
58
65
 
@@ -110,18 +117,20 @@ uv pip install chuk-tool-processor
110
117
 
111
118
  ```python
112
119
  import asyncio
113
- from chuk_tool_processor import ToolProcessor, tool
120
+ from chuk_tool_processor import ToolProcessor, create_registry
114
121
 
115
- @tool(name="calculator")
116
122
  class Calculator:
117
123
  async def execute(self, operation: str, a: float, b: float) -> dict:
118
124
  ops = {"add": a + b, "multiply": a * b, "subtract": a - b}
119
125
  return {"result": ops.get(operation, 0)}
120
126
 
121
127
  async def main():
122
- async with ToolProcessor(enable_caching=True, enable_retries=True) as p:
128
+ registry = create_registry()
129
+ await registry.register_tool(Calculator, name="math.calculator") # Dotted name → namespace="math"
130
+
131
+ async with ToolProcessor(registry=registry, enable_caching=True, enable_retries=True) as p:
123
132
  # Works with OpenAI, Anthropic, or JSON formats
124
- result = await p.process('<tool name="calculator" args=\'{"operation": "multiply", "a": 15, "b": 23}\'/>')
133
+ result = await p.process('<tool name="math.calculator" args=\'{"operation": "multiply", "a": 15, "b": 23}\'/>')
125
134
  print(result[0].result) # {'result': 345}
126
135
 
127
136
  asyncio.run(main())
@@ -129,6 +138,19 @@ asyncio.run(main())
129
138
 
130
139
  **That's it.** You now have production-ready tool execution with timeouts, retries, and caching.
131
140
 
141
+ ### Dotted Names for Namespacing
142
+
143
+ Dotted names are auto-parsed into namespace and tool name:
144
+
145
+ ```python
146
+ # These are equivalent:
147
+ await registry.register_tool(FetchUser, name="web.fetch_user") # Auto-parsed
148
+ await registry.register_tool(FetchUser, name="fetch_user", namespace="web") # Explicit
149
+
150
+ # Call using the full dotted name
151
+ result = await processor.process([{"tool": "web.fetch_user", "arguments": {"user_id": "123"}}])
152
+ ```
153
+
132
154
  ### Works with Any LLM Format
133
155
 
134
156
  ```python
@@ -165,15 +187,26 @@ results = await processor.process(json_output)
165
187
  | **Rate Limiting** | Global and per-tool rate limits with sliding windows |
166
188
  | **Caching** | Result caching with TTL and SHA256-based idempotency keys |
167
189
  | **Circuit Breakers** | Prevent cascading failures with automatic recovery |
190
+ | **Structured Errors** | Machine-readable error categories with retry hints for planners |
168
191
 
169
192
  ### Multi-Tenant & Isolation
170
193
 
171
194
  | Feature | Description |
172
195
  |---------|-------------|
173
196
  | **Bulkheads** | Per-tool/namespace concurrency limits to prevent resource starvation |
197
+ | **Pattern Bulkheads** | Glob patterns like `"db.*": 3` for grouped concurrency limits |
174
198
  | **Scoped Registries** | Isolated registries for multi-tenant apps and testing |
175
199
  | **ExecutionContext** | Request-scoped metadata propagation (user, tenant, tracing, deadlines) |
176
200
  | **Isolated Strategy** | Subprocess execution for untrusted code (zero crash blast radius) |
201
+ | **Redis Registry** | Distributed tool registry for multi-process/multi-machine deployments |
202
+
203
+ ### Advanced Scheduling
204
+
205
+ | Feature | Description |
206
+ |---------|-------------|
207
+ | **Return Order** | Choose completion order (fast first) or submission order (deterministic) |
208
+ | **SchedulerPolicy** | DAG-based scheduling with dependencies, deadlines, pool limits |
209
+ | **GreedyDagScheduler** | Built-in scheduler with topological sort and deadline-aware skipping |
177
210
 
178
211
  ### Integration & Observability
179
212
 
@@ -211,6 +244,7 @@ async with ToolProcessor(
211
244
  bulkhead_config=BulkheadConfig(
212
245
  default_limit=10,
213
246
  tool_limits={"slow_api": 2},
247
+ patterns={"db.*": 3, "mcp.notion.*": 2}, # Pattern-based limits
214
248
  ),
215
249
  ) as processor:
216
250
  # Execute with request context
@@ -224,6 +258,56 @@ async with ToolProcessor(
224
258
 
225
259
  ---
226
260
 
261
+ ## Return Order & Scheduling
262
+
263
+ Control how results are returned and plan complex execution graphs:
264
+
265
+ ```python
266
+ from chuk_tool_processor import ToolProcessor, ReturnOrder
267
+
268
+ async with ToolProcessor() as processor:
269
+ # Results return as tools complete (fast tools first) - default
270
+ results = await processor.process(calls, return_order="completion")
271
+
272
+ # Results return in submission order (deterministic)
273
+ results = await processor.process(calls, return_order="submission")
274
+ ```
275
+
276
+ ### DAG Scheduling with Dependencies
277
+
278
+ ```python
279
+ from chuk_tool_processor import (
280
+ GreedyDagScheduler,
281
+ SchedulingConstraints,
282
+ ToolCallSpec,
283
+ ToolMetadata,
284
+ )
285
+
286
+ scheduler = GreedyDagScheduler()
287
+
288
+ # Define calls with dependencies
289
+ calls = [
290
+ ToolCallSpec(call_id="fetch", tool_name="api.fetch",
291
+ metadata=ToolMetadata(pool="web", est_ms=300)),
292
+ ToolCallSpec(call_id="transform", tool_name="compute.transform",
293
+ depends_on=("fetch",)),
294
+ ToolCallSpec(call_id="store", tool_name="db.write",
295
+ depends_on=("transform",)),
296
+ ]
297
+
298
+ # Plan execution with constraints
299
+ constraints = SchedulingConstraints(
300
+ deadline_ms=5000,
301
+ pool_limits={"web": 2, "db": 1},
302
+ )
303
+ plan = scheduler.plan(calls, constraints)
304
+
305
+ # plan.stages: (('fetch',), ('transform',), ('store',))
306
+ # plan.skip: () or low-priority calls that would miss deadline
307
+ ```
308
+
309
+ ---
310
+
227
311
  ## MCP Integration
228
312
 
229
313
  Connect to remote tool servers using the [Model Context Protocol](https://modelcontextprotocol.io):
@@ -259,6 +343,95 @@ results = await processor.process(
259
343
 
260
344
  See [MCP_INTEGRATION.md](docs/MCP_INTEGRATION.md) for complete examples with OAuth token refresh.
261
345
 
346
+ ### MCP Middleware Stack
347
+
348
+ For production deployments, wrap MCP connections with resilience middleware:
349
+
350
+ ```python
351
+ from chuk_tool_processor.mcp.middleware import (
352
+ MiddlewareConfig,
353
+ MiddlewareStack,
354
+ RetrySettings,
355
+ CircuitBreakerSettings,
356
+ RateLimitSettings,
357
+ )
358
+
359
+ # Configure middleware layers
360
+ config = MiddlewareConfig(
361
+ retry=RetrySettings(max_retries=3, base_delay=1.0),
362
+ circuit_breaker=CircuitBreakerSettings(failure_threshold=5),
363
+ rate_limiting=RateLimitSettings(enabled=True, global_limit=100),
364
+ )
365
+
366
+ # Wrap StreamManager with middleware
367
+ middleware = MiddlewareStack(stream_manager, config=config)
368
+
369
+ # Execute with automatic retry, circuit breaking, and rate limiting
370
+ result = await middleware.call_tool("notion.search", {"query": "docs"})
371
+ ```
372
+
373
+ ---
374
+
375
+ ## Distributed Deployments (Redis)
376
+
377
+ For multi-process or multi-machine deployments, configure Redis backends via environment variables:
378
+
379
+ ```bash
380
+ # Enable Redis for everything
381
+ export CHUK_REGISTRY_BACKEND=redis
382
+ export CHUK_RESILIENCE_BACKEND=redis
383
+ export CHUK_REDIS_URL=redis://localhost:6379/0
384
+
385
+ # Enable resilience features
386
+ export CHUK_CIRCUIT_BREAKER_ENABLED=true
387
+ export CHUK_RATE_LIMIT_ENABLED=true
388
+ export CHUK_RATE_LIMIT_GLOBAL=100
389
+ ```
390
+
391
+ ```python
392
+ from chuk_tool_processor import ProcessorConfig
393
+
394
+ # Load from environment and create fully-configured processor
395
+ config = ProcessorConfig.from_env()
396
+ processor = await config.create_processor()
397
+
398
+ async with processor:
399
+ results = await processor.process(llm_output)
400
+ ```
401
+
402
+ Or configure programmatically:
403
+
404
+ ```python
405
+ from chuk_tool_processor import ProcessorConfig, RegistryConfig, BackendType
406
+ from chuk_tool_processor.config import CircuitBreakerConfig, RateLimitConfig
407
+
408
+ config = ProcessorConfig(
409
+ # Registry and resilience use Redis
410
+ registry=RegistryConfig(backend=BackendType.REDIS),
411
+ resilience_backend=BackendType.REDIS,
412
+ redis_url="redis://localhost:6379/0",
413
+
414
+ # Enable features
415
+ circuit_breaker=CircuitBreakerConfig(enabled=True, failure_threshold=5),
416
+ rate_limit=RateLimitConfig(enabled=True, global_limit=100),
417
+ )
418
+
419
+ processor = await config.create_processor()
420
+ ```
421
+
422
+ **Key features:**
423
+ - **Distributed registry**: Tool metadata shared across processes
424
+ - **Distributed circuit breaker**: Failure counts shared (prevents cascading failures across instances)
425
+ - **Distributed rate limiting**: Global limits enforced across all instances
426
+ - **Multi-tenant isolation**: Key prefixes isolate data per tenant
427
+
428
+ **Installation:**
429
+ ```bash
430
+ pip install chuk-tool-processor[redis] # or: uv add chuk-tool-processor[redis]
431
+ ```
432
+
433
+ See [examples/02_production_features/distributed_config_demo.py](examples/02_production_features/distributed_config_demo.py) for a complete example.
434
+
262
435
  ---
263
436
 
264
437
  ## Observability
@@ -287,6 +460,30 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
287
460
 
288
461
  ---
289
462
 
463
+ ## Structured Error Handling
464
+
465
+ Errors include machine-readable categories and retry hints for planner decision-making:
466
+
467
+ ```python
468
+ from chuk_tool_processor.core.exceptions import ErrorCategory
469
+
470
+ results = await processor.process(llm_output)
471
+ for result in results:
472
+ if result.error_info:
473
+ match result.error_info.category:
474
+ case ErrorCategory.RATE_LIMIT:
475
+ await asyncio.sleep(result.retry_after_ms / 1000)
476
+ return await retry()
477
+ case ErrorCategory.CIRCUIT_OPEN:
478
+ return await use_fallback_tool()
479
+ case _ if not result.retryable:
480
+ return await report_permanent_failure()
481
+ ```
482
+
483
+ See [ERRORS.md](docs/ERRORS.md) for complete error taxonomy.
484
+
485
+ ---
486
+
290
487
  ## Documentation
291
488
 
292
489
  | Document | Description |
@@ -294,7 +491,7 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
294
491
  | [**GETTING_STARTED.md**](docs/GETTING_STARTED.md) | Creating tools, using the processor, ValidatedTool, StreamingTool |
295
492
  | [**CORE_CONCEPTS.md**](docs/CORE_CONCEPTS.md) | Registry, strategies, wrappers, parsers, MCP overview |
296
493
  | [**PRODUCTION_PATTERNS.md**](docs/PRODUCTION_PATTERNS.md) | Bulkheads, scoped registries, ExecutionContext, parallel execution |
297
- | [**MCP_INTEGRATION.md**](docs/MCP_INTEGRATION.md) | HTTP Streamable, STDIO, SSE, OAuth token refresh |
494
+ | [**MCP_INTEGRATION.md**](docs/MCP_INTEGRATION.md) | HTTP Streamable, STDIO, SSE, OAuth, Middleware Stack |
298
495
  | [**ADVANCED_TOPICS.md**](docs/ADVANCED_TOPICS.md) | Deferred loading, code sandbox, isolated strategy, testing |
299
496
  | [**CONFIGURATION.md**](docs/CONFIGURATION.md) | All config options and environment variables |
300
497
  | [**OBSERVABILITY.md**](docs/OBSERVABILITY.md) | OpenTelemetry, Prometheus, metrics reference |
@@ -308,15 +505,31 @@ See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
308
505
  # Getting started
309
506
  python examples/01_getting_started/hello_tool.py
310
507
 
508
+ # Hero demo: 8 tools, 5-second deadline, 3 pools (DAG + bulkheads + context)
509
+ python examples/02_production_features/hero_runtime_demo.py
510
+
311
511
  # Production patterns (bulkheads, context, scoped registries)
312
512
  python examples/02_production_features/production_patterns_demo.py
313
513
 
514
+ # Runtime features (return order, pattern bulkheads, scheduling)
515
+ python examples/02_production_features/runtime_features_demo.py
516
+
517
+ # Structured error handling for planners
518
+ python examples/02_production_features/structured_errors_demo.py
519
+
520
+ # Redis registry for distributed deployments
521
+ python examples/02_production_features/redis_registry_demo.py
522
+
523
+ # Distributed configuration (Redis registry + resilience)
524
+ python examples/02_production_features/distributed_config_demo.py
525
+
314
526
  # Observability demo
315
527
  python examples/02_production_features/observability_demo.py
316
528
 
317
529
  # MCP integration
318
530
  python examples/04_mcp_integration/stdio_echo.py
319
531
  python examples/04_mcp_integration/notion_oauth.py
532
+ python examples/04_mcp_integration/middleware_demo.py
320
533
  ```
321
534
 
322
535
  See [examples/](examples/) for 20+ working examples.
@@ -347,6 +560,9 @@ pip install chuk-tool-processor[observability]
347
560
  # With MCP support
348
561
  pip install chuk-tool-processor[mcp]
349
562
 
563
+ # With Redis registry (distributed deployments)
564
+ pip install chuk-tool-processor[redis]
565
+
350
566
  # With fast JSON (2-3x faster with orjson)
351
567
  pip install chuk-tool-processor[fast-json]
352
568
 
@@ -367,10 +583,30 @@ pip install chuk-tool-processor[all]
367
583
  - You want production-grade observability
368
584
 
369
585
  **Don't use this if:**
370
- - You want an agent framework (this is the execution layer, not the agent)
586
+ - You want an agent framework (this is the execution runtime, not the agent)
371
587
  - You want conversation flow/memory orchestration
588
+ - You need a planner to decide *which* tools to call
589
+
590
+ ### The Seam: Runtime vs Planner
591
+
592
+ CHUK Tool Processor deliberately does not plan workflows or decide which tools to call. It executes tool calls reliably, under constraints, as directed by higher-level planners.
593
+
594
+ ```
595
+ ┌─────────────────────────────────────────────────────┐
596
+ │ Your Agent / LangChain / LlamaIndex / Custom │ ← Decides WHICH tools
597
+ └─────────────────────────────────────────────────────┘
598
+
599
+ ┌─────────────────────────────────────────────────────┐
600
+ │ CHUK Tool Processor │ ← Executes tools RELIABLY
601
+ │ (timeouts, retries, caching, rate limits, etc.) │
602
+ └─────────────────────────────────────────────────────┘
603
+
604
+ ┌─────────────────────────────────────────────────────┐
605
+ │ Local Tools / MCP Servers │ ← Does the actual work
606
+ └─────────────────────────────────────────────────────┘
607
+ ```
372
608
 
373
- > **Not a framework.** If LangChain/LlamaIndex help decide *which* tool to call, CHUK Tool Processor makes sure the tool call **actually succeeds**.
609
+ This separation means you can swap planners without changing execution infrastructure, and vice versa.
374
610
 
375
611
  ---
376
612