chuk-tool-processor 0.6.4__py3-none-any.whl → 0.9.7__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.

Files changed (66) hide show
  1. chuk_tool_processor/core/__init__.py +32 -1
  2. chuk_tool_processor/core/exceptions.py +225 -13
  3. chuk_tool_processor/core/processor.py +135 -104
  4. chuk_tool_processor/execution/strategies/__init__.py +6 -0
  5. chuk_tool_processor/execution/strategies/inprocess_strategy.py +142 -150
  6. chuk_tool_processor/execution/strategies/subprocess_strategy.py +202 -206
  7. chuk_tool_processor/execution/tool_executor.py +82 -84
  8. chuk_tool_processor/execution/wrappers/__init__.py +42 -0
  9. chuk_tool_processor/execution/wrappers/caching.py +150 -116
  10. chuk_tool_processor/execution/wrappers/circuit_breaker.py +370 -0
  11. chuk_tool_processor/execution/wrappers/rate_limiting.py +76 -43
  12. chuk_tool_processor/execution/wrappers/retry.py +116 -78
  13. chuk_tool_processor/logging/__init__.py +23 -17
  14. chuk_tool_processor/logging/context.py +40 -45
  15. chuk_tool_processor/logging/formatter.py +22 -21
  16. chuk_tool_processor/logging/helpers.py +28 -42
  17. chuk_tool_processor/logging/metrics.py +13 -15
  18. chuk_tool_processor/mcp/__init__.py +8 -12
  19. chuk_tool_processor/mcp/mcp_tool.py +158 -114
  20. chuk_tool_processor/mcp/register_mcp_tools.py +22 -22
  21. chuk_tool_processor/mcp/setup_mcp_http_streamable.py +57 -17
  22. chuk_tool_processor/mcp/setup_mcp_sse.py +57 -17
  23. chuk_tool_processor/mcp/setup_mcp_stdio.py +11 -11
  24. chuk_tool_processor/mcp/stream_manager.py +333 -276
  25. chuk_tool_processor/mcp/transport/__init__.py +22 -29
  26. chuk_tool_processor/mcp/transport/base_transport.py +180 -44
  27. chuk_tool_processor/mcp/transport/http_streamable_transport.py +505 -325
  28. chuk_tool_processor/mcp/transport/models.py +100 -0
  29. chuk_tool_processor/mcp/transport/sse_transport.py +607 -276
  30. chuk_tool_processor/mcp/transport/stdio_transport.py +597 -116
  31. chuk_tool_processor/models/__init__.py +21 -1
  32. chuk_tool_processor/models/execution_strategy.py +16 -21
  33. chuk_tool_processor/models/streaming_tool.py +28 -25
  34. chuk_tool_processor/models/tool_call.py +49 -31
  35. chuk_tool_processor/models/tool_export_mixin.py +22 -8
  36. chuk_tool_processor/models/tool_result.py +40 -77
  37. chuk_tool_processor/models/tool_spec.py +350 -0
  38. chuk_tool_processor/models/validated_tool.py +36 -18
  39. chuk_tool_processor/observability/__init__.py +30 -0
  40. chuk_tool_processor/observability/metrics.py +312 -0
  41. chuk_tool_processor/observability/setup.py +105 -0
  42. chuk_tool_processor/observability/tracing.py +345 -0
  43. chuk_tool_processor/plugins/__init__.py +1 -1
  44. chuk_tool_processor/plugins/discovery.py +11 -11
  45. chuk_tool_processor/plugins/parsers/__init__.py +1 -1
  46. chuk_tool_processor/plugins/parsers/base.py +1 -2
  47. chuk_tool_processor/plugins/parsers/function_call_tool.py +13 -8
  48. chuk_tool_processor/plugins/parsers/json_tool.py +4 -3
  49. chuk_tool_processor/plugins/parsers/openai_tool.py +12 -7
  50. chuk_tool_processor/plugins/parsers/xml_tool.py +4 -4
  51. chuk_tool_processor/registry/__init__.py +12 -12
  52. chuk_tool_processor/registry/auto_register.py +22 -30
  53. chuk_tool_processor/registry/decorators.py +127 -129
  54. chuk_tool_processor/registry/interface.py +26 -23
  55. chuk_tool_processor/registry/metadata.py +27 -22
  56. chuk_tool_processor/registry/provider.py +17 -18
  57. chuk_tool_processor/registry/providers/__init__.py +16 -19
  58. chuk_tool_processor/registry/providers/memory.py +18 -25
  59. chuk_tool_processor/registry/tool_export.py +42 -51
  60. chuk_tool_processor/utils/validation.py +15 -16
  61. chuk_tool_processor-0.9.7.dist-info/METADATA +1813 -0
  62. chuk_tool_processor-0.9.7.dist-info/RECORD +67 -0
  63. chuk_tool_processor-0.6.4.dist-info/METADATA +0 -697
  64. chuk_tool_processor-0.6.4.dist-info/RECORD +0 -60
  65. {chuk_tool_processor-0.6.4.dist-info → chuk_tool_processor-0.9.7.dist-info}/WHEEL +0 -0
  66. {chuk_tool_processor-0.6.4.dist-info → chuk_tool_processor-0.9.7.dist-info}/top_level.txt +0 -0
@@ -8,103 +8,101 @@ them appropriately.
8
8
  """
9
9
 
10
10
  import asyncio
11
+ import atexit
11
12
  import functools
12
13
  import inspect
13
14
  import sys
14
- import weakref
15
- import atexit
16
15
  import warnings
17
- from typing import Any, Callable, Dict, Optional, Type, TypeVar, cast, Set, List, Awaitable
16
+ import weakref
17
+ from collections.abc import Awaitable, Callable
18
+ from typing import TypeVar
18
19
 
19
20
  from chuk_tool_processor.registry.provider import ToolRegistryProvider
20
21
 
21
- T = TypeVar('T')
22
+ T = TypeVar("T")
22
23
 
23
24
  # Global tracking of classes to be registered
24
- _PENDING_REGISTRATIONS: List[Callable[[], Awaitable]] = []
25
+ _PENDING_REGISTRATIONS: list[Callable[[], Awaitable]] = []
25
26
  _REGISTERED_CLASSES = weakref.WeakSet()
26
27
  _SHUTTING_DOWN = False
27
28
 
28
29
 
29
- def _is_pydantic_model(cls: Type) -> bool:
30
+ def _is_pydantic_model(cls: type) -> bool:
30
31
  """Check if a class is a Pydantic model."""
31
32
  try:
32
33
  # Check for Pydantic v2
33
- return hasattr(cls, 'model_fields') or hasattr(cls, '__pydantic_core_schema__')
34
- except:
34
+ return hasattr(cls, "model_fields") or hasattr(cls, "__pydantic_core_schema__")
35
+ except Exception:
35
36
  try:
36
37
  # Check for Pydantic v1
37
- return hasattr(cls, '__fields__')
38
- except:
38
+ return hasattr(cls, "__fields__")
39
+ except Exception:
39
40
  return False
40
41
 
41
42
 
42
- def _add_subprocess_serialization_support(cls: Type, tool_name: str) -> Type:
43
+ def _add_subprocess_serialization_support(cls: type, tool_name: str) -> type:
43
44
  """
44
45
  Add subprocess serialization support to a tool class.
45
-
46
+
46
47
  FIXED: Now properly handles Pydantic models by using class attributes
47
48
  instead of instance attributes where necessary.
48
49
  """
49
50
  # Store the tool name for serialization at class level
50
51
  cls._tool_name = tool_name
51
-
52
+
52
53
  # Check if this is a Pydantic model
53
54
  is_pydantic = _is_pydantic_model(cls)
54
-
55
+
55
56
  # Check if the class already has custom serialization methods
56
- has_custom_getstate = '__getstate__' in cls.__dict__ and callable(cls.__dict__['__getstate__'])
57
- has_custom_setstate = '__setstate__' in cls.__dict__ and callable(cls.__dict__['__setstate__'])
58
-
57
+ has_custom_getstate = "__getstate__" in cls.__dict__ and callable(cls.__dict__["__getstate__"])
58
+ has_custom_setstate = "__setstate__" in cls.__dict__ and callable(cls.__dict__["__setstate__"])
59
+
59
60
  if has_custom_getstate and has_custom_setstate:
60
61
  # Class already has both custom serialization methods
61
62
  original_getstate = cls.__getstate__
62
63
  original_setstate = cls.__setstate__
63
-
64
+
64
65
  def enhanced_getstate(self):
65
66
  """Enhanced __getstate__ that ensures tool_name is included."""
66
67
  state = original_getstate(self)
67
68
  if isinstance(state, dict):
68
- state['tool_name'] = getattr(self, 'tool_name', tool_name)
69
+ state["tool_name"] = getattr(self, "tool_name", tool_name)
69
70
  return state
70
71
  else:
71
- return {
72
- '_custom_state': state,
73
- 'tool_name': getattr(self, 'tool_name', tool_name)
74
- }
75
-
72
+ return {"_custom_state": state, "tool_name": getattr(self, "tool_name", tool_name)}
73
+
76
74
  def enhanced_setstate(self, state):
77
75
  """Enhanced __setstate__ that handles tool_name."""
78
- if isinstance(state, dict) and '_custom_state' in state:
76
+ if isinstance(state, dict) and "_custom_state" in state:
79
77
  # FIXED: For Pydantic models, set as class attribute
80
78
  if is_pydantic:
81
- self.__class__._tool_name = state.get('tool_name', tool_name)
79
+ self.__class__._tool_name = state.get("tool_name", tool_name)
82
80
  else:
83
- self.tool_name = state.get('tool_name', tool_name)
84
- original_setstate(self, state['_custom_state'])
81
+ self.tool_name = state.get("tool_name", tool_name)
82
+ original_setstate(self, state["_custom_state"])
85
83
  else:
86
84
  if isinstance(state, dict):
87
85
  if is_pydantic:
88
- self.__class__._tool_name = state.get('tool_name', tool_name)
86
+ self.__class__._tool_name = state.get("tool_name", tool_name)
89
87
  else:
90
- self.tool_name = state.get('tool_name', tool_name)
88
+ self.tool_name = state.get("tool_name", tool_name)
91
89
  original_setstate(self, state)
92
-
90
+
93
91
  cls.__getstate__ = enhanced_getstate
94
92
  cls.__setstate__ = enhanced_setstate
95
-
93
+
96
94
  elif not has_custom_getstate and not has_custom_setstate:
97
95
  # No custom serialization methods - add default implementation
98
-
96
+
99
97
  if is_pydantic:
100
98
  # FIXED: Special handling for Pydantic models
101
99
  def __getstate__(self):
102
100
  """Pydantic-compatible serialization method."""
103
101
  try:
104
102
  # Try Pydantic v2 first
105
- if hasattr(self, 'model_dump'):
103
+ if hasattr(self, "model_dump"):
106
104
  state = self.model_dump()
107
- elif hasattr(self, 'dict'):
105
+ elif hasattr(self, "dict"):
108
106
  # Pydantic v1
109
107
  state = self.dict()
110
108
  else:
@@ -113,257 +111,257 @@ def _add_subprocess_serialization_support(cls: Type, tool_name: str) -> Type:
113
111
  except Exception:
114
112
  # Fallback to __dict__
115
113
  state = self.__dict__.copy()
116
-
114
+
117
115
  # Always include tool_name
118
- state['tool_name'] = getattr(self, 'tool_name', getattr(self.__class__, '_tool_name', tool_name))
116
+ state["tool_name"] = getattr(self, "tool_name", getattr(self.__class__, "_tool_name", tool_name))
119
117
  return state
120
-
118
+
121
119
  def __setstate__(self, state):
122
120
  """Pydantic-compatible deserialization method."""
123
121
  if isinstance(state, dict):
124
122
  # Extract tool_name and store at class level for Pydantic
125
- tool_name_value = state.get('tool_name', tool_name)
123
+ tool_name_value = state.get("tool_name", tool_name)
126
124
  self.__class__._tool_name = tool_name_value
127
-
125
+
128
126
  # For Pydantic models, we need to be careful about restoration
129
127
  try:
130
128
  # Remove tool_name from state since it's not a Pydantic field
131
129
  state_copy = state.copy()
132
- state_copy.pop('tool_name', None)
133
-
130
+ state_copy.pop("tool_name", None)
131
+
134
132
  # Update the object's fields
135
- if hasattr(self, '__dict__'):
133
+ if hasattr(self, "__dict__"):
136
134
  self.__dict__.update(state_copy)
137
135
  except Exception:
138
136
  # Fallback - just update __dict__
139
- if hasattr(self, '__dict__'):
137
+ if hasattr(self, "__dict__"):
140
138
  self.__dict__.update(state)
141
139
  else:
142
140
  # Non-dict state
143
141
  self.__class__._tool_name = tool_name
144
-
142
+
145
143
  else:
146
144
  # Regular class handling (same as before)
147
145
  def __getstate__(self):
148
146
  """Default serialization method for subprocess execution."""
149
147
  state = self.__dict__.copy()
150
- state['tool_name'] = getattr(self, 'tool_name', tool_name)
151
-
148
+ state["tool_name"] = getattr(self, "tool_name", tool_name)
149
+
152
150
  # Remove non-serializable attributes
153
151
  non_serializable_attrs = []
154
152
  for key, value in list(state.items()):
155
- if key == 'tool_name':
153
+ if key == "tool_name":
156
154
  continue
157
155
  try:
158
156
  import pickle
157
+
159
158
  pickle.dumps(value)
160
159
  except (TypeError, AttributeError, pickle.PicklingError):
161
160
  non_serializable_attrs.append(key)
162
-
161
+
163
162
  for key in non_serializable_attrs:
164
163
  if key in state:
165
164
  del state[key]
166
-
165
+
167
166
  return state
168
-
167
+
169
168
  def __setstate__(self, state):
170
169
  """Default deserialization method for subprocess execution."""
171
170
  if isinstance(state, dict):
172
171
  self.__dict__.update(state)
173
- if not hasattr(self, 'tool_name') or not self.tool_name:
174
- self.tool_name = state.get('tool_name', tool_name)
172
+ if not hasattr(self, "tool_name") or not self.tool_name:
173
+ self.tool_name = state.get("tool_name", tool_name)
175
174
  else:
176
175
  self.tool_name = tool_name
177
-
176
+
178
177
  cls.__getstate__ = __getstate__
179
178
  cls.__setstate__ = __setstate__
180
-
179
+
181
180
  # FIXED: Enhanced __init__ wrapper that handles Pydantic models
182
- if hasattr(cls, '__init__'):
181
+ if hasattr(cls, "__init__"):
183
182
  original_init = cls.__init__
184
-
183
+
185
184
  @functools.wraps(original_init)
186
185
  def enhanced_init(self, *args, **kwargs):
187
186
  # Call original __init__
188
187
  original_init(self, *args, **kwargs)
189
-
188
+
190
189
  # FIXED: Handle tool_name setting based on model type
191
190
  if is_pydantic:
192
191
  # For Pydantic models, store at class level and add property
193
192
  self.__class__._tool_name = tool_name
194
-
193
+
195
194
  # Add a property to access tool_name if it doesn't exist
196
- if not hasattr(self.__class__, 'tool_name'):
195
+ if not hasattr(self.__class__, "tool_name"):
196
+
197
197
  def tool_name_property(self):
198
- return getattr(self.__class__, '_tool_name', tool_name)
199
-
198
+ return getattr(self.__class__, "_tool_name", tool_name)
199
+
200
200
  # Add as a property
201
- setattr(self.__class__, 'tool_name', property(tool_name_property))
201
+ self.__class__.tool_name = property(tool_name_property)
202
202
  else:
203
203
  # For regular classes, set as instance attribute
204
- if not hasattr(self, 'tool_name'):
204
+ if not hasattr(self, "tool_name"):
205
205
  self.tool_name = tool_name
206
-
206
+
207
207
  cls.__init__ = enhanced_init
208
208
  else:
209
209
  # FIXED: Add appropriate __init__ based on model type
210
210
  if is_pydantic:
211
+
211
212
  def __init__(self, *args, **kwargs):
212
213
  super(cls, self).__init__(*args, **kwargs)
213
214
  self.__class__._tool_name = tool_name
214
215
  else:
216
+
215
217
  def __init__(self):
216
218
  self.tool_name = tool_name
217
-
219
+
218
220
  cls.__init__ = __init__
219
-
221
+
220
222
  # FIXED: Add tool_name property for Pydantic models
221
- if is_pydantic and not hasattr(cls, 'tool_name'):
223
+ if is_pydantic and not hasattr(cls, "tool_name"):
224
+
222
225
  def tool_name_property(self):
223
- return getattr(self.__class__, '_tool_name', tool_name)
224
-
226
+ return getattr(self.__class__, "_tool_name", tool_name)
227
+
225
228
  # Add as a property so it can be accessed but not set directly
226
- setattr(cls, 'tool_name', property(tool_name_property))
227
-
229
+ cls.tool_name = property(tool_name_property)
230
+
228
231
  return cls
229
232
 
230
233
 
231
- def register_tool(name: Optional[str] = None, namespace: str = "default", **metadata):
234
+ def register_tool(name: str | None = None, namespace: str = "default", **metadata):
232
235
  """
233
236
  Decorator for registering tools with the global registry.
234
-
237
+
235
238
  FIXED: Now properly handles Pydantic models (like StreamingTool).
236
-
239
+
237
240
  This decorator automatically adds subprocess serialization support,
238
241
  making tools compatible with both InProcessStrategy and SubprocessStrategy.
239
242
  """
240
- def decorator(cls: Type[T]) -> Type[T]:
243
+
244
+ def decorator(cls: type[T]) -> type[T]:
241
245
  # Skip if already registered
242
246
  if cls in _REGISTERED_CLASSES:
243
247
  return cls
244
-
248
+
245
249
  # Skip if shutting down
246
250
  if _SHUTTING_DOWN:
247
251
  return cls
248
252
 
249
253
  # Ensure execute method is async
250
- if hasattr(cls, 'execute') and not inspect.iscoroutinefunction(cls.execute):
254
+ if hasattr(cls, "execute") and not inspect.iscoroutinefunction(cls.execute):
251
255
  raise TypeError(f"Tool {cls.__name__} must have an async execute method")
252
-
256
+
253
257
  # Determine the tool name
254
258
  tool_name = name or cls.__name__
255
-
259
+
256
260
  # FIXED: Add subprocess serialization support with Pydantic handling
257
261
  enhanced_cls = _add_subprocess_serialization_support(cls, tool_name)
258
-
262
+
259
263
  # Create registration function
260
264
  async def do_register():
261
265
  registry = await ToolRegistryProvider.get_registry()
262
- await registry.register_tool(
263
- enhanced_cls,
264
- name=tool_name,
265
- namespace=namespace,
266
- metadata=metadata
267
- )
268
-
266
+ await registry.register_tool(enhanced_cls, name=tool_name, namespace=namespace, metadata=metadata)
267
+
269
268
  _PENDING_REGISTRATIONS.append(do_register)
270
269
  _REGISTERED_CLASSES.add(enhanced_cls)
271
-
270
+
272
271
  # Add class attribute for identification
273
- enhanced_cls._tool_registration_info = {
274
- 'name': tool_name,
275
- 'namespace': namespace,
276
- 'metadata': metadata
277
- }
278
-
272
+ enhanced_cls._tool_registration_info = {"name": tool_name, "namespace": namespace, "metadata": metadata}
273
+
279
274
  return enhanced_cls
280
-
275
+
281
276
  return decorator
282
277
 
283
278
 
284
279
  # Alternative approach: A helper function for Pydantic compatibility
285
- def make_pydantic_tool_compatible(cls: Type, tool_name: str) -> Type:
280
+ def make_pydantic_tool_compatible(cls: type, tool_name: str) -> type:
286
281
  """
287
282
  Alternative helper function to make Pydantic tools subprocess-compatible.
288
-
283
+
289
284
  This can be used as a manual alternative if the decorator approach
290
285
  doesn't work for your specific use case.
291
286
  """
292
287
  # Store tool name at class level
293
288
  cls._tool_name = tool_name
294
-
289
+
295
290
  # Add property access
296
- if not hasattr(cls, 'tool_name'):
291
+ if not hasattr(cls, "tool_name"):
292
+
297
293
  def tool_name_getter(self):
298
- return getattr(self.__class__, '_tool_name', tool_name)
299
-
300
- setattr(cls, 'tool_name', property(tool_name_getter))
301
-
294
+ return getattr(self.__class__, "_tool_name", tool_name)
295
+
296
+ cls.tool_name = property(tool_name_getter)
297
+
302
298
  # Add serialization methods
303
- if not hasattr(cls, '__getstate__'):
299
+ if not hasattr(cls, "__getstate__"):
300
+
304
301
  def __getstate__(self):
305
302
  try:
306
- if hasattr(self, 'model_dump'):
303
+ if hasattr(self, "model_dump"):
307
304
  state = self.model_dump()
308
- elif hasattr(self, 'dict'):
305
+ elif hasattr(self, "dict"):
309
306
  state = self.dict()
310
307
  else:
311
308
  state = self.__dict__.copy()
312
- except:
309
+ except Exception:
313
310
  state = self.__dict__.copy()
314
-
315
- state['tool_name'] = getattr(self.__class__, '_tool_name', tool_name)
311
+
312
+ state["tool_name"] = getattr(self.__class__, "_tool_name", tool_name)
316
313
  return state
317
-
314
+
318
315
  cls.__getstate__ = __getstate__
319
-
320
- if not hasattr(cls, '__setstate__'):
316
+
317
+ if not hasattr(cls, "__setstate__"):
318
+
321
319
  def __setstate__(self, state):
322
320
  if isinstance(state, dict):
323
- self.__class__._tool_name = state.get('tool_name', tool_name)
321
+ self.__class__._tool_name = state.get("tool_name", tool_name)
324
322
  state_copy = state.copy()
325
- state_copy.pop('tool_name', None)
326
- if hasattr(self, '__dict__'):
323
+ state_copy.pop("tool_name", None)
324
+ if hasattr(self, "__dict__"):
327
325
  self.__dict__.update(state_copy)
328
-
326
+
329
327
  cls.__setstate__ = __setstate__
330
-
328
+
331
329
  return cls
332
330
 
333
331
 
334
332
  async def ensure_registrations() -> None:
335
333
  """Process all pending tool registrations."""
336
334
  global _PENDING_REGISTRATIONS
337
-
335
+
338
336
  if not _PENDING_REGISTRATIONS:
339
337
  return
340
-
338
+
341
339
  tasks = []
342
340
  for registration_fn in _PENDING_REGISTRATIONS:
343
341
  tasks.append(asyncio.create_task(registration_fn()))
344
-
342
+
345
343
  _PENDING_REGISTRATIONS.clear()
346
-
344
+
347
345
  if tasks:
348
346
  await asyncio.gather(*tasks)
349
347
 
350
348
 
351
- def discover_decorated_tools() -> List[Type]:
349
+ def discover_decorated_tools() -> list[type]:
352
350
  """Discover all tool classes decorated with @register_tool."""
353
351
  tools = []
354
-
352
+
355
353
  for module_name, module in list(sys.modules.items()):
356
- if not module_name.startswith('chuk_tool_processor'):
354
+ if not module_name.startswith("chuk_tool_processor"):
357
355
  continue
358
-
356
+
359
357
  for attr_name in dir(module):
360
358
  try:
361
359
  attr = getattr(module, attr_name)
362
- if hasattr(attr, '_tool_registration_info'):
360
+ if hasattr(attr, "_tool_registration_info"):
363
361
  tools.append(attr)
364
362
  except (AttributeError, ImportError):
365
363
  pass
366
-
364
+
367
365
  return tools
368
366
 
369
367
 
@@ -376,4 +374,4 @@ def _handle_shutdown():
376
374
 
377
375
 
378
376
  atexit.register(_handle_shutdown)
379
- warnings.filterwarnings("ignore", message="coroutine.*was never awaited")
377
+ warnings.filterwarnings("ignore", message="coroutine.*was never awaited")
@@ -2,14 +2,16 @@
2
2
  """
3
3
  Defines the interface for asynchronous tool registries.
4
4
  """
5
+
5
6
  from __future__ import annotations
6
7
 
7
- from typing import Protocol, Any, Dict, List, Optional, Tuple, TypeVar, runtime_checkable
8
+ from typing import Any, Protocol, TypeVar, runtime_checkable
8
9
 
9
10
  # imports
10
11
  from chuk_tool_processor.registry.metadata import ToolMetadata
11
12
 
12
- T = TypeVar('T')
13
+ T = TypeVar("T")
14
+
13
15
 
14
16
  @runtime_checkable
15
17
  class ToolRegistryInterface(Protocol):
@@ -17,12 +19,13 @@ class ToolRegistryInterface(Protocol):
17
19
  Protocol for an async tool registry. Implementations should allow registering tools
18
20
  and retrieving them by name and namespace.
19
21
  """
22
+
20
23
  async def register_tool(
21
- self,
22
- tool: Any,
23
- name: Optional[str] = None,
24
+ self,
25
+ tool: Any,
26
+ name: str | None = None,
24
27
  namespace: str = "default",
25
- metadata: Optional[Dict[str, Any]] = None
28
+ metadata: dict[str, Any] | None = None,
26
29
  ) -> None:
27
30
  """
28
31
  Register a tool implementation asynchronously.
@@ -35,14 +38,14 @@ class ToolRegistryInterface(Protocol):
35
38
  """
36
39
  ...
37
40
 
38
- async def get_tool(self, name: str, namespace: str = "default") -> Optional[Any]:
41
+ async def get_tool(self, name: str, namespace: str = "default") -> Any | None:
39
42
  """
40
43
  Retrieve a registered tool by name and namespace asynchronously.
41
-
44
+
42
45
  Args:
43
46
  name: The name of the tool.
44
47
  namespace: The namespace of the tool (default: "default").
45
-
48
+
46
49
  Returns:
47
50
  The tool implementation or None if not found.
48
51
  """
@@ -51,54 +54,54 @@ class ToolRegistryInterface(Protocol):
51
54
  async def get_tool_strict(self, name: str, namespace: str = "default") -> Any:
52
55
  """
53
56
  Retrieve a registered tool by name and namespace, raising if not found.
54
-
57
+
55
58
  Args:
56
59
  name: The name of the tool.
57
60
  namespace: The namespace of the tool (default: "default").
58
-
61
+
59
62
  Returns:
60
63
  The tool implementation.
61
-
64
+
62
65
  Raises:
63
66
  ToolNotFoundError: If the tool is not found in the registry.
64
67
  """
65
68
  ...
66
69
 
67
- async def get_metadata(self, name: str, namespace: str = "default") -> Optional[ToolMetadata]:
70
+ async def get_metadata(self, name: str, namespace: str = "default") -> ToolMetadata | None:
68
71
  """
69
72
  Retrieve metadata for a registered tool asynchronously.
70
-
73
+
71
74
  Args:
72
75
  name: The name of the tool.
73
76
  namespace: The namespace of the tool (default: "default").
74
-
77
+
75
78
  Returns:
76
79
  ToolMetadata if found, None otherwise.
77
80
  """
78
81
  ...
79
82
 
80
- async def list_tools(self, namespace: Optional[str] = None) -> List[Tuple[str, str]]:
83
+ async def list_tools(self, namespace: str | None = None) -> list[tuple[str, str]]:
81
84
  """
82
85
  List all registered tool names asynchronously, optionally filtered by namespace.
83
-
86
+
84
87
  Args:
85
88
  namespace: Optional namespace filter.
86
-
89
+
87
90
  Returns:
88
91
  List of (namespace, name) tuples.
89
92
  """
90
93
  ...
91
94
 
92
- async def list_namespaces(self) -> List[str]:
95
+ async def list_namespaces(self) -> list[str]:
93
96
  """
94
97
  List all registered namespaces asynchronously.
95
-
98
+
96
99
  Returns:
97
100
  List of namespace names.
98
101
  """
99
102
  ...
100
-
101
- async def list_metadata(self, namespace: Optional[str] = None) -> List[ToolMetadata]:
103
+
104
+ async def list_metadata(self, namespace: str | None = None) -> list[ToolMetadata]:
102
105
  """
103
106
  Return all ToolMetadata objects asynchronously.
104
107
 
@@ -110,4 +113,4 @@ class ToolRegistryInterface(Protocol):
110
113
  Returns:
111
114
  List of ToolMetadata objects.
112
115
  """
113
- ...
116
+ ...