chuk-tool-processor 0.13__tar.gz → 0.15__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chuk_tool_processor-0.15/PKG-INFO +533 -0
- chuk_tool_processor-0.15/README.md +501 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/pyproject.toml +1 -1
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/__init__.py +51 -2
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/core/__init__.py +16 -0
- chuk_tool_processor-0.15/src/chuk_tool_processor/core/context.py +439 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/core/processor.py +94 -12
- chuk_tool_processor-0.15/src/chuk_tool_processor/execution/__init__.py +23 -0
- chuk_tool_processor-0.15/src/chuk_tool_processor/execution/bulkhead.py +520 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/strategies/inprocess_strategy.py +51 -15
- chuk_tool_processor-0.15/src/chuk_tool_processor/mcp/__init__.py +93 -0
- chuk_tool_processor-0.15/src/chuk_tool_processor/mcp/middleware.py +490 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/stream_manager.py +104 -3
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/transport/http_streamable_transport.py +0 -1
- chuk_tool_processor-0.15/src/chuk_tool_processor/models/return_order.py +19 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/models/tool_result.py +2 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/__init__.py +2 -1
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/decorators.py +39 -12
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/provider.py +68 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/providers/memory.py +21 -3
- chuk_tool_processor-0.15/src/chuk_tool_processor/scheduling/__init__.py +48 -0
- chuk_tool_processor-0.15/src/chuk_tool_processor/scheduling/greedy_dag.py +473 -0
- chuk_tool_processor-0.15/src/chuk_tool_processor/scheduling/policy.py +66 -0
- chuk_tool_processor-0.15/src/chuk_tool_processor/scheduling/types.py +220 -0
- chuk_tool_processor-0.15/src/chuk_tool_processor.egg-info/PKG-INFO +533 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor.egg-info/SOURCES.txt +12 -1
- chuk_tool_processor-0.15/tests/test_bulkhead.py +498 -0
- chuk_tool_processor-0.15/tests/test_execution_context.py +354 -0
- chuk_tool_processor-0.15/tests/test_scoped_registry.py +245 -0
- chuk_tool_processor-0.13/PKG-INFO +0 -2727
- chuk_tool_processor-0.13/README.md +0 -2695
- chuk_tool_processor-0.13/src/chuk_tool_processor/execution/__init__.py +0 -6
- chuk_tool_processor-0.13/src/chuk_tool_processor/mcp/__init__.py +0 -34
- chuk_tool_processor-0.13/src/chuk_tool_processor.egg-info/PKG-INFO +0 -2727
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/setup.cfg +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/core/exceptions.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/code_sandbox.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/strategies/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/strategies/subprocess_strategy.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/tool_executor.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/wrappers/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/wrappers/caching.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/wrappers/circuit_breaker.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/wrappers/rate_limiting.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/execution/wrappers/retry.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/logging/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/logging/context.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/logging/formatter.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/logging/helpers.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/logging/metrics.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/mcp_tool.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/models.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/register_mcp_tools.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/setup_mcp_http_streamable.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/setup_mcp_sse.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/setup_mcp_stdio.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/transport/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/transport/base_transport.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/transport/models.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/transport/sse_transport.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/mcp/transport/stdio_transport.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/models/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/models/execution_strategy.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/models/streaming_tool.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/models/tool_call.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/models/tool_export_mixin.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/models/tool_spec.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/models/validated_tool.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/observability/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/observability/metrics.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/observability/setup.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/observability/tracing.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/plugins/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/plugins/discovery.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/plugins/parsers/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/plugins/parsers/base.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/plugins/parsers/function_call_tool.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/plugins/parsers/json_tool.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/plugins/parsers/openai_tool.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/plugins/parsers/xml_tool.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/py.typed +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/auto_register.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/interface.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/metadata.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/providers/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/registry/tool_export.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/utils/__init__.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/utils/fast_json.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor/utils/validation.py +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor.egg-info/dependency_links.txt +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor.egg-info/requires.txt +0 -0
- {chuk_tool_processor-0.13 → chuk_tool_processor-0.15}/src/chuk_tool_processor.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chuk-tool-processor
|
|
3
|
+
Version: 0.15
|
|
4
|
+
Summary: Async-native framework for registering, discovering, and executing tools referenced in LLM responses
|
|
5
|
+
Author-email: CHUK Team <chrishayuk@somejunkmailbox.com>
|
|
6
|
+
Maintainer-email: CHUK Team <chrishayuk@somejunkmailbox.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: llm,tools,async,ai,openai,mcp,model-context-protocol,tool-calling,function-calling
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Framework :: AsyncIO
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: chuk-mcp>=0.9
|
|
24
|
+
Requires-Dist: dotenv>=0.9.9
|
|
25
|
+
Requires-Dist: psutil>=7.0.0
|
|
26
|
+
Requires-Dist: pydantic>=2.11.3
|
|
27
|
+
Requires-Dist: uuid>=1.30
|
|
28
|
+
Provides-Extra: fast-json
|
|
29
|
+
Requires-Dist: orjson<4,>=3.10.0; extra == "fast-json"
|
|
30
|
+
Provides-Extra: full
|
|
31
|
+
Requires-Dist: orjson<4,>=3.10.0; extra == "full"
|
|
32
|
+
|
|
33
|
+
# CHUK Tool Processor — A Tool Execution Runtime for AI Systems
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/chuk-tool-processor/)
|
|
36
|
+
[](https://pypi.org/project/chuk-tool-processor/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://www.python.org/dev/peps/pep-0561/)
|
|
39
|
+
[](https://pypi.org/project/chuk-tool-processor/)
|
|
40
|
+
[](docs/OBSERVABILITY.md)
|
|
41
|
+
|
|
42
|
+
**Reliable tool execution for LLMs — timeouts, retries, caching, rate limits, circuit breakers, and MCP integration — in one composable layer.**
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## The Missing Runtime Layer
|
|
47
|
+
|
|
48
|
+
LLMs are good at *deciding which tools to call*. The hard part is **executing** those tools reliably.
|
|
49
|
+
|
|
50
|
+
**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).
|
|
51
|
+
|
|
52
|
+
**What it does:**
|
|
53
|
+
- Parses tool calls from any model (Anthropic XML, OpenAI `tool_calls`, JSON)
|
|
54
|
+
- Executes them with **timeouts, retries, caching, rate limits, circuit breaker, observability**
|
|
55
|
+
- Runs tools locally, in **isolated subprocesses**, or **remote via MCP**
|
|
56
|
+
|
|
57
|
+
Works with OpenAI, Anthropic, local models (Ollama/MLX/vLLM), and any framework.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Architecture
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
LLM Output
|
|
65
|
+
↓
|
|
66
|
+
CHUK Tool Processor
|
|
67
|
+
↓
|
|
68
|
+
┌──────────────┬────────────────────┐
|
|
69
|
+
│ Local Tools │ Remote Tools (MCP) │
|
|
70
|
+
└──────────────┴────────────────────┘
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**How it works internally:**
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
LLM Output
|
|
77
|
+
↓
|
|
78
|
+
Parsers (XML / OpenAI / JSON)
|
|
79
|
+
↓
|
|
80
|
+
┌─────────────────────────────┐
|
|
81
|
+
│ Execution Middleware │
|
|
82
|
+
│ (Applied in this order) │
|
|
83
|
+
│ • Cache │
|
|
84
|
+
│ • Rate Limit │
|
|
85
|
+
│ • Retry (with backoff) │
|
|
86
|
+
│ • Circuit Breaker │
|
|
87
|
+
│ • Bulkhead │
|
|
88
|
+
└─────────────────────────────┘
|
|
89
|
+
↓
|
|
90
|
+
Execution Strategy
|
|
91
|
+
┌──────────────────────┐
|
|
92
|
+
│ • InProcess │ ← Fast, trusted
|
|
93
|
+
│ • Isolated/Subprocess│ ← Safe, untrusted
|
|
94
|
+
│ • Remote via MCP │ ← Distributed
|
|
95
|
+
└──────────────────────┘
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Quick Start
|
|
101
|
+
|
|
102
|
+
### Installation
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install chuk-tool-processor
|
|
106
|
+
|
|
107
|
+
# Or with uv (recommended)
|
|
108
|
+
uv pip install chuk-tool-processor
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 60-Second Example
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import asyncio
|
|
115
|
+
from chuk_tool_processor import ToolProcessor, create_registry
|
|
116
|
+
|
|
117
|
+
class Calculator:
|
|
118
|
+
async def execute(self, operation: str, a: float, b: float) -> dict:
|
|
119
|
+
ops = {"add": a + b, "multiply": a * b, "subtract": a - b}
|
|
120
|
+
return {"result": ops.get(operation, 0)}
|
|
121
|
+
|
|
122
|
+
async def main():
|
|
123
|
+
registry = create_registry()
|
|
124
|
+
await registry.register_tool(Calculator, name="math.calculator") # Dotted name → namespace="math"
|
|
125
|
+
|
|
126
|
+
async with ToolProcessor(registry=registry, enable_caching=True, enable_retries=True) as p:
|
|
127
|
+
# Works with OpenAI, Anthropic, or JSON formats
|
|
128
|
+
result = await p.process('<tool name="math.calculator" args=\'{"operation": "multiply", "a": 15, "b": 23}\'/>')
|
|
129
|
+
print(result[0].result) # {'result': 345}
|
|
130
|
+
|
|
131
|
+
asyncio.run(main())
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**That's it.** You now have production-ready tool execution with timeouts, retries, and caching.
|
|
135
|
+
|
|
136
|
+
### Dotted Names for Namespacing
|
|
137
|
+
|
|
138
|
+
Dotted names are auto-parsed into namespace and tool name:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
# These are equivalent:
|
|
142
|
+
await registry.register_tool(FetchUser, name="web.fetch_user") # Auto-parsed
|
|
143
|
+
await registry.register_tool(FetchUser, name="fetch_user", namespace="web") # Explicit
|
|
144
|
+
|
|
145
|
+
# Call using the full dotted name
|
|
146
|
+
result = await processor.process([{"tool": "web.fetch_user", "arguments": {"user_id": "123"}}])
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Works with Any LLM Format
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# Anthropic XML format
|
|
153
|
+
anthropic_output = '<tool name="search" args=\'{"query": "Python"}\'/>'
|
|
154
|
+
|
|
155
|
+
# OpenAI tool_calls format
|
|
156
|
+
openai_output = {
|
|
157
|
+
"tool_calls": [{
|
|
158
|
+
"type": "function",
|
|
159
|
+
"function": {"name": "search", "arguments": '{"query": "Python"}'}
|
|
160
|
+
}]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# Direct JSON
|
|
164
|
+
json_output = [{"tool": "search", "arguments": {"query": "Python"}}]
|
|
165
|
+
|
|
166
|
+
# All work identically
|
|
167
|
+
results = await processor.process(anthropic_output)
|
|
168
|
+
results = await processor.process(openai_output)
|
|
169
|
+
results = await processor.process(json_output)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Key Features
|
|
175
|
+
|
|
176
|
+
### Production Reliability
|
|
177
|
+
|
|
178
|
+
| Feature | Description |
|
|
179
|
+
|---------|-------------|
|
|
180
|
+
| **Timeouts** | Every tool execution has proper timeout handling |
|
|
181
|
+
| **Retries** | Automatic retry with exponential backoff and jitter |
|
|
182
|
+
| **Rate Limiting** | Global and per-tool rate limits with sliding windows |
|
|
183
|
+
| **Caching** | Result caching with TTL and SHA256-based idempotency keys |
|
|
184
|
+
| **Circuit Breakers** | Prevent cascading failures with automatic recovery |
|
|
185
|
+
|
|
186
|
+
### Multi-Tenant & Isolation
|
|
187
|
+
|
|
188
|
+
| Feature | Description |
|
|
189
|
+
|---------|-------------|
|
|
190
|
+
| **Bulkheads** | Per-tool/namespace concurrency limits to prevent resource starvation |
|
|
191
|
+
| **Pattern Bulkheads** | Glob patterns like `"db.*": 3` for grouped concurrency limits |
|
|
192
|
+
| **Scoped Registries** | Isolated registries for multi-tenant apps and testing |
|
|
193
|
+
| **ExecutionContext** | Request-scoped metadata propagation (user, tenant, tracing, deadlines) |
|
|
194
|
+
| **Isolated Strategy** | Subprocess execution for untrusted code (zero crash blast radius) |
|
|
195
|
+
|
|
196
|
+
### Advanced Scheduling
|
|
197
|
+
|
|
198
|
+
| Feature | Description |
|
|
199
|
+
|---------|-------------|
|
|
200
|
+
| **Return Order** | Choose completion order (fast first) or submission order (deterministic) |
|
|
201
|
+
| **SchedulerPolicy** | DAG-based scheduling with dependencies, deadlines, pool limits |
|
|
202
|
+
| **GreedyDagScheduler** | Built-in scheduler with topological sort and deadline-aware skipping |
|
|
203
|
+
|
|
204
|
+
### Integration & Observability
|
|
205
|
+
|
|
206
|
+
| Feature | Description |
|
|
207
|
+
|---------|-------------|
|
|
208
|
+
| **Multi-Format Parsing** | XML (Anthropic), OpenAI `tool_calls`, JSON — all work automatically |
|
|
209
|
+
| **MCP Integration** | Connect to remote tools via HTTP Streamable, STDIO, SSE |
|
|
210
|
+
| **OpenTelemetry** | Distributed tracing with automatic span creation |
|
|
211
|
+
| **Prometheus** | Metrics for error rates, latency, cache hits, circuit breaker state |
|
|
212
|
+
| **Type Safety** | PEP 561 compliant with full mypy support |
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Production Configuration
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
async with ToolProcessor(
|
|
220
|
+
# Execution settings
|
|
221
|
+
default_timeout=30.0,
|
|
222
|
+
max_concurrency=20,
|
|
223
|
+
|
|
224
|
+
# Reliability features
|
|
225
|
+
enable_caching=True,
|
|
226
|
+
cache_ttl=600,
|
|
227
|
+
enable_rate_limiting=True,
|
|
228
|
+
global_rate_limit=100,
|
|
229
|
+
tool_rate_limits={"expensive_api": (5, 60)}, # 5 req/min
|
|
230
|
+
enable_retries=True,
|
|
231
|
+
max_retries=3,
|
|
232
|
+
enable_circuit_breaker=True,
|
|
233
|
+
circuit_breaker_threshold=5,
|
|
234
|
+
|
|
235
|
+
# Multi-tenant isolation
|
|
236
|
+
enable_bulkhead=True,
|
|
237
|
+
bulkhead_config=BulkheadConfig(
|
|
238
|
+
default_limit=10,
|
|
239
|
+
tool_limits={"slow_api": 2},
|
|
240
|
+
patterns={"db.*": 3, "mcp.notion.*": 2}, # Pattern-based limits
|
|
241
|
+
),
|
|
242
|
+
) as processor:
|
|
243
|
+
# Execute with request context
|
|
244
|
+
ctx = ExecutionContext(
|
|
245
|
+
request_id="req-123",
|
|
246
|
+
user_id="user-456",
|
|
247
|
+
tenant_id="acme-corp",
|
|
248
|
+
)
|
|
249
|
+
results = await processor.process(llm_output, context=ctx)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Return Order & Scheduling
|
|
255
|
+
|
|
256
|
+
Control how results are returned and plan complex execution graphs:
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
from chuk_tool_processor import ToolProcessor, ReturnOrder
|
|
260
|
+
|
|
261
|
+
async with ToolProcessor() as processor:
|
|
262
|
+
# Results return as tools complete (fast tools first) - default
|
|
263
|
+
results = await processor.process(calls, return_order="completion")
|
|
264
|
+
|
|
265
|
+
# Results return in submission order (deterministic)
|
|
266
|
+
results = await processor.process(calls, return_order="submission")
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### DAG Scheduling with Dependencies
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
from chuk_tool_processor import (
|
|
273
|
+
GreedyDagScheduler,
|
|
274
|
+
SchedulingConstraints,
|
|
275
|
+
ToolCallSpec,
|
|
276
|
+
ToolMetadata,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
scheduler = GreedyDagScheduler()
|
|
280
|
+
|
|
281
|
+
# Define calls with dependencies
|
|
282
|
+
calls = [
|
|
283
|
+
ToolCallSpec(call_id="fetch", tool_name="api.fetch",
|
|
284
|
+
metadata=ToolMetadata(pool="web", est_ms=300)),
|
|
285
|
+
ToolCallSpec(call_id="transform", tool_name="compute.transform",
|
|
286
|
+
depends_on=("fetch",)),
|
|
287
|
+
ToolCallSpec(call_id="store", tool_name="db.write",
|
|
288
|
+
depends_on=("transform",)),
|
|
289
|
+
]
|
|
290
|
+
|
|
291
|
+
# Plan execution with constraints
|
|
292
|
+
constraints = SchedulingConstraints(
|
|
293
|
+
deadline_ms=5000,
|
|
294
|
+
pool_limits={"web": 2, "db": 1},
|
|
295
|
+
)
|
|
296
|
+
plan = scheduler.plan(calls, constraints)
|
|
297
|
+
|
|
298
|
+
# plan.stages: (('fetch',), ('transform',), ('store',))
|
|
299
|
+
# plan.skip: () or low-priority calls that would miss deadline
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## MCP Integration
|
|
305
|
+
|
|
306
|
+
Connect to remote tool servers using the [Model Context Protocol](https://modelcontextprotocol.io):
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
from chuk_tool_processor.mcp import setup_mcp_http_streamable
|
|
310
|
+
|
|
311
|
+
# Cloud services (Notion, etc.)
|
|
312
|
+
processor, manager = await setup_mcp_http_streamable(
|
|
313
|
+
servers=[{
|
|
314
|
+
"name": "notion",
|
|
315
|
+
"url": "https://mcp.notion.com/mcp",
|
|
316
|
+
"headers": {"Authorization": f"Bearer {token}"}
|
|
317
|
+
}],
|
|
318
|
+
namespace="notion",
|
|
319
|
+
enable_caching=True,
|
|
320
|
+
enable_retries=True
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Use remote tools
|
|
324
|
+
results = await processor.process(
|
|
325
|
+
'<tool name="notion.search_pages" args=\'{"query": "docs"}\'/>'
|
|
326
|
+
)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Transport Options:**
|
|
330
|
+
|
|
331
|
+
| Transport | Use Case | Example |
|
|
332
|
+
|-----------|----------|---------|
|
|
333
|
+
| **HTTP Streamable** | Cloud SaaS with OAuth | Notion, custom APIs |
|
|
334
|
+
| **STDIO** | Local tools, databases | SQLite, file systems |
|
|
335
|
+
| **SSE** | Legacy MCP servers | Atlassian |
|
|
336
|
+
|
|
337
|
+
See [MCP_INTEGRATION.md](docs/MCP_INTEGRATION.md) for complete examples with OAuth token refresh.
|
|
338
|
+
|
|
339
|
+
### MCP Middleware Stack
|
|
340
|
+
|
|
341
|
+
For production deployments, wrap MCP connections with resilience middleware:
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
from chuk_tool_processor.mcp.middleware import (
|
|
345
|
+
MiddlewareConfig,
|
|
346
|
+
MiddlewareStack,
|
|
347
|
+
RetrySettings,
|
|
348
|
+
CircuitBreakerSettings,
|
|
349
|
+
RateLimitSettings,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# Configure middleware layers
|
|
353
|
+
config = MiddlewareConfig(
|
|
354
|
+
retry=RetrySettings(max_retries=3, base_delay=1.0),
|
|
355
|
+
circuit_breaker=CircuitBreakerSettings(failure_threshold=5),
|
|
356
|
+
rate_limiting=RateLimitSettings(enabled=True, global_limit=100),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
# Wrap StreamManager with middleware
|
|
360
|
+
middleware = MiddlewareStack(stream_manager, config=config)
|
|
361
|
+
|
|
362
|
+
# Execute with automatic retry, circuit breaking, and rate limiting
|
|
363
|
+
result = await middleware.call_tool("notion.search", {"query": "docs"})
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Observability
|
|
369
|
+
|
|
370
|
+
One-line setup for production monitoring:
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
from chuk_tool_processor.observability import setup_observability
|
|
374
|
+
|
|
375
|
+
setup_observability(
|
|
376
|
+
service_name="my-tool-service",
|
|
377
|
+
enable_tracing=True, # → OpenTelemetry traces
|
|
378
|
+
enable_metrics=True, # → Prometheus metrics at :9090/metrics
|
|
379
|
+
metrics_port=9090
|
|
380
|
+
)
|
|
381
|
+
# Every tool execution is now automatically traced and metered
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**What you get:**
|
|
385
|
+
- Distributed traces (Jaeger, Zipkin, any OTLP collector)
|
|
386
|
+
- Prometheus metrics (error rate, latency P50/P95/P99, cache hit rate)
|
|
387
|
+
- Circuit breaker state monitoring
|
|
388
|
+
- Zero code changes to your tools
|
|
389
|
+
|
|
390
|
+
See [OBSERVABILITY.md](docs/OBSERVABILITY.md) for complete setup guide.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## Documentation
|
|
395
|
+
|
|
396
|
+
| Document | Description |
|
|
397
|
+
|----------|-------------|
|
|
398
|
+
| [**GETTING_STARTED.md**](docs/GETTING_STARTED.md) | Creating tools, using the processor, ValidatedTool, StreamingTool |
|
|
399
|
+
| [**CORE_CONCEPTS.md**](docs/CORE_CONCEPTS.md) | Registry, strategies, wrappers, parsers, MCP overview |
|
|
400
|
+
| [**PRODUCTION_PATTERNS.md**](docs/PRODUCTION_PATTERNS.md) | Bulkheads, scoped registries, ExecutionContext, parallel execution |
|
|
401
|
+
| [**MCP_INTEGRATION.md**](docs/MCP_INTEGRATION.md) | HTTP Streamable, STDIO, SSE, OAuth, Middleware Stack |
|
|
402
|
+
| [**ADVANCED_TOPICS.md**](docs/ADVANCED_TOPICS.md) | Deferred loading, code sandbox, isolated strategy, testing |
|
|
403
|
+
| [**CONFIGURATION.md**](docs/CONFIGURATION.md) | All config options and environment variables |
|
|
404
|
+
| [**OBSERVABILITY.md**](docs/OBSERVABILITY.md) | OpenTelemetry, Prometheus, metrics reference |
|
|
405
|
+
| [**ERRORS.md**](docs/ERRORS.md) | Error codes and handling patterns |
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Examples
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
# Getting started
|
|
413
|
+
python examples/01_getting_started/hello_tool.py
|
|
414
|
+
|
|
415
|
+
# Hero demo: 8 tools, 5-second deadline, 3 pools (DAG + bulkheads + context)
|
|
416
|
+
python examples/02_production_features/hero_runtime_demo.py
|
|
417
|
+
|
|
418
|
+
# Production patterns (bulkheads, context, scoped registries)
|
|
419
|
+
python examples/02_production_features/production_patterns_demo.py
|
|
420
|
+
|
|
421
|
+
# Runtime features (return order, pattern bulkheads, scheduling)
|
|
422
|
+
python examples/02_production_features/runtime_features_demo.py
|
|
423
|
+
|
|
424
|
+
# Observability demo
|
|
425
|
+
python examples/02_production_features/observability_demo.py
|
|
426
|
+
|
|
427
|
+
# MCP integration
|
|
428
|
+
python examples/04_mcp_integration/stdio_echo.py
|
|
429
|
+
python examples/04_mcp_integration/notion_oauth.py
|
|
430
|
+
python examples/04_mcp_integration/middleware_demo.py
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
See [examples/](examples/) for 20+ working examples.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Compatibility
|
|
438
|
+
|
|
439
|
+
| Component | Supported |
|
|
440
|
+
|-----------|-----------|
|
|
441
|
+
| **Python** | 3.11, 3.12, 3.13 |
|
|
442
|
+
| **Platforms** | macOS, Linux, Windows |
|
|
443
|
+
| **LLM Providers** | OpenAI, Anthropic, Local models (Ollama, MLX, vLLM) |
|
|
444
|
+
| **MCP Transports** | HTTP Streamable, STDIO, SSE |
|
|
445
|
+
| **MCP Spec** | 2025-11-25, 2025-06-18, 2025-03-26 |
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## Installation Options
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
# Core package
|
|
453
|
+
pip install chuk-tool-processor
|
|
454
|
+
|
|
455
|
+
# With observability (OpenTelemetry + Prometheus)
|
|
456
|
+
pip install chuk-tool-processor[observability]
|
|
457
|
+
|
|
458
|
+
# With MCP support
|
|
459
|
+
pip install chuk-tool-processor[mcp]
|
|
460
|
+
|
|
461
|
+
# With fast JSON (2-3x faster with orjson)
|
|
462
|
+
pip install chuk-tool-processor[fast-json]
|
|
463
|
+
|
|
464
|
+
# All extras
|
|
465
|
+
pip install chuk-tool-processor[all]
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## When to Use This
|
|
471
|
+
|
|
472
|
+
**Use CHUK Tool Processor when:**
|
|
473
|
+
- Your LLM calls tools or APIs
|
|
474
|
+
- You need retries, timeouts, caching, or rate limits
|
|
475
|
+
- You need to run untrusted tools safely
|
|
476
|
+
- Your tools are local or remote (MCP)
|
|
477
|
+
- You need multi-tenant isolation
|
|
478
|
+
- You want production-grade observability
|
|
479
|
+
|
|
480
|
+
**Don't use this if:**
|
|
481
|
+
- You want an agent framework (this is the execution runtime, not the agent)
|
|
482
|
+
- You want conversation flow/memory orchestration
|
|
483
|
+
- You need a planner to decide *which* tools to call
|
|
484
|
+
|
|
485
|
+
### The Seam: Runtime vs Planner
|
|
486
|
+
|
|
487
|
+
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.
|
|
488
|
+
|
|
489
|
+
```
|
|
490
|
+
┌─────────────────────────────────────────────────────┐
|
|
491
|
+
│ Your Agent / LangChain / LlamaIndex / Custom │ ← Decides WHICH tools
|
|
492
|
+
└─────────────────────────────────────────────────────┘
|
|
493
|
+
↓
|
|
494
|
+
┌─────────────────────────────────────────────────────┐
|
|
495
|
+
│ CHUK Tool Processor │ ← Executes tools RELIABLY
|
|
496
|
+
│ (timeouts, retries, caching, rate limits, etc.) │
|
|
497
|
+
└─────────────────────────────────────────────────────┘
|
|
498
|
+
↓
|
|
499
|
+
┌─────────────────────────────────────────────────────┐
|
|
500
|
+
│ Local Tools / MCP Servers │ ← Does the actual work
|
|
501
|
+
└─────────────────────────────────────────────────────┘
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
This separation means you can swap planners without changing execution infrastructure, and vice versa.
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## Contributing
|
|
509
|
+
|
|
510
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
# Development setup
|
|
514
|
+
git clone https://github.com/chrishayuk/chuk-tool-processor.git
|
|
515
|
+
cd chuk-tool-processor
|
|
516
|
+
uv pip install -e ".[dev]"
|
|
517
|
+
|
|
518
|
+
# Run tests
|
|
519
|
+
make check
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## License
|
|
525
|
+
|
|
526
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## Related Projects
|
|
531
|
+
|
|
532
|
+
- [chuk-mcp](https://github.com/chrishayuk/chuk-mcp) - Low-level MCP protocol client
|
|
533
|
+
- [Model Context Protocol](https://modelcontextprotocol.io) - MCP specification
|