chuk-tool-processor 0.3__py3-none-any.whl → 0.4__py3-none-any.whl

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.

Potentially problematic release.


This version of chuk-tool-processor might be problematic. Click here for more details.

@@ -3,7 +3,7 @@ chuk_tool_processor/core/__init__.py,sha256=slM7pZna88tyZrF3KtN22ApYyCqGNt5Yscv-
3
3
  chuk_tool_processor/core/exceptions.py,sha256=h4zL1jpCY1Ud1wT8xDeMxZ8GR8ttmkObcv36peUHJEA,1571
4
4
  chuk_tool_processor/core/processor.py,sha256=ttEYZTQHctXXiUP8gxAMCCSjbRvyOHojQe_UJezJYRs,18369
5
5
  chuk_tool_processor/execution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- chuk_tool_processor/execution/tool_executor.py,sha256=NSzmvqGMMyKuVapJAmPr-YtNgGhZI3fcAxhilyGG5kY,12174
6
+ chuk_tool_processor/execution/tool_executor.py,sha256=SGnOrsQJ8b9dPD_2rYlRyp1WLcwn7pLfbrm5APOsQvo,14387
7
7
  chuk_tool_processor/execution/strategies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  chuk_tool_processor/execution/strategies/inprocess_strategy.py,sha256=UJIv1g3Z9LpMsTYa9cqJB376StsI0up3cftH4OkqC2I,22582
9
9
  chuk_tool_processor/execution/strategies/subprocess_strategy.py,sha256=Rb5GTffl-4dkAQG_zz8wjggqyWznVOr9gReLGHmE2io,22469
@@ -21,10 +21,10 @@ chuk_tool_processor/mcp/mcp_tool.py,sha256=a7WnBiu8DaSuZ8RI0Ums4M5A7v46RvijlqZa0
21
21
  chuk_tool_processor/mcp/register_mcp_tools.py,sha256=0q73gafC1d0ei_gqNidcUeY7NUg13UZdjhVOKEFcD5o,3642
22
22
  chuk_tool_processor/mcp/setup_mcp_sse.py,sha256=T0V27azQy06yc-RSc5uzEKyhbyAXFT-7O3pIn4k10HQ,3769
23
23
  chuk_tool_processor/mcp/setup_mcp_stdio.py,sha256=P9qSgmxoNQbsOlGp83DlLLpN9BsG__MhlRsxFplNP3M,2753
24
- chuk_tool_processor/mcp/stream_manager.py,sha256=cRnGiuX2A4vHLP91XxFyNKp9Qbv41ImiqMS9F3UlUoA,14030
24
+ chuk_tool_processor/mcp/stream_manager.py,sha256=Wro1csV8S-V-PBan-4-c3cCyhJPmxG1wQJa4MLh02Dc,16914
25
25
  chuk_tool_processor/mcp/transport/__init__.py,sha256=7QQqeSKVKv0N9GcyJuYF0R4FDZeooii5RjggvFFg5GY,296
26
26
  chuk_tool_processor/mcp/transport/base_transport.py,sha256=1E29LjWw5vLQrPUDF_9TJt63P5dxAAN7n6E_KiZbGUY,3427
27
- chuk_tool_processor/mcp/transport/sse_transport.py,sha256=RC_m1relMkr5gQCUbH1z9JB2raZHj2MIEIq9Qyfpw0Y,17696
27
+ chuk_tool_processor/mcp/transport/sse_transport.py,sha256=2nyuc04Clc4FmJWxxZ2TmwiRd9NI7fpLqSN-g1wvRAI,19689
28
28
  chuk_tool_processor/mcp/transport/stdio_transport.py,sha256=lFXL7p8ca4z_J0RBL8UCHrQ1UH7C2-LbC0tZhpya4V4,7763
29
29
  chuk_tool_processor/models/__init__.py,sha256=TC__rdVa0lQsmJHM_hbLDPRgToa_pQT_UxRcPZk6iVw,40
30
30
  chuk_tool_processor/models/execution_strategy.py,sha256=UVW35YIeMY2B3mpIKZD2rAkyOPayI6ckOOUALyf0YiQ,2115
@@ -52,7 +52,7 @@ chuk_tool_processor/registry/providers/__init__.py,sha256=eigwG_So11j7WbDGSWaKd3
52
52
  chuk_tool_processor/registry/providers/memory.py,sha256=LlpPUU9E7S8Se6Q3VyKxLwpNm82SvmP8GLUmI8MkHxQ,5188
53
53
  chuk_tool_processor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  chuk_tool_processor/utils/validation.py,sha256=fiTSsHq7zx-kyd755GaFCvPCa-EVasSpg0A1liNHkxU,4138
55
- chuk_tool_processor-0.3.dist-info/METADATA,sha256=9D_wk8oZqFKWnxdjMTcqB9K2bIa0QrBk7Ep_kxqBZZ0,10163
56
- chuk_tool_processor-0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- chuk_tool_processor-0.3.dist-info/top_level.txt,sha256=7lTsnuRx4cOW4U2sNJWNxl4ZTt_J1ndkjTbj3pHPY5M,20
58
- chuk_tool_processor-0.3.dist-info/RECORD,,
55
+ chuk_tool_processor-0.4.dist-info/METADATA,sha256=yk5rGA3xmEMCEZtikLNE508Q7oY1GwbTZR2jzOR2P5I,24312
56
+ chuk_tool_processor-0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ chuk_tool_processor-0.4.dist-info/top_level.txt,sha256=7lTsnuRx4cOW4U2sNJWNxl4ZTt_J1ndkjTbj3pHPY5M,20
58
+ chuk_tool_processor-0.4.dist-info/RECORD,,
@@ -1,401 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: chuk-tool-processor
3
- Version: 0.3
4
- Summary: Add your description here
5
- Requires-Python: >=3.11
6
- Description-Content-Type: text/markdown
7
- Requires-Dist: chuk-mcp>=0.1.12
8
- Requires-Dist: dotenv>=0.9.9
9
- Requires-Dist: openai>=1.76.0
10
- Requires-Dist: pydantic>=2.11.3
11
- Requires-Dist: uuid>=1.30
12
-
13
- # CHUK Tool Processor
14
-
15
- An async-native framework for registering, discovering, and executing tools referenced in LLM responses.
16
-
17
- ## Quick Start
18
-
19
- ### Installation
20
-
21
- ```bash
22
- # Clone the repository
23
- git clone https://github.com/your-org/chuk-tool-processor.git
24
- cd chuk-tool-processor
25
-
26
- # Install with pip
27
- pip install -e .
28
- ```
29
-
30
- ### Basic Usage
31
-
32
- ```python
33
- import asyncio
34
- from chuk_tool_processor.registry import register_tool, initialize
35
- from chuk_tool_processor.models.tool_call import ToolCall
36
- from chuk_tool_processor.execution.strategies.inprocess_strategy import InProcessStrategy
37
- from chuk_tool_processor.execution.tool_executor import ToolExecutor
38
-
39
- # Register a simple tool
40
- @register_tool(name="calculator", description="Perform basic calculations")
41
- class CalculatorTool:
42
- async def execute(self, operation: str, x: float, y: float) -> dict:
43
- if operation == "add":
44
- result = x + y
45
- elif operation == "multiply":
46
- result = x * y
47
- else:
48
- raise ValueError(f"Unknown operation: {operation}")
49
-
50
- return {
51
- "operation": operation,
52
- "x": x,
53
- "y": y,
54
- "result": result
55
- }
56
-
57
- # Setup and execute tools
58
- async def main():
59
- # Initialize registry
60
- await initialize()
61
-
62
- # Get the default registry
63
- from chuk_tool_processor.registry import get_default_registry
64
- registry = await get_default_registry()
65
-
66
- # Create execution strategy and executor
67
- strategy = InProcessStrategy(registry)
68
- executor = ToolExecutor(registry=registry, strategy=strategy)
69
-
70
- # Create a tool call
71
- call = ToolCall(
72
- tool="calculator",
73
- arguments={
74
- "operation": "multiply",
75
- "x": 5,
76
- "y": 7
77
- }
78
- )
79
-
80
- # Execute tool
81
- results = await executor.execute([call])
82
-
83
- # Display result
84
- result = results[0]
85
- if not result.error:
86
- print(f"Result: {result.result}")
87
- else:
88
- print(f"Error: {result.error}")
89
-
90
- if __name__ == "__main__":
91
- asyncio.run(main())
92
- ```
93
-
94
- ## Core Features
95
-
96
- ### Async-Native Architecture
97
-
98
- The entire framework is built with native `async/await` support, allowing for:
99
- - Non-blocking execution of tools
100
- - True concurrency with controlled parallelism
101
- - Task-local context tracking across async boundaries
102
-
103
- ### Tool Registry
104
-
105
- Tools are registered in a central registry with optional namespaces:
106
-
107
- ```python
108
- # Register with default parameters
109
- @register_tool()
110
- class SimpleGreeter:
111
- async def execute(self, name: str) -> str:
112
- return f"Hello, {name}!"
113
-
114
- # Register with custom name and namespace
115
- @register_tool(name="weather", namespace="api", description="Get weather info")
116
- class WeatherTool:
117
- async def execute(self, location: str, units: str = "metric") -> dict:
118
- # Implementation...
119
- return {"temperature": 23.5, "conditions": "Sunny"}
120
- ```
121
-
122
- Initialize and access the registry:
123
-
124
- ```python
125
- # Initialize registry (required at startup)
126
- await initialize()
127
-
128
- # Get registry
129
- registry = await get_default_registry()
130
-
131
- # Look up a tool
132
- tool = await registry.get_tool("weather", namespace="api")
133
-
134
- # List registered tools
135
- tools = await registry.list_tools()
136
-
137
- # Get tool metadata
138
- metadata = await registry.get_metadata("weather", namespace="api")
139
- ```
140
-
141
- ### Execution Strategies
142
-
143
- Two execution strategies are provided:
144
-
145
- #### 1. InProcessStrategy
146
-
147
- Executes tools in the same process with optional concurrency control:
148
-
149
- ```python
150
- from chuk_tool_processor.execution.strategies.inprocess_strategy import InProcessStrategy
151
-
152
- strategy = InProcessStrategy(
153
- registry,
154
- default_timeout=10.0, # Timeout for tool execution
155
- max_concurrency=5 # Maximum concurrent executions
156
- )
157
- ```
158
-
159
- #### 2. SubprocessStrategy
160
-
161
- Executes tools in separate processes for true isolation:
162
-
163
- ```python
164
- from chuk_tool_processor.execution.strategies.subprocess_strategy import SubprocessStrategy
165
-
166
- strategy = SubprocessStrategy(
167
- registry,
168
- max_workers=4, # Maximum worker processes
169
- default_timeout=30.0 # Timeout for tool execution
170
- )
171
- ```
172
-
173
- ### Execution Wrappers
174
-
175
- Enhance tool execution with optional wrappers:
176
-
177
- #### Retry Logic
178
-
179
- ```python
180
- from chuk_tool_processor.execution.wrappers.retry import RetryConfig, RetryableToolExecutor
181
-
182
- # Use as a wrapper
183
- retry_executor = RetryableToolExecutor(
184
- executor=base_executor,
185
- default_config=RetryConfig(
186
- max_retries=3,
187
- base_delay=0.5,
188
- jitter=True
189
- )
190
- )
191
-
192
- # Or as a decorator
193
- from chuk_tool_processor.execution.wrappers.retry import retryable
194
-
195
- @retryable(max_retries=3, base_delay=0.5)
196
- @register_tool(name="flaky_api")
197
- class FlakyApiTool:
198
- async def execute(self, query: str) -> dict:
199
- # Implementation with potential failures
200
- pass
201
- ```
202
-
203
- #### Caching
204
-
205
- ```python
206
- from chuk_tool_processor.execution.wrappers.caching import InMemoryCache, CachingToolExecutor
207
-
208
- # Use as a wrapper
209
- cache = InMemoryCache(default_ttl=60) # 60 second TTL
210
- cache_executor = CachingToolExecutor(
211
- executor=base_executor,
212
- cache=cache
213
- )
214
-
215
- # Or as a decorator
216
- from chuk_tool_processor.execution.wrappers.caching import cacheable
217
-
218
- @cacheable(ttl=300) # Cache for 5 minutes
219
- @register_tool(name="expensive_operation")
220
- class ExpensiveOperationTool:
221
- async def execute(self, input_value: int) -> dict:
222
- # Expensive computation
223
- pass
224
- ```
225
-
226
- #### Rate Limiting
227
-
228
- ```python
229
- from chuk_tool_processor.execution.wrappers.rate_limiting import RateLimiter, RateLimitedToolExecutor
230
-
231
- # Use as a wrapper
232
- limiter = RateLimiter(global_limit=100, global_period=60.0) # 100 requests per minute
233
- rate_limited_executor = RateLimitedToolExecutor(
234
- executor=base_executor,
235
- limiter=limiter
236
- )
237
-
238
- # Or as a decorator
239
- from chuk_tool_processor.execution.wrappers.rate_limiting import rate_limited
240
-
241
- @rate_limited(limit=5, period=60.0) # 5 requests per minute
242
- @register_tool(name="external_api")
243
- class ExternalApiTool:
244
- async def execute(self, query: str) -> dict:
245
- # Call to rate-limited external API
246
- pass
247
- ```
248
-
249
- ### Streaming Tool Support
250
-
251
- Tools can stream results incrementally:
252
-
253
- ```python
254
- from chuk_tool_processor.models.streaming_tool import StreamingTool
255
- from pydantic import BaseModel, Field
256
- from typing import AsyncIterator
257
-
258
- @register_tool(name="counter")
259
- class CounterTool(StreamingTool):
260
- """Stream incremental counts."""
261
-
262
- class Arguments(BaseModel):
263
- start: int = Field(1, description="Starting value")
264
- end: int = Field(10, description="Ending value")
265
- delay: float = Field(0.5, description="Delay between items")
266
-
267
- class Result(BaseModel):
268
- value: int
269
- timestamp: str
270
-
271
- async def _stream_execute(self, start: int, end: int, delay: float) -> AsyncIterator[Result]:
272
- """Stream each count with a delay."""
273
- from datetime import datetime
274
-
275
- for i in range(start, end + 1):
276
- await asyncio.sleep(delay)
277
- yield self.Result(
278
- value=i,
279
- timestamp=datetime.now().isoformat()
280
- )
281
-
282
- # Stream results
283
- async for result in executor.stream_execute([tool_call]):
284
- print(f"Received: {result.result.value}")
285
- ```
286
-
287
- ### Comprehensive Error Handling
288
-
289
- Errors are captured in the result objects rather than raising exceptions:
290
-
291
- ```python
292
- # Execute tool calls
293
- results = await executor.execute([call1, call2, call3])
294
-
295
- for result in results:
296
- if result.error:
297
- print(f"Tool {result.tool} failed: {result.error}")
298
- print(f"Duration: {(result.end_time - result.start_time).total_seconds()}s")
299
- else:
300
- print(f"Tool {result.tool} succeeded: {result.result}")
301
- ```
302
-
303
- ### Validation
304
-
305
- Validate tool arguments and results:
306
-
307
- ```python
308
- from pydantic import BaseModel
309
- from chuk_tool_processor.models.validated_tool import ValidatedTool
310
-
311
- @register_tool(name="validate_data", namespace="utils")
312
- class ValidatedDataTool(ValidatedTool):
313
- class Arguments(BaseModel):
314
- username: str
315
- age: int
316
- email: str
317
-
318
- class Result(BaseModel):
319
- is_valid: bool
320
- errors: list[str] = []
321
-
322
- async def _execute(self, username: str, age: int, email: str) -> Result:
323
- errors = []
324
-
325
- if len(username) < 3:
326
- errors.append("Username too short")
327
-
328
- if age < 18:
329
- errors.append("Must be 18 or older")
330
-
331
- if "@" not in email:
332
- errors.append("Invalid email")
333
-
334
- return self.Result(
335
- is_valid=len(errors) == 0,
336
- errors=errors
337
- )
338
- ```
339
-
340
- ## Processing LLM Responses
341
-
342
- The `ToolProcessor` helps extract and execute tool calls from LLM responses:
343
-
344
- ```python
345
- from chuk_tool_processor.core.processor import ToolProcessor
346
-
347
- # Create processor
348
- processor = ToolProcessor()
349
- await processor.initialize()
350
-
351
- # Process text with tool calls
352
- llm_output = """
353
- I'll help calculate that for you.
354
-
355
- <tool name="calculator" args='{"operation": "multiply", "x": 5, "y": 7}'/>
356
-
357
- The result should be 35.
358
- """
359
-
360
- # Extract and execute tool calls
361
- results = await processor.process_text(llm_output)
362
-
363
- # Process results
364
- for result in results:
365
- print(f"Tool: {result.tool}")
366
- print(f"Result: {result.result}")
367
- print(f"Error: {result.error}")
368
- ```
369
-
370
- The processor supports various formats:
371
-
372
- ```python
373
- # XML format
374
- <tool name="calculator" args='{"operation": "add", "x": 5, "y": 3}'/>
375
-
376
- # OpenAI function call format
377
- {
378
- "function_call": {
379
- "name": "calculator",
380
- "arguments": "{\"operation\": \"add\", \"x\": 5, \"y\": 3}"
381
- }
382
- }
383
-
384
- # JSON format
385
- {
386
- "tool_calls": [
387
- {
388
- "id": "call_123",
389
- "type": "function",
390
- "function": {
391
- "name": "calculator",
392
- "arguments": "{\"operation\": \"add\", \"x\": 5, \"y\": 3}"
393
- }
394
- }
395
- ]
396
- }
397
- ```
398
-
399
- ## License
400
-
401
- This project is licensed under the MIT License - see the LICENSE file for details.