chuk-tool-processor 0.6.7__py3-none-any.whl → 0.6.9__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,6 +3,7 @@ from __future__ import annotations
3
3
 
4
4
  import asyncio
5
5
  import json
6
+ import time
6
7
  from typing import Dict, Any, List, Optional
7
8
  import logging
8
9
 
@@ -11,28 +12,34 @@ from chuk_mcp.transports.stdio import stdio_client
11
12
  from chuk_mcp.transports.stdio.parameters import StdioParameters
12
13
  from chuk_mcp.protocol.messages import (
13
14
  send_initialize, send_ping, send_tools_list, send_tools_call,
15
+ send_resources_list, send_resources_read,
16
+ send_prompts_list, send_prompts_get,
14
17
  )
15
18
 
16
- # Optional imports
17
- try:
18
- from chuk_mcp.protocol.messages import send_resources_list, send_resources_read
19
- HAS_RESOURCES = True
20
- except ImportError:
21
- HAS_RESOURCES = False
22
-
23
- try:
24
- from chuk_mcp.protocol.messages import send_prompts_list, send_prompts_get
25
- HAS_PROMPTS = True
26
- except ImportError:
27
- HAS_PROMPTS = False
28
-
29
19
  logger = logging.getLogger(__name__)
30
20
 
31
21
 
32
22
  class StdioTransport(MCPBaseTransport):
33
- """Ultra-lightweight wrapper around chuk-mcp stdio transport."""
23
+ """
24
+ STDIO transport for MCP communication using process pipes.
25
+
26
+ This transport uses subprocess communication via stdin/stdout pipes
27
+ to communicate with MCP servers.
28
+ """
34
29
 
35
- def __init__(self, server_params):
30
+ def __init__(self, server_params,
31
+ connection_timeout: float = 30.0,
32
+ default_timeout: float = 30.0,
33
+ enable_metrics: bool = True):
34
+ """
35
+ Initialize STDIO transport.
36
+
37
+ Args:
38
+ server_params: Server parameters (dict or StdioParameters object)
39
+ connection_timeout: Timeout for initial connection setup
40
+ default_timeout: Default timeout for operations
41
+ enable_metrics: Whether to track performance metrics
42
+ """
36
43
  # Convert dict to StdioParameters if needed
37
44
  if isinstance(server_params, dict):
38
45
  self.server_params = StdioParameters(
@@ -43,107 +50,278 @@ class StdioTransport(MCPBaseTransport):
43
50
  else:
44
51
  self.server_params = server_params
45
52
 
53
+ self.connection_timeout = connection_timeout
54
+ self.default_timeout = default_timeout
55
+ self.enable_metrics = enable_metrics
56
+
57
+ # Connection state
46
58
  self._context = None
47
59
  self._streams = None
48
60
  self._initialized = False
61
+
62
+ # Performance metrics (consistent with other transports)
63
+ self._metrics = {
64
+ "total_calls": 0,
65
+ "successful_calls": 0,
66
+ "failed_calls": 0,
67
+ "total_time": 0.0,
68
+ "avg_response_time": 0.0,
69
+ "last_ping_time": None,
70
+ "initialization_time": None,
71
+ "process_restarts": 0,
72
+ "pipe_errors": 0
73
+ }
74
+
75
+ logger.debug("STDIO transport initialized for command: %s",
76
+ getattr(self.server_params, 'command', 'unknown'))
49
77
 
50
78
  async def initialize(self) -> bool:
51
- """Initialize by delegating to chuk-mcp."""
79
+ """Initialize by delegating to chuk-mcp with timeout protection."""
52
80
  if self._initialized:
81
+ logger.warning("Transport already initialized")
53
82
  return True
54
-
83
+
84
+ start_time = time.time()
85
+
55
86
  try:
56
87
  logger.debug("Initializing STDIO transport...")
88
+
89
+ # Create context with timeout protection
57
90
  self._context = stdio_client(self.server_params)
58
- self._streams = await self._context.__aenter__()
91
+ self._streams = await asyncio.wait_for(
92
+ self._context.__aenter__(),
93
+ timeout=self.connection_timeout
94
+ )
95
+
96
+ # Send initialize message with timeout
97
+ init_result = await asyncio.wait_for(
98
+ send_initialize(*self._streams),
99
+ timeout=self.default_timeout
100
+ )
59
101
 
60
- # Send initialize message
61
- init_result = await send_initialize(*self._streams)
62
102
  if init_result:
63
103
  self._initialized = True
64
- logger.debug("STDIO transport initialized successfully")
104
+
105
+ if self.enable_metrics:
106
+ init_time = time.time() - start_time
107
+ self._metrics["initialization_time"] = init_time
108
+
109
+ logger.debug("STDIO transport initialized successfully in %.3fs", time.time() - start_time)
65
110
  return True
66
111
  else:
112
+ logger.error("STDIO initialization failed")
67
113
  await self._cleanup()
68
114
  return False
115
+
116
+ except asyncio.TimeoutError:
117
+ logger.error("STDIO initialization timed out after %ss", self.connection_timeout)
118
+ await self._cleanup()
119
+ return False
69
120
  except Exception as e:
70
121
  logger.error("Error initializing STDIO transport: %s", e)
71
122
  await self._cleanup()
72
123
  return False
73
124
 
74
125
  async def close(self) -> None:
75
- """Close by delegating to chuk-mcp context manager."""
126
+ """Close by delegating to chuk-mcp context manager with enhanced cleanup."""
127
+ if not self._initialized:
128
+ return
129
+
130
+ # Log final metrics
131
+ if self.enable_metrics and self._metrics["total_calls"] > 0:
132
+ logger.debug(
133
+ "STDIO transport closing - Total calls: %d, Success rate: %.1f%%, Avg response time: %.3fs",
134
+ self._metrics["total_calls"],
135
+ (self._metrics["successful_calls"] / self._metrics["total_calls"] * 100),
136
+ self._metrics["avg_response_time"]
137
+ )
138
+
76
139
  if self._context:
77
140
  try:
78
- # Simple delegation - the StreamManager now calls this in the correct context
79
141
  await self._context.__aexit__(None, None, None)
142
+ logger.debug("STDIO context closed")
80
143
  except Exception as e:
81
- logger.debug("Error during close: %s", e)
144
+ logger.debug("Error during STDIO close: %s", e)
82
145
  finally:
83
146
  await self._cleanup()
84
147
 
85
148
  async def _cleanup(self) -> None:
86
- """Minimal cleanup."""
149
+ """Clean up internal state."""
87
150
  self._context = None
88
151
  self._streams = None
89
152
  self._initialized = False
90
153
 
91
154
  async def send_ping(self) -> bool:
92
- """Delegate ping to chuk-mcp."""
155
+ """Send ping with performance tracking."""
93
156
  if not self._initialized:
94
157
  return False
158
+
159
+ start_time = time.time()
95
160
  try:
96
- return bool(await send_ping(*self._streams))
97
- except Exception:
161
+ result = await asyncio.wait_for(
162
+ send_ping(*self._streams),
163
+ timeout=self.default_timeout
164
+ )
165
+
166
+ if self.enable_metrics:
167
+ ping_time = time.time() - start_time
168
+ self._metrics["last_ping_time"] = ping_time
169
+ logger.debug("STDIO ping completed in %.3fs: %s", ping_time, result)
170
+
171
+ return bool(result)
172
+ except asyncio.TimeoutError:
173
+ logger.error("STDIO ping timed out")
98
174
  return False
175
+ except Exception as e:
176
+ logger.error("STDIO ping failed: %s", e)
177
+ if self.enable_metrics:
178
+ self._metrics["pipe_errors"] += 1
179
+ return False
180
+
181
+ def is_connected(self) -> bool:
182
+ """Check connection status."""
183
+ return self._initialized and self._streams is not None
99
184
 
100
185
  async def get_tools(self) -> List[Dict[str, Any]]:
101
- """Delegate tools list to chuk-mcp."""
186
+ """Get tools list with performance tracking."""
102
187
  if not self._initialized:
188
+ logger.error("Cannot get tools: transport not initialized")
103
189
  return []
190
+
191
+ start_time = time.time()
104
192
  try:
105
- response = await send_tools_list(*self._streams)
193
+ response = await asyncio.wait_for(
194
+ send_tools_list(*self._streams),
195
+ timeout=self.default_timeout
196
+ )
197
+
198
+ # Normalize response
106
199
  if isinstance(response, dict):
107
- return response.get("tools", [])
108
- return response if isinstance(response, list) else []
109
- except Exception:
200
+ tools = response.get("tools", [])
201
+ elif isinstance(response, list):
202
+ tools = response
203
+ else:
204
+ logger.warning("Unexpected tools response type: %s", type(response))
205
+ tools = []
206
+
207
+ if self.enable_metrics:
208
+ response_time = time.time() - start_time
209
+ logger.debug("Retrieved %d tools in %.3fs", len(tools), response_time)
210
+
211
+ return tools
212
+
213
+ except asyncio.TimeoutError:
214
+ logger.error("Get tools timed out")
215
+ return []
216
+ except Exception as e:
217
+ logger.error("Error getting tools: %s", e)
218
+ if self.enable_metrics:
219
+ self._metrics["pipe_errors"] += 1
110
220
  return []
111
221
 
112
- async def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
113
- """Delegate tool execution to chuk-mcp."""
222
+ async def call_tool(self, tool_name: str, arguments: Dict[str, Any],
223
+ timeout: Optional[float] = None) -> Dict[str, Any]:
224
+ """Call tool with timeout support and performance tracking."""
114
225
  if not self._initialized:
115
226
  return {"isError": True, "error": "Transport not initialized"}
227
+
228
+ tool_timeout = timeout or self.default_timeout
229
+ start_time = time.time()
116
230
 
231
+ if self.enable_metrics:
232
+ self._metrics["total_calls"] += 1 # FIXED: INCREMENT FIRST
233
+
117
234
  try:
118
- response = await send_tools_call(*self._streams, tool_name, arguments)
119
- return self._normalize_response(response)
235
+ logger.debug("Calling tool '%s' with timeout %ss", tool_name, tool_timeout)
236
+
237
+ response = await asyncio.wait_for(
238
+ send_tools_call(*self._streams, tool_name, arguments),
239
+ timeout=tool_timeout
240
+ )
241
+
242
+ response_time = time.time() - start_time
243
+ result = self._normalize_mcp_response(response)
244
+
245
+ if self.enable_metrics:
246
+ self._update_metrics(response_time, not result.get("isError", False))
247
+
248
+ if not result.get("isError", False):
249
+ logger.debug("Tool '%s' completed successfully in %.3fs", tool_name, response_time)
250
+ else:
251
+ logger.warning("Tool '%s' failed in %.3fs: %s", tool_name, response_time, result.get('error', 'Unknown error'))
252
+
253
+ return result
254
+
255
+ except asyncio.TimeoutError:
256
+ response_time = time.time() - start_time
257
+ if self.enable_metrics:
258
+ self._update_metrics(response_time, False)
259
+
260
+ error_msg = f"Tool execution timed out after {tool_timeout}s"
261
+ logger.error("Tool '%s' %s", tool_name, error_msg)
262
+ return {
263
+ "isError": True,
264
+ "error": error_msg
265
+ }
120
266
  except Exception as e:
121
- return {"isError": True, "error": f"Tool execution failed: {str(e)}"}
267
+ response_time = time.time() - start_time
268
+ if self.enable_metrics:
269
+ self._update_metrics(response_time, False)
270
+ self._metrics["pipe_errors"] += 1
271
+
272
+ error_msg = f"Tool execution failed: {str(e)}"
273
+ logger.error("Tool '%s' error: %s", tool_name, error_msg)
274
+ return {
275
+ "isError": True,
276
+ "error": error_msg
277
+ }
122
278
 
123
- def _normalize_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
124
- """Minimal response normalization."""
279
+ def _update_metrics(self, response_time: float, success: bool) -> None:
280
+ """Update performance metrics."""
281
+ if success:
282
+ self._metrics["successful_calls"] += 1
283
+ else:
284
+ self._metrics["failed_calls"] += 1
285
+
286
+ self._metrics["total_time"] += response_time
287
+ # FIXED: Only calculate average if we have total calls
288
+ if self._metrics["total_calls"] > 0:
289
+ self._metrics["avg_response_time"] = (
290
+ self._metrics["total_time"] / self._metrics["total_calls"]
291
+ )
292
+
293
+ def _normalize_mcp_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
294
+ """
295
+ Normalize response using shared base class logic with STDIO-specific handling.
296
+
297
+ STDIO has special requirements for preserving string representations
298
+ of numeric values for backward compatibility.
299
+ """
300
+ # Handle explicit error in response
125
301
  if "error" in response:
126
302
  error_info = response["error"]
127
303
  error_msg = error_info.get("message", str(error_info)) if isinstance(error_info, dict) else str(error_info)
128
304
  return {"isError": True, "error": error_msg}
129
305
 
306
+ # Handle successful response with result
130
307
  if "result" in response:
131
308
  result = response["result"]
132
309
  if isinstance(result, dict) and "content" in result:
133
- return {"isError": False, "content": self._extract_content(result["content"])}
310
+ return {"isError": False, "content": self._extract_stdio_content(result["content"])}
134
311
  return {"isError": False, "content": result}
135
312
 
313
+ # Handle direct content-based response
136
314
  if "content" in response:
137
- return {"isError": False, "content": self._extract_content(response["content"])}
315
+ return {"isError": False, "content": self._extract_stdio_content(response["content"])}
138
316
 
139
317
  return {"isError": False, "content": response}
140
318
 
141
- def _extract_content(self, content_list: Any) -> Any:
319
+ def _extract_stdio_content(self, content_list: Any) -> Any:
142
320
  """
143
- Minimal content extraction - FIXED to return strings consistently.
321
+ Extract content with STDIO-specific string preservation logic.
144
322
 
145
- The test expects result["content"] to be "42" (string), but we were
146
- returning 42 (integer) after JSON parsing.
323
+ STDIO transport preserves string representations of numeric values
324
+ for backward compatibility with existing tests.
147
325
  """
148
326
  if not isinstance(content_list, list) or not content_list:
149
327
  return content_list
@@ -153,12 +331,11 @@ class StdioTransport(MCPBaseTransport):
153
331
  if isinstance(item, dict) and item.get("type") == "text":
154
332
  text = item.get("text", "")
155
333
 
156
- # FIXED: Always try to parse JSON, but preserve strings as strings
157
- # if they look like simple values (numbers, booleans, etc.)
334
+ # STDIO-specific: preserve string format for numeric values
158
335
  try:
159
336
  parsed = json.loads(text)
160
337
  # If the parsed result is a simple type and the original was a string,
161
- # keep it as a string to maintain consistency with test expectations
338
+ # keep it as a string to maintain compatibility
162
339
  if isinstance(parsed, (int, float, bool)) and isinstance(text, str):
163
340
  # Check if this looks like a simple numeric string
164
341
  if text.strip().isdigit() or (text.strip().replace('.', '', 1).isdigit()):
@@ -170,67 +347,106 @@ class StdioTransport(MCPBaseTransport):
170
347
 
171
348
  return content_list
172
349
 
173
- # Optional features
174
350
  async def list_resources(self) -> Dict[str, Any]:
175
- """Delegate resources to chuk-mcp if available."""
176
- if not HAS_RESOURCES or not self._initialized:
351
+ """List resources with error handling."""
352
+ if not self._initialized:
177
353
  return {}
178
354
  try:
179
- response = await send_resources_list(*self._streams)
355
+ response = await asyncio.wait_for(
356
+ send_resources_list(*self._streams),
357
+ timeout=self.default_timeout
358
+ )
180
359
  return response if isinstance(response, dict) else {}
181
- except Exception:
360
+ except asyncio.TimeoutError:
361
+ logger.error("List resources timed out")
362
+ return {}
363
+ except Exception as e:
364
+ logger.debug("Error listing resources: %s", e)
182
365
  return {}
183
366
 
184
- async def read_resource(self, uri: str) -> Dict[str, Any]:
185
- """Delegate resource reading to chuk-mcp if available."""
186
- if not HAS_RESOURCES or not self._initialized:
367
+ async def list_prompts(self) -> Dict[str, Any]:
368
+ """List prompts with error handling."""
369
+ if not self._initialized:
187
370
  return {}
188
371
  try:
189
- response = await send_resources_read(*self._streams, uri)
372
+ response = await asyncio.wait_for(
373
+ send_prompts_list(*self._streams),
374
+ timeout=self.default_timeout
375
+ )
190
376
  return response if isinstance(response, dict) else {}
191
- except Exception:
377
+ except asyncio.TimeoutError:
378
+ logger.error("List prompts timed out")
379
+ return {}
380
+ except Exception as e:
381
+ logger.debug("Error listing prompts: %s", e)
192
382
  return {}
193
383
 
194
- async def list_prompts(self) -> Dict[str, Any]:
195
- """Delegate prompts to chuk-mcp if available."""
196
- if not HAS_PROMPTS or not self._initialized:
384
+ async def read_resource(self, uri: str) -> Dict[str, Any]:
385
+ """Read a specific resource."""
386
+ if not self._initialized:
197
387
  return {}
198
388
  try:
199
- response = await send_prompts_list(*self._streams)
389
+ response = await asyncio.wait_for(
390
+ send_resources_read(*self._streams, uri),
391
+ timeout=self.default_timeout
392
+ )
200
393
  return response if isinstance(response, dict) else {}
201
- except Exception:
394
+ except Exception as e:
395
+ logger.debug("Error reading resource: %s", e)
202
396
  return {}
203
397
 
204
398
  async def get_prompt(self, name: str, arguments: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
205
- """Delegate prompt retrieval to chuk-mcp if available."""
206
- if not HAS_PROMPTS or not self._initialized:
399
+ """Get a specific prompt."""
400
+ if not self._initialized:
207
401
  return {}
208
402
  try:
209
- response = await send_prompts_get(*self._streams, name, arguments or {})
403
+ response = await asyncio.wait_for(
404
+ send_prompts_get(*self._streams, name, arguments or {}),
405
+ timeout=self.default_timeout
406
+ )
210
407
  return response if isinstance(response, dict) else {}
211
- except Exception:
408
+ except Exception as e:
409
+ logger.debug("Error getting prompt: %s", e)
212
410
  return {}
213
411
 
214
- # Backward compatibility
412
+ # ------------------------------------------------------------------ #
413
+ # Metrics and monitoring (now consistent with other transports) #
414
+ # ------------------------------------------------------------------ #
415
+ def get_metrics(self) -> Dict[str, Any]:
416
+ """Get performance and connection metrics."""
417
+ return self._metrics.copy()
418
+
419
+ def reset_metrics(self) -> None:
420
+ """Reset performance metrics."""
421
+ self._metrics = {
422
+ "total_calls": 0,
423
+ "successful_calls": 0,
424
+ "failed_calls": 0,
425
+ "total_time": 0.0,
426
+ "avg_response_time": 0.0,
427
+ "last_ping_time": self._metrics.get("last_ping_time"),
428
+ "initialization_time": self._metrics.get("initialization_time"),
429
+ "process_restarts": self._metrics.get("process_restarts", 0),
430
+ "pipe_errors": 0
431
+ }
432
+
433
+ # ------------------------------------------------------------------ #
434
+ # Backward compatibility #
435
+ # ------------------------------------------------------------------ #
215
436
  def get_streams(self) -> List[tuple]:
216
437
  """Provide streams for backward compatibility."""
217
438
  return [self._streams] if self._streams else []
218
439
 
219
- def is_connected(self) -> bool:
220
- """Check connection status."""
221
- return self._initialized
222
-
440
+ # ------------------------------------------------------------------ #
441
+ # Context manager support (now uses base class with fixed error) #
442
+ # ------------------------------------------------------------------ #
223
443
  async def __aenter__(self):
224
444
  """Context manager support."""
225
- if not await self.initialize():
226
- raise RuntimeError("Failed to initialize STDIO transport")
445
+ success = await self.initialize()
446
+ if not success:
447
+ raise RuntimeError("Failed to initialize StdioTransport") # FIXED: message
227
448
  return self
228
449
 
229
450
  async def __aexit__(self, exc_type, exc_val, exc_tb):
230
451
  """Context manager cleanup."""
231
- await self.close()
232
-
233
- def __repr__(self) -> str:
234
- """String representation."""
235
- status = "initialized" if self._initialized else "not initialized"
236
- return f"StdioTransport(status={status}, command={getattr(self.server_params, 'command', 'unknown')})"
452
+ await self.close()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chuk-tool-processor
3
- Version: 0.6.7
3
+ Version: 0.6.9
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>
@@ -22,12 +22,12 @@ chuk_tool_processor/mcp/register_mcp_tools.py,sha256=s6mQMtZr7dswT2WXDJ84zjOTSi3
22
22
  chuk_tool_processor/mcp/setup_mcp_http_streamable.py,sha256=ZJUAj7LL4CRfc-CBl0SQJk0qfW12IuixR-7J2hbQ8S8,4538
23
23
  chuk_tool_processor/mcp/setup_mcp_sse.py,sha256=4nf0V6cykAPLxtgsl8RTAYQdVWITUNu_3CIU1vcLjlo,3795
24
24
  chuk_tool_processor/mcp/setup_mcp_stdio.py,sha256=L8anrx_b5HDsaMqAfbpWaHex084DTd76W8WBf3ClC48,2884
25
- chuk_tool_processor/mcp/stream_manager.py,sha256=DU1RkFDqyz9PZVNBgx-LeJM8Tt7hf-_mmYymRvfnqKY,32701
26
- chuk_tool_processor/mcp/transport/__init__.py,sha256=0DX7m_VvlXPxijc-88_QTLhq4ZqAgUgzBjSMGL9C_lM,963
27
- chuk_tool_processor/mcp/transport/base_transport.py,sha256=bqId34OMQMxzMXtrKq_86sot0_x0NS_ecaIllsCyy6I,3423
28
- chuk_tool_processor/mcp/transport/http_streamable_transport.py,sha256=3I3tNYU8r4YqCbNhMCkoucvZc6VS2ulzeUjDe2FbcRk,19108
29
- chuk_tool_processor/mcp/transport/sse_transport.py,sha256=s8an1sHLFsIJwqxUcdSvY6bYBkdyNQ-LkQMqzD5okdI,17253
30
- chuk_tool_processor/mcp/transport/stdio_transport.py,sha256=DPXLR_OxuCJ2bgwYDuT_iYC_CDcUIySvLNO7JzyiPyc,9099
25
+ chuk_tool_processor/mcp/stream_manager.py,sha256=sRy6X893ISht4XUFVIjUHQ0zVisyP7ex2s2wiPQv2fg,33858
26
+ chuk_tool_processor/mcp/transport/__init__.py,sha256=2IZHt5x8GD7jgPmt5JvUIhSBij4kf9vZta1rL_-UDMc,657
27
+ chuk_tool_processor/mcp/transport/base_transport.py,sha256=EapglrjRuSniGkEoEeL76ZGx_VriHqR1coK32nFMrd8,9175
28
+ chuk_tool_processor/mcp/transport/http_streamable_transport.py,sha256=FkOlZDROLrRdPV5e3UeEwHlUbQlMK9eLaK8LbVcnnOo,18372
29
+ chuk_tool_processor/mcp/transport/sse_transport.py,sha256=zE91K48HNcqcyiUL8nEn-9oDDpR-rHi483WgZ3f-CSA,22684
30
+ chuk_tool_processor/mcp/transport/stdio_transport.py,sha256=I64dYt3ZXGmWMmTegdspKZcLggoNAdvxfqh-z8tAO1s,17683
31
31
  chuk_tool_processor/models/__init__.py,sha256=TC__rdVa0lQsmJHM_hbLDPRgToa_pQT_UxRcPZk6iVw,40
32
32
  chuk_tool_processor/models/execution_strategy.py,sha256=UVW35YIeMY2B3mpIKZD2rAkyOPayI6ckOOUALyf0YiQ,2115
33
33
  chuk_tool_processor/models/streaming_tool.py,sha256=0v2PSPTgZ5TS_PpVdohvVhh99fPwPQM_R_z4RU0mlLM,3541
@@ -54,7 +54,7 @@ chuk_tool_processor/registry/providers/__init__.py,sha256=eigwG_So11j7WbDGSWaKd3
54
54
  chuk_tool_processor/registry/providers/memory.py,sha256=6cMtUwLO6zrk3pguQRgxJ2CReHAzewgZsizWZhsoStk,5184
55
55
  chuk_tool_processor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
56
  chuk_tool_processor/utils/validation.py,sha256=V5N1dH9sJlHepFIbiI2k2MU82o7nvnh0hKyIt2jdgww,4136
57
- chuk_tool_processor-0.6.7.dist-info/METADATA,sha256=2MPxA-1YJcyWceWZpucwslCce1w7xewFoDZXb1iDi80,23463
58
- chuk_tool_processor-0.6.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
59
- chuk_tool_processor-0.6.7.dist-info/top_level.txt,sha256=7lTsnuRx4cOW4U2sNJWNxl4ZTt_J1ndkjTbj3pHPY5M,20
60
- chuk_tool_processor-0.6.7.dist-info/RECORD,,
57
+ chuk_tool_processor-0.6.9.dist-info/METADATA,sha256=ywTwAgW9KZmQxNtELdMvGenXaiFNvjflyvI3oevrbuY,23463
58
+ chuk_tool_processor-0.6.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
59
+ chuk_tool_processor-0.6.9.dist-info/top_level.txt,sha256=7lTsnuRx4cOW4U2sNJWNxl4ZTt_J1ndkjTbj3pHPY5M,20
60
+ chuk_tool_processor-0.6.9.dist-info/RECORD,,