chuk-tool-processor 0.1.6__py3-none-any.whl → 0.1.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 (45) hide show
  1. chuk_tool_processor/core/processor.py +345 -132
  2. chuk_tool_processor/execution/strategies/inprocess_strategy.py +512 -68
  3. chuk_tool_processor/execution/strategies/subprocess_strategy.py +523 -63
  4. chuk_tool_processor/execution/tool_executor.py +282 -24
  5. chuk_tool_processor/execution/wrappers/caching.py +465 -123
  6. chuk_tool_processor/execution/wrappers/rate_limiting.py +199 -86
  7. chuk_tool_processor/execution/wrappers/retry.py +133 -23
  8. chuk_tool_processor/logging/__init__.py +83 -10
  9. chuk_tool_processor/logging/context.py +218 -22
  10. chuk_tool_processor/logging/formatter.py +56 -13
  11. chuk_tool_processor/logging/helpers.py +91 -16
  12. chuk_tool_processor/logging/metrics.py +75 -6
  13. chuk_tool_processor/mcp/mcp_tool.py +80 -35
  14. chuk_tool_processor/mcp/register_mcp_tools.py +74 -56
  15. chuk_tool_processor/mcp/setup_mcp_sse.py +41 -36
  16. chuk_tool_processor/mcp/setup_mcp_stdio.py +39 -37
  17. chuk_tool_processor/models/execution_strategy.py +52 -3
  18. chuk_tool_processor/models/streaming_tool.py +110 -0
  19. chuk_tool_processor/models/tool_call.py +56 -4
  20. chuk_tool_processor/models/tool_result.py +115 -9
  21. chuk_tool_processor/models/validated_tool.py +15 -13
  22. chuk_tool_processor/plugins/discovery.py +115 -70
  23. chuk_tool_processor/plugins/parsers/base.py +13 -5
  24. chuk_tool_processor/plugins/parsers/{function_call_tool_plugin.py → function_call_tool.py} +39 -20
  25. chuk_tool_processor/plugins/parsers/json_tool.py +50 -0
  26. chuk_tool_processor/plugins/parsers/openai_tool.py +88 -0
  27. chuk_tool_processor/plugins/parsers/xml_tool.py +74 -20
  28. chuk_tool_processor/registry/__init__.py +46 -7
  29. chuk_tool_processor/registry/auto_register.py +92 -28
  30. chuk_tool_processor/registry/decorators.py +134 -11
  31. chuk_tool_processor/registry/interface.py +48 -14
  32. chuk_tool_processor/registry/metadata.py +52 -6
  33. chuk_tool_processor/registry/provider.py +75 -36
  34. chuk_tool_processor/registry/providers/__init__.py +49 -10
  35. chuk_tool_processor/registry/providers/memory.py +59 -48
  36. chuk_tool_processor/registry/tool_export.py +208 -39
  37. chuk_tool_processor/utils/validation.py +18 -13
  38. chuk_tool_processor-0.1.7.dist-info/METADATA +401 -0
  39. chuk_tool_processor-0.1.7.dist-info/RECORD +58 -0
  40. {chuk_tool_processor-0.1.6.dist-info → chuk_tool_processor-0.1.7.dist-info}/WHEEL +1 -1
  41. chuk_tool_processor/plugins/parsers/json_tool_plugin.py +0 -38
  42. chuk_tool_processor/plugins/parsers/openai_tool_plugin.py +0 -76
  43. chuk_tool_processor-0.1.6.dist-info/METADATA +0 -462
  44. chuk_tool_processor-0.1.6.dist-info/RECORD +0 -57
  45. {chuk_tool_processor-0.1.6.dist-info → chuk_tool_processor-0.1.7.dist-info}/top_level.txt +0 -0
@@ -1,21 +1,60 @@
1
+ # chuk_tool_processor/registry/__init__.py
1
2
  """
2
- Tool registry package for managing and accessing tool implementations.
3
+ Async-native tool registry package for managing and accessing tool implementations.
3
4
  """
4
5
 
6
+ import asyncio
7
+ from typing import Optional
8
+
5
9
  from chuk_tool_processor.registry.interface import ToolRegistryInterface
6
- from chuk_tool_processor.registry.metadata import ToolMetadata
7
- from chuk_tool_processor.registry.provider import ToolRegistryProvider
8
- from chuk_tool_processor.registry.decorators import register_tool
10
+ from chuk_tool_processor.registry.metadata import ToolMetadata, StreamingToolMetadata
11
+ from chuk_tool_processor.registry.provider import ToolRegistryProvider, get_registry
12
+ from chuk_tool_processor.registry.decorators import register_tool, ensure_registrations, discover_decorated_tools
9
13
 
10
14
  # --------------------------------------------------------------------------- #
11
- # Expose the *singleton* registry that every part of the library should use
15
+ # The default_registry is now an async function instead of direct property access
12
16
  # --------------------------------------------------------------------------- #
13
- default_registry = ToolRegistryProvider.get_registry()
17
+ async def get_default_registry() -> ToolRegistryInterface:
18
+ """
19
+ Get the default registry instance.
20
+
21
+ This is a convenience function that calls ToolRegistryProvider.get_registry()
22
+
23
+ Returns:
24
+ The default tool registry
25
+ """
26
+ return await ToolRegistryProvider.get_registry()
14
27
 
15
28
  __all__ = [
16
29
  "ToolRegistryInterface",
17
30
  "ToolMetadata",
31
+ "StreamingToolMetadata",
18
32
  "ToolRegistryProvider",
19
33
  "register_tool",
20
- "default_registry",
34
+ "ensure_registrations",
35
+ "discover_decorated_tools",
36
+ "get_default_registry",
37
+ "get_registry",
21
38
  ]
39
+
40
+ # --------------------------------------------------------------------------- #
41
+ # Initialization helper that should be called at application startup
42
+ # --------------------------------------------------------------------------- #
43
+ async def initialize():
44
+ """
45
+ Initialize the registry system.
46
+
47
+ This function should be called during application startup to:
48
+ 1. Ensure the registry is created
49
+ 2. Register all tools decorated with @register_tool
50
+
51
+ Returns:
52
+ The initialized registry
53
+ """
54
+ # Initialize registry
55
+ registry = await get_default_registry()
56
+
57
+ # Process all pending tool registrations
58
+ await ensure_registrations()
59
+
60
+ return registry
@@ -1,11 +1,12 @@
1
1
  # chuk_tool_processor/registry/auto_register.py
2
2
  """
3
- Tiny auto-register helpers so you can do
3
+ Async auto-register helpers for registering functions and LangChain tools.
4
4
 
5
- register_fn_tool(my_function)
6
- register_langchain_tool(my_langchain_tool)
5
+ Usage:
6
+ await register_fn_tool(my_function)
7
+ await register_langchain_tool(my_langchain_tool)
7
8
 
8
- and they immediately show up in the global registry.
9
+ These tools will immediately show up in the global registry.
9
10
  """
10
11
 
11
12
  from __future__ import annotations
@@ -13,7 +14,7 @@ from __future__ import annotations
13
14
  import asyncio
14
15
  import inspect
15
16
  import types
16
- from typing import Callable, ForwardRef, Type, get_type_hints
17
+ from typing import Callable, ForwardRef, Type, get_type_hints, Any, Optional, Dict, Union
17
18
 
18
19
  import anyio
19
20
  from pydantic import BaseModel, create_model
@@ -23,7 +24,9 @@ try: # optional dependency
23
24
  except ModuleNotFoundError: # pragma: no cover
24
25
  BaseTool = None # noqa: N816 – keep the name for isinstance() checks
25
26
 
26
- from chuk_tool_processor.registry.decorators import register_tool
27
+ # registry
28
+ from .decorators import register_tool
29
+ from .provider import ToolRegistryProvider
27
30
 
28
31
 
29
32
  # ────────────────────────────────────────────────────────────────────────────
@@ -37,7 +40,7 @@ def _auto_schema(func: Callable) -> Type[BaseModel]:
37
40
 
38
41
  *Unknown* or *un-imported* annotations (common with third-party libs that
39
42
  use forward-refs without importing the target – e.g. ``uuid.UUID`` in
40
- LangChains `CallbackManagerForToolRun`) default to ``str`` instead of
43
+ LangChain's `CallbackManagerForToolRun`) default to ``str`` instead of
41
44
  crashing `get_type_hints()`.
42
45
  """
43
46
  try:
@@ -49,14 +52,14 @@ def _auto_schema(func: Callable) -> Type[BaseModel]:
49
52
  for param in inspect.signature(func).parameters.values():
50
53
  raw_hint = hints.get(param.name, param.annotation)
51
54
  # Default to ``str`` for ForwardRef / string annotations or if we
52
- # couldnt resolve the type.
55
+ # couldn't resolve the type.
53
56
  hint: type = (
54
57
  raw_hint
55
58
  if raw_hint not in (inspect._empty, None, str)
56
59
  and not isinstance(raw_hint, (str, ForwardRef))
57
60
  else str
58
61
  )
59
- fields[param.name] = (hint, ...) # “...” → required
62
+ fields[param.name] = (hint, ...) # "..." → required
60
63
 
61
64
  return create_model(f"{func.__name__.title()}Args", **fields) # type: ignore
62
65
 
@@ -66,25 +69,54 @@ def _auto_schema(func: Callable) -> Type[BaseModel]:
66
69
  # ────────────────────────────────────────────────────────────────────────────
67
70
 
68
71
 
69
- def register_fn_tool(
72
+ async def register_fn_tool(
70
73
  func: Callable,
71
74
  *,
72
75
  name: str | None = None,
73
76
  description: str | None = None,
77
+ namespace: str = "default",
74
78
  ) -> None:
75
- """Register a plain function as a tool – one line is all you need."""
76
-
79
+ """
80
+ Register a plain function as a tool asynchronously.
81
+
82
+ Args:
83
+ func: The function to register (can be sync or async)
84
+ name: Optional name for the tool (defaults to function name)
85
+ description: Optional description (defaults to function docstring)
86
+ namespace: Registry namespace (defaults to "default")
87
+ """
77
88
  schema = _auto_schema(func)
78
- name = name or func.__name__
79
- description = (description or func.__doc__ or "").strip()
80
-
81
- @register_tool(name=name, description=description, arg_schema=schema)
89
+ tool_name = name or func.__name__
90
+ tool_description = (description or func.__doc__ or "").strip()
91
+
92
+ # Create the tool wrapper class
82
93
  class _Tool: # noqa: D401, N801 – internal auto-wrapper
83
- async def _execute(self, **kwargs):
94
+ """Auto-generated tool wrapper for function."""
95
+
96
+ async def execute(self, **kwargs: Any) -> Any:
97
+ """Execute the wrapped function."""
84
98
  if inspect.iscoroutinefunction(func):
85
99
  return await func(**kwargs)
86
100
  # off-load blocking sync work
87
101
  return await anyio.to_thread.run_sync(func, **kwargs)
102
+
103
+ # Set the docstring
104
+ _Tool.__doc__ = tool_description
105
+
106
+ # Get the registry and register directly
107
+ registry = await ToolRegistryProvider.get_registry()
108
+ await registry.register_tool(
109
+ _Tool(),
110
+ name=tool_name,
111
+ namespace=namespace,
112
+ metadata={
113
+ "description": tool_description,
114
+ "is_async": True,
115
+ "argument_schema": schema.model_json_schema(),
116
+ "source": "function",
117
+ "source_name": func.__qualname__,
118
+ }
119
+ )
88
120
 
89
121
 
90
122
  # ────────────────────────────────────────────────────────────────────────────
@@ -92,18 +124,27 @@ def register_fn_tool(
92
124
  # ────────────────────────────────────────────────────────────────────────────
93
125
 
94
126
 
95
- def register_langchain_tool(
96
- tool,
127
+ async def register_langchain_tool(
128
+ tool: Any,
97
129
  *,
98
130
  name: str | None = None,
99
131
  description: str | None = None,
132
+ namespace: str = "default",
100
133
  ) -> None:
101
134
  """
102
- Register a **LangChain** `BaseTool` instance (or anything exposing
103
- ``.run`` / ``.arun``).
104
-
105
- If LangChain isn’t installed you’ll get a clear error instead of an import
106
- failure deep in the stack.
135
+ Register a **LangChain** `BaseTool` instance asynchronously.
136
+
137
+ Works with any object exposing `.run` / `.arun` methods.
138
+
139
+ Args:
140
+ tool: The LangChain tool to register
141
+ name: Optional name for the tool (defaults to tool.name)
142
+ description: Optional description (defaults to tool.description)
143
+ namespace: Registry namespace (defaults to "default")
144
+
145
+ Raises:
146
+ RuntimeError: If LangChain isn't installed
147
+ TypeError: If the object isn't a LangChain BaseTool
107
148
  """
108
149
  if BaseTool is None:
109
150
  raise RuntimeError(
@@ -117,9 +158,32 @@ def register_langchain_tool(
117
158
  f"{type(tool).__name__}"
118
159
  )
119
160
 
120
- fn = tool.arun if hasattr(tool, "arun") else tool.run # prefer async
121
- register_fn_tool(
161
+ # Prefer async implementation if available
162
+ fn = tool.arun if hasattr(tool, "arun") else tool.run
163
+
164
+ tool_name = name or tool.name or tool.__class__.__name__
165
+ tool_description = description or tool.description or (tool.__doc__ or "")
166
+
167
+ await register_fn_tool(
122
168
  fn,
123
- name=name or tool.name or tool.__class__.__name__,
124
- description=description or tool.description or (tool.__doc__ or ""),
169
+ name=tool_name,
170
+ description=tool_description,
171
+ namespace=namespace,
125
172
  )
173
+
174
+ # Update the metadata to include LangChain info
175
+ registry = await ToolRegistryProvider.get_registry()
176
+ metadata = await registry.get_metadata(tool_name, namespace)
177
+
178
+ if metadata:
179
+ updated_metadata = metadata.model_copy()
180
+ # Update source info
181
+ updated_metadata.tags.add("langchain")
182
+
183
+ # Re-register with updated metadata
184
+ await registry.register_tool(
185
+ await registry.get_tool(tool_name, namespace),
186
+ name=tool_name,
187
+ namespace=namespace,
188
+ metadata=updated_metadata.model_dump(),
189
+ )
@@ -1,24 +1,42 @@
1
1
  # chuk_tool_processor/registry/decorators.py
2
2
  """
3
- Decorators for registering tools with the registry.
3
+ Decorators for registering tools with the registry asynchronously.
4
4
  """
5
5
 
6
- from functools import wraps
7
- from typing import Any, Callable, Dict, Optional, Type, TypeVar
6
+ import asyncio
7
+ import functools
8
+ import inspect
9
+ import sys
10
+ import weakref
11
+ import atexit
12
+ import warnings
13
+ from typing import Any, Callable, Dict, Optional, Type, TypeVar, cast, Set, List, Awaitable
8
14
 
9
15
  from chuk_tool_processor.registry.provider import ToolRegistryProvider
10
16
 
11
17
  T = TypeVar('T')
12
18
 
19
+ # Global tracking of classes to be registered
20
+ # Store coroutines rather than awaitables to avoid warnings
21
+ _PENDING_REGISTRATIONS: List[Callable[[], Awaitable]] = []
22
+ _REGISTERED_CLASSES = weakref.WeakSet()
23
+
24
+ # Keep track of whether we're shutting down
25
+ _SHUTTING_DOWN = False
26
+
13
27
 
14
28
  def register_tool(name: Optional[str] = None, namespace: str = "default", **metadata):
15
29
  """
16
30
  Decorator for registering tools with the global registry.
17
31
 
32
+ This decorator will queue the registration to happen asynchronously.
33
+ You must call `await ensure_registrations()` in your application startup
34
+ to complete all registrations.
35
+
18
36
  Example:
19
37
  @register_tool(name="my_tool", namespace="math", description="Performs math operations")
20
38
  class MyTool:
21
- def execute(self, x: int, y: int) -> int:
39
+ async def execute(self, x: int, y: int) -> int:
22
40
  return x + y
23
41
 
24
42
  Args:
@@ -30,13 +48,118 @@ def register_tool(name: Optional[str] = None, namespace: str = "default", **meta
30
48
  A decorator function that registers the class with the registry.
31
49
  """
32
50
  def decorator(cls: Type[T]) -> Type[T]:
33
- registry = ToolRegistryProvider.get_registry()
34
- registry.register_tool(cls, name=name, namespace=namespace, metadata=metadata)
51
+ # Skip if already registered
52
+ if cls in _REGISTERED_CLASSES:
53
+ return cls
54
+
55
+ # Skip if shutting down
56
+ if _SHUTTING_DOWN:
57
+ return cls
58
+
59
+ # Ensure execute method is async
60
+ if hasattr(cls, 'execute') and not inspect.iscoroutinefunction(cls.execute):
61
+ raise TypeError(f"Tool {cls.__name__} must have an async execute method")
62
+
63
+ # Create registration function (not coroutine)
64
+ async def do_register():
65
+ registry = await ToolRegistryProvider.get_registry()
66
+ await registry.register_tool(
67
+ cls,
68
+ name=name,
69
+ namespace=namespace,
70
+ metadata=metadata
71
+ )
35
72
 
36
- @wraps(cls)
37
- def wrapper(*args: Any, **kwargs: Dict[str, Any]) -> T:
38
- return cls(*args, **kwargs)
73
+ # Store the function, not the coroutine
74
+ _PENDING_REGISTRATIONS.append(do_register)
75
+ _REGISTERED_CLASSES.add(cls)
76
+
77
+ # Add class attribute so we can identify decorated classes
78
+ cls._tool_registration_info = {
79
+ 'name': name or cls.__name__,
80
+ 'namespace': namespace,
81
+ 'metadata': metadata
82
+ }
83
+
84
+ # Don't modify the original class
85
+ return cls
86
+
87
+ return decorator
88
+
89
+
90
+ async def ensure_registrations() -> None:
91
+ """
92
+ Process all pending tool registrations.
93
+
94
+ This must be called during application startup to register
95
+ all tools decorated with @register_tool.
96
+
97
+ Returns:
98
+ None
99
+ """
100
+ global _PENDING_REGISTRATIONS
101
+
102
+ if not _PENDING_REGISTRATIONS:
103
+ return
39
104
 
40
- return wrapper
105
+ # Create tasks from the stored functions
106
+ tasks = []
107
+ for registration_fn in _PENDING_REGISTRATIONS:
108
+ # Now we await the function to get the coroutine, then create a task
109
+ tasks.append(asyncio.create_task(registration_fn()))
110
+
111
+ # Clear the pending list
112
+ _PENDING_REGISTRATIONS.clear()
41
113
 
42
- return decorator
114
+ # Wait for all registrations to complete
115
+ if tasks:
116
+ await asyncio.gather(*tasks)
117
+
118
+
119
+ def discover_decorated_tools() -> List[Type]:
120
+ """
121
+ Discover all tool classes decorated with @register_tool.
122
+
123
+ This can be used to inspect what tools have been registered
124
+ without awaiting ensure_registrations().
125
+
126
+ Returns:
127
+ List of tool classes that have been decorated
128
+ """
129
+ tools = []
130
+
131
+ # Search all loaded modules
132
+ for module_name, module in list(sys.modules.items()):
133
+ if not module_name.startswith('chuk_tool_processor'):
134
+ continue
135
+
136
+ for attr_name in dir(module):
137
+ try:
138
+ attr = getattr(module, attr_name)
139
+ if hasattr(attr, '_tool_registration_info'):
140
+ tools.append(attr)
141
+ except (AttributeError, ImportError):
142
+ pass
143
+
144
+ return tools
145
+
146
+
147
+ # Register atexit handler to prevent warnings at shutdown
148
+ def _handle_shutdown():
149
+ """
150
+ Handle shutdown by marking shutdown flag and clearing pending registrations.
151
+ This prevents warnings about unawaited coroutines.
152
+ """
153
+ global _SHUTTING_DOWN, _PENDING_REGISTRATIONS
154
+
155
+ # Set the shutdown flag
156
+ _SHUTTING_DOWN = True
157
+
158
+ # Clear without creating any coroutines
159
+ _PENDING_REGISTRATIONS = []
160
+
161
+ # Register the shutdown handler
162
+ atexit.register(_handle_shutdown)
163
+
164
+ # Filter the coroutine never awaited warning
165
+ warnings.filterwarnings("ignore", message="coroutine.*was never awaited")
@@ -1,19 +1,23 @@
1
1
  # chuk_tool_processor/registry/interface.py
2
2
  """
3
- Defines the interface for tool registries.
3
+ Defines the interface for asynchronous tool registries.
4
4
  """
5
- from typing import Protocol, Any, Dict, List, Optional, Tuple
5
+ from __future__ import annotations
6
6
 
7
- # imports
7
+ from typing import Protocol, Any, Dict, List, Optional, Tuple, TypeVar, runtime_checkable
8
+
9
+ # imports
8
10
  from chuk_tool_processor.registry.metadata import ToolMetadata
9
11
 
12
+ T = TypeVar('T')
10
13
 
14
+ @runtime_checkable
11
15
  class ToolRegistryInterface(Protocol):
12
16
  """
13
- Protocol for a tool registry. Implementations should allow registering tools
17
+ Protocol for an async tool registry. Implementations should allow registering tools
14
18
  and retrieving them by name and namespace.
15
19
  """
16
- def register_tool(
20
+ async def register_tool(
17
21
  self,
18
22
  tool: Any,
19
23
  name: Optional[str] = None,
@@ -21,7 +25,7 @@ class ToolRegistryInterface(Protocol):
21
25
  metadata: Optional[Dict[str, Any]] = None
22
26
  ) -> None:
23
27
  """
24
- Register a tool implementation.
28
+ Register a tool implementation asynchronously.
25
29
 
26
30
  Args:
27
31
  tool: The tool class or instance with an `execute` method.
@@ -31,9 +35,9 @@ class ToolRegistryInterface(Protocol):
31
35
  """
32
36
  ...
33
37
 
34
- def get_tool(self, name: str, namespace: str = "default") -> Optional[Any]:
38
+ async def get_tool(self, name: str, namespace: str = "default") -> Optional[Any]:
35
39
  """
36
- Retrieve a registered tool by name and namespace.
40
+ Retrieve a registered tool by name and namespace asynchronously.
37
41
 
38
42
  Args:
39
43
  name: The name of the tool.
@@ -44,9 +48,25 @@ class ToolRegistryInterface(Protocol):
44
48
  """
45
49
  ...
46
50
 
47
- def get_metadata(self, name: str, namespace: str = "default") -> Optional[ToolMetadata]:
51
+ async def get_tool_strict(self, name: str, namespace: str = "default") -> Any:
52
+ """
53
+ Retrieve a registered tool by name and namespace, raising if not found.
54
+
55
+ Args:
56
+ name: The name of the tool.
57
+ namespace: The namespace of the tool (default: "default").
58
+
59
+ Returns:
60
+ The tool implementation.
61
+
62
+ Raises:
63
+ ToolNotFoundError: If the tool is not found in the registry.
64
+ """
65
+ ...
66
+
67
+ async def get_metadata(self, name: str, namespace: str = "default") -> Optional[ToolMetadata]:
48
68
  """
49
- Retrieve metadata for a registered tool.
69
+ Retrieve metadata for a registered tool asynchronously.
50
70
 
51
71
  Args:
52
72
  name: The name of the tool.
@@ -57,9 +77,9 @@ class ToolRegistryInterface(Protocol):
57
77
  """
58
78
  ...
59
79
 
60
- def list_tools(self, namespace: Optional[str] = None) -> List[Tuple[str, str]]:
80
+ async def list_tools(self, namespace: Optional[str] = None) -> List[Tuple[str, str]]:
61
81
  """
62
- List all registered tool names, optionally filtered by namespace.
82
+ List all registered tool names asynchronously, optionally filtered by namespace.
63
83
 
64
84
  Args:
65
85
  namespace: Optional namespace filter.
@@ -69,11 +89,25 @@ class ToolRegistryInterface(Protocol):
69
89
  """
70
90
  ...
71
91
 
72
- def list_namespaces(self) -> List[str]:
92
+ async def list_namespaces(self) -> List[str]:
73
93
  """
74
- List all registered namespaces.
94
+ List all registered namespaces asynchronously.
75
95
 
76
96
  Returns:
77
97
  List of namespace names.
78
98
  """
99
+ ...
100
+
101
+ async def list_metadata(self, namespace: Optional[str] = None) -> List[ToolMetadata]:
102
+ """
103
+ Return all ToolMetadata objects asynchronously.
104
+
105
+ Args:
106
+ namespace: Optional filter by namespace.
107
+ • None (default) – metadata from all namespaces
108
+ • "some_ns" – only that namespace
109
+
110
+ Returns:
111
+ List of ToolMetadata objects.
112
+ """
79
113
  ...
@@ -1,9 +1,12 @@
1
1
  # chuk_tool_processor/registry/metadata.py
2
2
  """
3
- Tool metadata models for the registry.
3
+ Tool metadata models for the registry with async-native support.
4
4
  """
5
- from typing import Any, Dict, Optional, Set
6
- from pydantic import BaseModel, Field
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Dict, Optional, Set, List, Union
8
+ from datetime import datetime
9
+ from pydantic import BaseModel, Field, model_validator
7
10
 
8
11
 
9
12
  class ToolMetadata(BaseModel):
@@ -20,17 +23,60 @@ class ToolMetadata(BaseModel):
20
23
  result_schema: Optional schema for the tool's result.
21
24
  requires_auth: Whether the tool requires authentication.
22
25
  tags: Set of tags associated with the tool.
26
+ created_at: When the tool was first registered.
27
+ updated_at: When the tool was last updated.
28
+ source: Optional source information (e.g., "function", "class", "langchain").
29
+ source_name: Optional source identifier.
30
+ concurrency_limit: Optional maximum concurrent executions.
31
+ timeout: Optional default timeout in seconds.
32
+ rate_limit: Optional rate limiting configuration.
23
33
  """
24
34
  name: str = Field(..., description="Tool name")
25
35
  namespace: str = Field("default", description="Namespace the tool belongs to")
26
36
  description: Optional[str] = Field(None, description="Tool description")
27
37
  version: str = Field("1.0.0", description="Tool implementation version")
28
- is_async: bool = Field(False, description="Whether the tool's execute method is asynchronous")
38
+ is_async: bool = Field(True, description="Whether the tool's execute method is asynchronous")
29
39
  argument_schema: Optional[Dict[str, Any]] = Field(None, description="Schema for the tool's arguments")
30
40
  result_schema: Optional[Dict[str, Any]] = Field(None, description="Schema for the tool's result")
31
41
  requires_auth: bool = Field(False, description="Whether the tool requires authentication")
32
42
  tags: Set[str] = Field(default_factory=set, description="Tags associated with the tool")
33
-
43
+ created_at: datetime = Field(default_factory=datetime.utcnow, description="When the tool was first registered")
44
+ updated_at: datetime = Field(default_factory=datetime.utcnow, description="When the tool was last updated")
45
+ source: Optional[str] = Field(None, description="Source of the tool (e.g., 'function', 'class', 'langchain')")
46
+ source_name: Optional[str] = Field(None, description="Source identifier (e.g., function name, class name)")
47
+ concurrency_limit: Optional[int] = Field(None, description="Maximum concurrent executions (None = unlimited)")
48
+ timeout: Optional[float] = Field(None, description="Default timeout in seconds (None = no timeout)")
49
+ rate_limit: Optional[Dict[str, Any]] = Field(None, description="Rate limiting configuration")
50
+
51
+ # Additional fields for async-native architecture
52
+ supports_streaming: bool = Field(False, description="Whether the tool supports streaming responses")
53
+ execution_options: Dict[str, Any] = Field(default_factory=dict, description="Additional execution options")
54
+ dependencies: List[str] = Field(default_factory=list, description="Dependencies on other tools")
55
+
56
+ @model_validator(mode='after')
57
+ def ensure_async(self) -> 'ToolMetadata':
58
+ """Ensure all tools are marked as async in the async-native architecture."""
59
+ self.is_async = True
60
+ return self
61
+
62
+ def with_updated_timestamp(self) -> 'ToolMetadata':
63
+ """Create a copy with updated timestamp."""
64
+ return self.model_copy(update={"updated_at": datetime.utcnow()})
65
+
34
66
  def __str__(self) -> str:
35
67
  """String representation of the tool metadata."""
36
- return f"{self.namespace}.{self.name} (v{self.version})"
68
+ return f"{self.namespace}.{self.name} (v{self.version})"
69
+
70
+
71
+ class RateLimitConfig(BaseModel):
72
+ """Rate limiting configuration for tools."""
73
+ requests: int = Field(..., description="Maximum number of requests")
74
+ period: float = Field(..., description="Time period in seconds")
75
+ scope: str = Field("global", description="Scope of rate limiting: 'global', 'user', 'ip'")
76
+
77
+
78
+ class StreamingToolMetadata(ToolMetadata):
79
+ """Extended metadata for tools that support streaming responses."""
80
+ supports_streaming: bool = Field(True, description="Whether the tool supports streaming responses")
81
+ chunk_size: Optional[int] = Field(None, description="Suggested chunk size for streaming")
82
+ content_type: Optional[str] = Field(None, description="Content type for streaming responses")