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.
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/PKG-INFO +249 -13
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/README.md +243 -12
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/pyproject.toml +14 -1
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/__init__.py +33 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/config.py +576 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/core/__init__.py +15 -1
- chuk_tool_processor-0.17/src/chuk_tool_processor/core/exceptions.py +691 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/core/processor.py +12 -1
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/bulkhead.py +70 -3
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/strategies/inprocess_strategy.py +51 -15
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/strategies/subprocess_strategy.py +50 -5
- chuk_tool_processor-0.17/src/chuk_tool_processor/execution/wrappers/__init__.py +107 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/wrappers/circuit_breaker.py +3 -3
- chuk_tool_processor-0.17/src/chuk_tool_processor/execution/wrappers/factory.py +503 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/execution/wrappers/redis_circuit_breaker.py +634 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/execution/wrappers/redis_rate_limiting.py +328 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/wrappers/retry.py +10 -3
- chuk_tool_processor-0.17/src/chuk_tool_processor/mcp/__init__.py +93 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/mcp/middleware.py +490 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/stream_manager.py +104 -3
- chuk_tool_processor-0.17/src/chuk_tool_processor/models/return_order.py +19 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/models/tool_result.py +300 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/decorators.py +42 -15
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/providers/__init__.py +24 -3
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/providers/memory.py +21 -3
- chuk_tool_processor-0.17/src/chuk_tool_processor/registry/providers/redis.py +555 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/scheduling/__init__.py +48 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/scheduling/greedy_dag.py +473 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/scheduling/policy.py +66 -0
- chuk_tool_processor-0.17/src/chuk_tool_processor/scheduling/types.py +220 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/PKG-INFO +249 -13
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/SOURCES.txt +11 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/requires.txt +7 -0
- chuk_tool_processor-0.14/src/chuk_tool_processor/core/exceptions.py +0 -308
- chuk_tool_processor-0.14/src/chuk_tool_processor/execution/wrappers/__init__.py +0 -42
- chuk_tool_processor-0.14/src/chuk_tool_processor/mcp/__init__.py +0 -34
- chuk_tool_processor-0.14/src/chuk_tool_processor/models/tool_result.py +0 -118
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/setup.cfg +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/core/context.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/code_sandbox.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/strategies/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/tool_executor.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/wrappers/caching.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/execution/wrappers/rate_limiting.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/context.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/formatter.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/helpers.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/logging/metrics.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/mcp_tool.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/models.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/register_mcp_tools.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/setup_mcp_http_streamable.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/setup_mcp_sse.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/setup_mcp_stdio.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/base_transport.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/http_streamable_transport.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/models.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/sse_transport.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/mcp/transport/stdio_transport.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/execution_strategy.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/streaming_tool.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/tool_call.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/tool_export_mixin.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/tool_spec.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/models/validated_tool.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/observability/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/observability/metrics.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/observability/setup.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/observability/tracing.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/discovery.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/base.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/function_call_tool.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/json_tool.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/openai_tool.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/plugins/parsers/xml_tool.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/py.typed +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/auto_register.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/interface.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/metadata.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/provider.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/registry/tool_export.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/utils/__init__.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/utils/fast_json.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor/utils/validation.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/dependency_links.txt +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/src/chuk_tool_processor.egg-info/top_level.txt +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/tests/test_bulkhead.py +0 -0
- {chuk_tool_processor-0.14 → chuk_tool_processor-0.17}/tests/test_execution_context.py +0 -0
- {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.
|
|
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 —
|
|
38
|
+
# CHUK Tool Processor — A Tool Execution Runtime for AI Systems
|
|
34
39
|
|
|
35
40
|
[](https://pypi.org/project/chuk-tool-processor/)
|
|
36
41
|
[](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
|
|
51
|
+
## The Missing Runtime Layer
|
|
47
52
|
|
|
48
|
-
LLMs are good at *
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
609
|
+
This separation means you can swap planners without changing execution infrastructure, and vice versa.
|
|
374
610
|
|
|
375
611
|
---
|
|
376
612
|
|