swarmauri_toolkit_runtime 0.1.1.dev2__py3-none-any.whl → 0.11.0.dev1__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.
- swarmauri_toolkit_runtime/InspectRuntimeTool.py +1 -1
- swarmauri_toolkit_runtime/ListRuntimeTools.py +4 -2
- swarmauri_toolkit_runtime/RegisterRuntimeTool.py +16 -6
- swarmauri_toolkit_runtime/ReplaceRuntimeTool.py +22 -7
- swarmauri_toolkit_runtime/RuntimeToolkit.py +3 -3
- swarmauri_toolkit_runtime/UnregisterRuntimeTool.py +3 -1
- swarmauri_toolkit_runtime/_dynamic_runtime_tool.py +176 -0
- swarmauri_toolkit_runtime/_tool_factory.py +22 -25
- {swarmauri_toolkit_runtime-0.1.1.dev2.dist-info → swarmauri_toolkit_runtime-0.11.0.dev1.dist-info}/METADATA +27 -15
- swarmauri_toolkit_runtime-0.11.0.dev1.dist-info/RECORD +14 -0
- {swarmauri_toolkit_runtime-0.1.1.dev2.dist-info → swarmauri_toolkit_runtime-0.11.0.dev1.dist-info}/WHEEL +1 -1
- swarmauri_toolkit_runtime-0.1.1.dev2.dist-info/RECORD +0 -13
- {swarmauri_toolkit_runtime-0.1.1.dev2.dist-info → swarmauri_toolkit_runtime-0.11.0.dev1.dist-info}/entry_points.txt +0 -0
- {swarmauri_toolkit_runtime-0.1.1.dev2.dist-info → swarmauri_toolkit_runtime-0.11.0.dev1.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,7 +11,9 @@ from swarmauri_base.toolkits.ToolkitBase import ToolkitBase
|
|
|
11
11
|
class ListRuntimeTools(ToolBase):
|
|
12
12
|
version: str = "0.1.0"
|
|
13
13
|
name: str = "ListRuntimeTools"
|
|
14
|
-
description: str =
|
|
14
|
+
description: str = (
|
|
15
|
+
"List all tools currently available in the active toolkit."
|
|
16
|
+
)
|
|
15
17
|
type: Literal["ListRuntimeTools"] = "ListRuntimeTools"
|
|
16
18
|
toolkit: ToolkitBase | None = Field(default=None, exclude=True, repr=False)
|
|
17
19
|
parameters: List[Any] = Field(default_factory=list)
|
|
@@ -21,7 +23,7 @@ class ListRuntimeTools(ToolBase):
|
|
|
21
23
|
raise ValueError("toolkit is not configured")
|
|
22
24
|
|
|
23
25
|
tools = {
|
|
24
|
-
name: tool.model_dump(exclude_none=True)
|
|
26
|
+
name: tool.model_dump(exclude_none=True, by_alias=True)
|
|
25
27
|
for name, tool in self.toolkit.tools.items()
|
|
26
28
|
}
|
|
27
29
|
return {
|
|
@@ -15,7 +15,8 @@ class RegisterRuntimeTool(ToolBase):
|
|
|
15
15
|
version: str = "0.1.0"
|
|
16
16
|
name: str = "RegisterRuntimeTool"
|
|
17
17
|
description: str = (
|
|
18
|
-
"Create a tool from a serialized tool spec and add it to the
|
|
18
|
+
"Create a tool from a serialized tool spec and add it to the "
|
|
19
|
+
"active toolkit."
|
|
19
20
|
)
|
|
20
21
|
type: Literal["RegisterRuntimeTool"] = "RegisterRuntimeTool"
|
|
21
22
|
toolkit: ToolkitBase | None = Field(default=None, exclude=True, repr=False)
|
|
@@ -27,27 +28,36 @@ class RegisterRuntimeTool(ToolBase):
|
|
|
27
28
|
Parameter(
|
|
28
29
|
name="tool_spec",
|
|
29
30
|
input_type="object",
|
|
30
|
-
description=
|
|
31
|
+
description=(
|
|
32
|
+
"Serialized tool payload with 'type', 'parameters', and "
|
|
33
|
+
"'__call__' fields."
|
|
34
|
+
),
|
|
31
35
|
required=True,
|
|
32
36
|
)
|
|
33
37
|
]
|
|
34
38
|
)
|
|
35
39
|
|
|
36
|
-
def __call__(
|
|
40
|
+
def __call__(
|
|
41
|
+
self, tool_spec: Mapping[str, Any] | ToolBase
|
|
42
|
+
) -> Dict[str, Any]:
|
|
37
43
|
if self.toolkit is None:
|
|
38
44
|
raise ValueError("toolkit is not configured")
|
|
39
45
|
|
|
40
46
|
tool = build_tool_from_spec(tool_spec)
|
|
41
47
|
if tool.name in self.protected_tool_names:
|
|
42
|
-
raise ValueError(
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Tool '{tool.name}' is reserved by RuntimeToolkit"
|
|
50
|
+
)
|
|
43
51
|
if tool.name in self.toolkit.tools:
|
|
44
|
-
raise ValueError(
|
|
52
|
+
raise ValueError(
|
|
53
|
+
f"Tool '{tool.name}' already exists in the toolkit"
|
|
54
|
+
)
|
|
45
55
|
|
|
46
56
|
self.toolkit.add_tool(tool)
|
|
47
57
|
return {
|
|
48
58
|
"status": "created",
|
|
49
59
|
"tool_name": tool.name,
|
|
50
60
|
"tool_type": tool.type,
|
|
51
|
-
"tool": tool.model_dump(exclude_none=True),
|
|
61
|
+
"tool": tool.model_dump(exclude_none=True, by_alias=True),
|
|
52
62
|
"tool_count": len(self.toolkit),
|
|
53
63
|
}
|
|
@@ -14,7 +14,10 @@ from ._tool_factory import build_tool_from_spec
|
|
|
14
14
|
class ReplaceRuntimeTool(ToolBase):
|
|
15
15
|
version: str = "0.1.0"
|
|
16
16
|
name: str = "ReplaceRuntimeTool"
|
|
17
|
-
description: str =
|
|
17
|
+
description: str = (
|
|
18
|
+
"Replace an existing non-reserved tool in the active toolkit "
|
|
19
|
+
"with a new serialized tool spec."
|
|
20
|
+
)
|
|
18
21
|
type: Literal["ReplaceRuntimeTool"] = "ReplaceRuntimeTool"
|
|
19
22
|
toolkit: ToolkitBase | None = Field(default=None, exclude=True, repr=False)
|
|
20
23
|
protected_tool_names: set[str] = Field(
|
|
@@ -31,7 +34,10 @@ class ReplaceRuntimeTool(ToolBase):
|
|
|
31
34
|
Parameter(
|
|
32
35
|
name="tool_spec",
|
|
33
36
|
input_type="object",
|
|
34
|
-
description=
|
|
37
|
+
description=(
|
|
38
|
+
"Serialized replacement tool payload with 'type', "
|
|
39
|
+
"'parameters', and '__call__' fields."
|
|
40
|
+
),
|
|
35
41
|
required=True,
|
|
36
42
|
),
|
|
37
43
|
]
|
|
@@ -43,7 +49,9 @@ class ReplaceRuntimeTool(ToolBase):
|
|
|
43
49
|
if self.toolkit is None:
|
|
44
50
|
raise ValueError("toolkit is not configured")
|
|
45
51
|
if tool_name in self.protected_tool_names:
|
|
46
|
-
raise ValueError(
|
|
52
|
+
raise ValueError(
|
|
53
|
+
f"Tool '{tool_name}' is reserved by RuntimeToolkit"
|
|
54
|
+
)
|
|
47
55
|
if tool_name not in self.toolkit.tools:
|
|
48
56
|
raise ValueError(f"Tool '{tool_name}' not found in the toolkit")
|
|
49
57
|
|
|
@@ -52,9 +60,16 @@ class ReplaceRuntimeTool(ToolBase):
|
|
|
52
60
|
replacement.name in self.protected_tool_names
|
|
53
61
|
and replacement.name != tool_name
|
|
54
62
|
):
|
|
55
|
-
raise ValueError(
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
raise ValueError(
|
|
64
|
+
f"Tool '{replacement.name}' is reserved by RuntimeToolkit"
|
|
65
|
+
)
|
|
66
|
+
if (
|
|
67
|
+
replacement.name != tool_name
|
|
68
|
+
and replacement.name in self.toolkit.tools
|
|
69
|
+
):
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"Tool '{replacement.name}' already exists in the toolkit"
|
|
72
|
+
)
|
|
58
73
|
|
|
59
74
|
self.toolkit.remove_tool(tool_name)
|
|
60
75
|
self.toolkit.add_tool(replacement)
|
|
@@ -63,6 +78,6 @@ class ReplaceRuntimeTool(ToolBase):
|
|
|
63
78
|
"previous_tool_name": tool_name,
|
|
64
79
|
"tool_name": replacement.name,
|
|
65
80
|
"tool_type": replacement.type,
|
|
66
|
-
"tool": replacement.model_dump(exclude_none=True),
|
|
81
|
+
"tool": replacement.model_dump(exclude_none=True, by_alias=True),
|
|
67
82
|
"tool_count": len(self.toolkit),
|
|
68
83
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Dict, Literal
|
|
2
2
|
|
|
3
|
-
from swarmauri_base.ComponentBase import ComponentBase
|
|
3
|
+
from swarmauri_base.ComponentBase import ComponentBase
|
|
4
4
|
from swarmauri_base.tools.ToolBase import ToolBase
|
|
5
5
|
from swarmauri_base.toolkits.ToolkitBase import ToolkitBase
|
|
6
6
|
|
|
@@ -14,13 +14,13 @@ from .ReplaceRuntimeTool import ReplaceRuntimeTool
|
|
|
14
14
|
@ComponentBase.register_type(ToolkitBase, "RuntimeToolkit")
|
|
15
15
|
class RuntimeToolkit(ToolkitBase):
|
|
16
16
|
type: Literal["RuntimeToolkit"] = "RuntimeToolkit"
|
|
17
|
-
tools: Dict[str,
|
|
17
|
+
tools: Dict[str, ToolBase] = {}
|
|
18
18
|
|
|
19
19
|
def __init__(self, **kwargs):
|
|
20
20
|
super().__init__(**kwargs)
|
|
21
21
|
self.tools = self._build_management_tools()
|
|
22
22
|
|
|
23
|
-
def _build_management_tools(self) -> Dict[str,
|
|
23
|
+
def _build_management_tools(self) -> Dict[str, ToolBase]:
|
|
24
24
|
protected_tool_names = {
|
|
25
25
|
"RegisterRuntimeTool",
|
|
26
26
|
"InspectRuntimeTool",
|
|
@@ -33,7 +33,9 @@ class UnregisterRuntimeTool(ToolBase):
|
|
|
33
33
|
if self.toolkit is None:
|
|
34
34
|
raise ValueError("toolkit is not configured")
|
|
35
35
|
if tool_name in self.protected_tool_names:
|
|
36
|
-
raise ValueError(
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Tool '{tool_name}' is reserved by RuntimeToolkit"
|
|
38
|
+
)
|
|
37
39
|
|
|
38
40
|
self.toolkit.remove_tool(tool_name)
|
|
39
41
|
return {
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from typing import Any, Dict, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import ConfigDict, Field
|
|
5
|
+
|
|
6
|
+
from swarmauri_base.tools.ToolBase import ToolBase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
SAFE_BUILTINS = {
|
|
10
|
+
"abs": abs,
|
|
11
|
+
"bool": bool,
|
|
12
|
+
"dict": dict,
|
|
13
|
+
"float": float,
|
|
14
|
+
"int": int,
|
|
15
|
+
"len": len,
|
|
16
|
+
"list": list,
|
|
17
|
+
"max": max,
|
|
18
|
+
"min": min,
|
|
19
|
+
"round": round,
|
|
20
|
+
"set": set,
|
|
21
|
+
"str": str,
|
|
22
|
+
"sum": sum,
|
|
23
|
+
"tuple": tuple,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ALLOWED_AST_NODES = (
|
|
27
|
+
ast.Add,
|
|
28
|
+
ast.And,
|
|
29
|
+
ast.BinOp,
|
|
30
|
+
ast.BoolOp,
|
|
31
|
+
ast.Compare,
|
|
32
|
+
ast.Constant,
|
|
33
|
+
ast.Dict,
|
|
34
|
+
ast.Div,
|
|
35
|
+
ast.Eq,
|
|
36
|
+
ast.Expression,
|
|
37
|
+
ast.FloorDiv,
|
|
38
|
+
ast.Gt,
|
|
39
|
+
ast.GtE,
|
|
40
|
+
ast.IfExp,
|
|
41
|
+
ast.List,
|
|
42
|
+
ast.Load,
|
|
43
|
+
ast.Lt,
|
|
44
|
+
ast.LtE,
|
|
45
|
+
ast.Mod,
|
|
46
|
+
ast.Mult,
|
|
47
|
+
ast.Name,
|
|
48
|
+
ast.Not,
|
|
49
|
+
ast.NotEq,
|
|
50
|
+
ast.Or,
|
|
51
|
+
ast.Pow,
|
|
52
|
+
ast.Set,
|
|
53
|
+
ast.Sub,
|
|
54
|
+
ast.Tuple,
|
|
55
|
+
ast.USub,
|
|
56
|
+
ast.UnaryOp,
|
|
57
|
+
ast.Call,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SafeExpressionValidator(ast.NodeVisitor):
|
|
62
|
+
def __init__(self, allowed_names: set[str]):
|
|
63
|
+
self.allowed_names = allowed_names
|
|
64
|
+
|
|
65
|
+
def generic_visit(self, node):
|
|
66
|
+
if not isinstance(node, ALLOWED_AST_NODES):
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"Runtime tool '__call__' contains unsafe syntax: {type(node).__name__}" # noqa: E501
|
|
69
|
+
)
|
|
70
|
+
super().generic_visit(node)
|
|
71
|
+
|
|
72
|
+
def visit_Name(self, node: ast.Name):
|
|
73
|
+
if node.id not in self.allowed_names:
|
|
74
|
+
raise ValueError(
|
|
75
|
+
f"Runtime tool '__call__' references disallowed name '{node.id}'" # noqa: E501
|
|
76
|
+
)
|
|
77
|
+
self.generic_visit(node)
|
|
78
|
+
|
|
79
|
+
def visit_Call(self, node: ast.Call):
|
|
80
|
+
if not isinstance(node.func, ast.Name):
|
|
81
|
+
raise ValueError(
|
|
82
|
+
(
|
|
83
|
+
"Runtime tool '__call__' only allows direct calls to approved " # noqa: E501
|
|
84
|
+
"builtins"
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
if node.func.id not in SAFE_BUILTINS:
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"Runtime tool '__call__' cannot call '{node.func.id}' safely"
|
|
90
|
+
)
|
|
91
|
+
self.generic_visit(node)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class DynamicRuntimeTool(ToolBase):
|
|
95
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
96
|
+
|
|
97
|
+
version: str = "0.1.0"
|
|
98
|
+
type: str
|
|
99
|
+
runtime_kind: Literal["dynamic"] = Field(default="dynamic", exclude=True)
|
|
100
|
+
call_source: str = Field(alias="__call__", serialization_alias="__call__")
|
|
101
|
+
|
|
102
|
+
def validate_call_source(self) -> "DynamicRuntimeTool":
|
|
103
|
+
self._compile_expression(self._normalized_expression())
|
|
104
|
+
return self
|
|
105
|
+
|
|
106
|
+
def __call__(self, *args, **kwargs) -> Any:
|
|
107
|
+
try:
|
|
108
|
+
expression = self._normalized_expression()
|
|
109
|
+
compiled = self._compile_expression(expression)
|
|
110
|
+
runtime_inputs = self._bind_inputs(args, kwargs)
|
|
111
|
+
runtime_globals = {"__builtins__": {}}
|
|
112
|
+
runtime_locals = {**SAFE_BUILTINS, **runtime_inputs}
|
|
113
|
+
return eval(compiled, runtime_globals, runtime_locals)
|
|
114
|
+
except BaseException as exc:
|
|
115
|
+
return self._safe_error_result(exc)
|
|
116
|
+
|
|
117
|
+
def _normalized_expression(self) -> str:
|
|
118
|
+
expression = self.call_source.strip()
|
|
119
|
+
if expression.startswith("return "):
|
|
120
|
+
expression = expression[7:].strip()
|
|
121
|
+
if not expression:
|
|
122
|
+
raise ValueError("Runtime tool '__call__' must not be empty")
|
|
123
|
+
return expression
|
|
124
|
+
|
|
125
|
+
def _compile_expression(self, expression: str):
|
|
126
|
+
try:
|
|
127
|
+
parsed = ast.parse(expression, mode="eval")
|
|
128
|
+
except SyntaxError as exc:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
"Runtime tool '__call__' must be a safe Python expression"
|
|
131
|
+
) from exc
|
|
132
|
+
|
|
133
|
+
allowed_names = {
|
|
134
|
+
parameter.name for parameter in self.parameters
|
|
135
|
+
} | set(SAFE_BUILTINS)
|
|
136
|
+
SafeExpressionValidator(allowed_names).visit(parsed)
|
|
137
|
+
return compile(
|
|
138
|
+
parsed, filename=f"<runtime-tool:{self.name}>", mode="eval"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def _bind_inputs(
|
|
142
|
+
self, args: tuple[Any, ...], kwargs: Dict[str, Any]
|
|
143
|
+
) -> Dict[str, Any]:
|
|
144
|
+
parameter_names = [parameter.name for parameter in self.parameters]
|
|
145
|
+
if len(args) > len(parameter_names):
|
|
146
|
+
raise ValueError(
|
|
147
|
+
f"Runtime tool '{self.name}' received too many positional arguments" # noqa: E501
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
bound_inputs: Dict[str, Any] = dict(zip(parameter_names, args))
|
|
151
|
+
for key, value in kwargs.items():
|
|
152
|
+
if key not in parameter_names:
|
|
153
|
+
raise ValueError(
|
|
154
|
+
f"Runtime tool '{self.name}' received unexpected argument '{key}'" # noqa: E501
|
|
155
|
+
)
|
|
156
|
+
if key in bound_inputs:
|
|
157
|
+
raise ValueError(
|
|
158
|
+
f"Runtime tool '{self.name}' received duplicate argument '{key}'" # noqa: E501
|
|
159
|
+
)
|
|
160
|
+
bound_inputs[key] = value
|
|
161
|
+
|
|
162
|
+
for parameter in self.parameters:
|
|
163
|
+
if parameter.required and parameter.name not in bound_inputs:
|
|
164
|
+
raise ValueError(
|
|
165
|
+
f"Runtime tool '{self.name}' is missing required argument '{parameter.name}'" # noqa: E501
|
|
166
|
+
)
|
|
167
|
+
return bound_inputs
|
|
168
|
+
|
|
169
|
+
def _safe_error_result(self, exc: BaseException) -> Dict[str, Any]:
|
|
170
|
+
return {
|
|
171
|
+
"status": "error",
|
|
172
|
+
"tool_name": self.name,
|
|
173
|
+
"tool_type": self.type,
|
|
174
|
+
"error_type": type(exc).__name__,
|
|
175
|
+
"error": str(exc),
|
|
176
|
+
}
|
|
@@ -1,27 +1,21 @@
|
|
|
1
|
-
from importlib import import_module
|
|
2
1
|
from typing import Any, Mapping
|
|
3
2
|
|
|
4
3
|
from swarmauri_base.tools.ToolBase import ToolBase
|
|
4
|
+
from swarmauri_standard.tools.Parameter import Parameter
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
def _resolve_tool_module(tool_type: str):
|
|
8
|
-
module_names = [
|
|
9
|
-
f"swarmauri.tools.{tool_type}",
|
|
10
|
-
f"swarmauri_standard.tools.{tool_type}",
|
|
11
|
-
]
|
|
12
|
-
last_error: ModuleNotFoundError | None = None
|
|
13
|
-
for module_name in module_names:
|
|
14
|
-
try:
|
|
15
|
-
return import_module(module_name)
|
|
16
|
-
except ModuleNotFoundError as exc:
|
|
17
|
-
last_error = exc
|
|
18
|
-
raise ValueError(f"Unable to resolve tool type '{tool_type}'") from last_error
|
|
6
|
+
from ._dynamic_runtime_tool import DynamicRuntimeTool
|
|
19
7
|
|
|
20
8
|
|
|
21
9
|
def build_tool_from_spec(tool_spec: Mapping[str, Any] | ToolBase) -> ToolBase:
|
|
22
10
|
if isinstance(tool_spec, ToolBase):
|
|
23
11
|
if not tool_spec.parameters:
|
|
24
|
-
raise ValueError(
|
|
12
|
+
raise ValueError(
|
|
13
|
+
"Runtime tool registration requires declared parameters"
|
|
14
|
+
)
|
|
15
|
+
if not callable(getattr(tool_spec, "__call__", None)):
|
|
16
|
+
raise ValueError(
|
|
17
|
+
"Runtime tool registration requires a callable '__call__'"
|
|
18
|
+
)
|
|
25
19
|
return tool_spec
|
|
26
20
|
if not isinstance(tool_spec, Mapping):
|
|
27
21
|
raise TypeError("tool_spec must be a mapping or ToolBase instance")
|
|
@@ -29,21 +23,24 @@ def build_tool_from_spec(tool_spec: Mapping[str, Any] | ToolBase) -> ToolBase:
|
|
|
29
23
|
tool_data = dict(tool_spec)
|
|
30
24
|
tool_type = tool_data.get("type")
|
|
31
25
|
if not isinstance(tool_type, str) or not tool_type:
|
|
32
|
-
raise ValueError(
|
|
26
|
+
raise ValueError(
|
|
27
|
+
"tool_spec must include a non-empty string 'type' field"
|
|
28
|
+
)
|
|
33
29
|
declared_parameters = tool_data.get("parameters")
|
|
34
30
|
if not isinstance(declared_parameters, list) or not declared_parameters:
|
|
35
31
|
raise ValueError(
|
|
36
32
|
"Runtime tool registration requires a non-empty 'parameters' list"
|
|
37
33
|
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
tool_cls = getattr(module, tool_type, None)
|
|
42
|
-
if tool_cls is None:
|
|
34
|
+
call_source = tool_data.get("__call__")
|
|
35
|
+
if not isinstance(call_source, str) or not call_source.strip():
|
|
43
36
|
raise ValueError(
|
|
44
|
-
|
|
37
|
+
"Runtime tool registration requires a non-empty '__call__' string"
|
|
45
38
|
)
|
|
46
|
-
if not issubclass(tool_cls, ToolBase):
|
|
47
|
-
raise TypeError(f"Resolved class '{tool_type}' is not a ToolBase subtype")
|
|
48
39
|
|
|
49
|
-
|
|
40
|
+
tool_data["parameters"] = [
|
|
41
|
+
parameter
|
|
42
|
+
if isinstance(parameter, Parameter)
|
|
43
|
+
else Parameter.model_validate(parameter)
|
|
44
|
+
for parameter in declared_parameters
|
|
45
|
+
]
|
|
46
|
+
return DynamicRuntimeTool.model_validate(tool_data).validate_call_source()
|
|
@@ -1,31 +1,33 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarmauri_toolkit_runtime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0.dev1
|
|
4
4
|
Summary: Runtime toolkit for explicit-parameter registration, inspection, replacement, listing, and removal of Swarmauri tools during agent execution.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Keywords: swarmauri,sdk,standards,toolkit,runtime,agent-runtime,tool-management,tool-registration,tool-inspection,tool-replacement,tool-removal,tool-schema,explicit-parameters,runtime-governance,agent,crud,runtime-tools
|
|
8
8
|
Author: Jacob Stewart
|
|
9
9
|
Author-email: jacob@swarmauri.com
|
|
10
|
-
Requires-Python: >=3.10,<3.
|
|
10
|
+
Requires-Python: >=3.10,<3.15
|
|
11
11
|
Classifier: Development Status :: 1 - Planning
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
14
|
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
15
16
|
Classifier: Programming Language :: Python
|
|
16
17
|
Classifier: Programming Language :: Python :: 3
|
|
17
18
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
-
Classifier:
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
24
|
Requires-Dist: pydantic
|
|
23
25
|
Requires-Dist: swarmauri_base
|
|
24
26
|
Requires-Dist: swarmauri_core
|
|
25
27
|
Requires-Dist: swarmauri_standard
|
|
26
28
|
Description-Content-Type: text/markdown
|
|
27
29
|
|
|
28
|
-

|
|
29
31
|
|
|
30
32
|
<p align="center">
|
|
31
33
|
<a href="https://pepy.tech/project/swarmauri_toolkit_runtime/">
|
|
@@ -33,22 +35,24 @@ Description-Content-Type: text/markdown
|
|
|
33
35
|
<a href="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_toolkit_runtime/">
|
|
34
36
|
<img alt="Hits" src="https://hits.sh/github.com/swarmauri/swarmauri-sdk/tree/master/pkgs/standards/swarmauri_toolkit_runtime.svg"/></a>
|
|
35
37
|
<a href="https://pypi.org/project/swarmauri_toolkit_runtime/">
|
|
36
|
-
<img src="https://img.shields.io/
|
|
38
|
+
<img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue" alt="PyPI - Python Version"/></a>
|
|
37
39
|
<a href="https://pypi.org/project/swarmauri_toolkit_runtime/">
|
|
38
40
|
<img src="https://img.shields.io/pypi/l/swarmauri_toolkit_runtime" alt="PyPI - License"/></a>
|
|
39
41
|
<a href="https://pypi.org/project/swarmauri_toolkit_runtime/">
|
|
40
42
|
<img src="https://img.shields.io/pypi/v/swarmauri_toolkit_runtime?label=swarmauri_toolkit_runtime&color=green" alt="PyPI - swarmauri_toolkit_runtime"/></a>
|
|
41
|
-
|
|
43
|
+
<a href="https://discord.gg/N4UpBuQv8T">
|
|
44
|
+
<img src="https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white" alt="Discord"/></a></p>
|
|
42
45
|
|
|
43
46
|
# Swarmauri Toolkit RuntimeToolkit
|
|
44
47
|
|
|
45
|
-
`RuntimeToolkit` is a Swarmauri runtime-management toolkit for agents that need to register, inspect, list, replace, and remove tools during execution. It keeps tool mutation inside the active toolkit instance and requires explicit parameter declarations when a new runtime tool is introduced.
|
|
48
|
+
`RuntimeToolkit` is a Swarmauri runtime-management toolkit for agents that need to register, inspect, list, replace, and remove tools during execution. It keeps tool mutation inside the active toolkit instance and requires both explicit parameter declarations and an explicit `__call__` implementation when a new runtime tool is introduced.
|
|
46
49
|
|
|
47
50
|
## Features
|
|
48
51
|
|
|
49
52
|
- Bundles agent-callable CRUD operations for toolkit members.
|
|
50
|
-
- Accepts serialized tool specs with `type`
|
|
51
|
-
- Enforces a non-empty declared `parameters` list for runtime tool registration and replacement.
|
|
53
|
+
- Accepts serialized runtime tool specs with `type`, `parameters`, and `__call__` fields.
|
|
54
|
+
- Enforces a non-empty declared `parameters` list and a non-empty `__call__` body for runtime tool registration and replacement.
|
|
55
|
+
- Evaluates runtime `__call__` bodies through a restricted expression rail rather than unrestricted Python execution.
|
|
52
56
|
- Returns agent-friendly dictionaries for reads, lists, and mutation results.
|
|
53
57
|
- Protects the toolkit's own management tools from accidental overwrite or removal.
|
|
54
58
|
- Supports Python 3.10 through 3.12.
|
|
@@ -78,7 +82,7 @@ pip install swarmauri_toolkit_runtime
|
|
|
78
82
|
Use `RuntimeToolkit` when an agent needs to mutate its own tool surface safely at runtime. The expected workflow is:
|
|
79
83
|
|
|
80
84
|
1. Start with the management toolkit.
|
|
81
|
-
2. Register a runtime tool using an explicit parameter contract.
|
|
85
|
+
2. Register a runtime tool using an explicit parameter contract and explicit callable body.
|
|
82
86
|
3. Inspect or list the available runtime tools.
|
|
83
87
|
4. Execute the registered tool.
|
|
84
88
|
5. Replace or unregister the tool when the runtime surface changes.
|
|
@@ -92,7 +96,7 @@ toolkit = RuntimeToolkit()
|
|
|
92
96
|
|
|
93
97
|
toolkit.get_tool_by_name("RegisterRuntimeTool")(
|
|
94
98
|
{
|
|
95
|
-
"type": "
|
|
99
|
+
"type": "RuntimeAdditionTool",
|
|
96
100
|
"name": "RuntimeAdditionTool",
|
|
97
101
|
"description": "Adds two integers during the active agent session.",
|
|
98
102
|
"parameters": [
|
|
@@ -109,6 +113,7 @@ toolkit.get_tool_by_name("RegisterRuntimeTool")(
|
|
|
109
113
|
"required": True,
|
|
110
114
|
},
|
|
111
115
|
],
|
|
116
|
+
"__call__": '{"sum": str(x + y)}',
|
|
112
117
|
}
|
|
113
118
|
)
|
|
114
119
|
|
|
@@ -128,14 +133,15 @@ print(result)
|
|
|
128
133
|
|
|
129
134
|
Mutation tools accept a serialized tool specification. At minimum, provide:
|
|
130
135
|
|
|
131
|
-
- a `type` field that
|
|
136
|
+
- a non-empty `type` field that names the runtime tool surface
|
|
132
137
|
- a non-empty `parameters` list that explicitly declares the runtime callable contract
|
|
138
|
+
- a non-empty `__call__` string that evaluates as a safe Python expression over the declared parameters and approved builtins
|
|
133
139
|
|
|
134
|
-
|
|
140
|
+
The runtime evaluator permits only a constrained expression subset and approved builtins such as `str`, `int`, `float`, `len`, `sum`, `min`, `max`, and `abs`. Imports, attribute traversal, and arbitrary function calls are rejected.
|
|
135
141
|
|
|
136
142
|
```python
|
|
137
143
|
{
|
|
138
|
-
"type": "
|
|
144
|
+
"type": "RuntimeAdditionTool",
|
|
139
145
|
"name": "RuntimeAdditionTool",
|
|
140
146
|
"description": "Adds two integers.",
|
|
141
147
|
"parameters": [
|
|
@@ -152,15 +158,19 @@ Resolution prefers the `swarmauri` facade when present and falls back to `swarma
|
|
|
152
158
|
"required": True,
|
|
153
159
|
},
|
|
154
160
|
],
|
|
161
|
+
"__call__": '{"sum": str(x + y)}',
|
|
155
162
|
}
|
|
156
163
|
```
|
|
157
164
|
|
|
158
|
-
|
|
165
|
+
The `__call__` value is evaluated against the declared parameter names. A body such as `{"sum": str(x + y)}` is valid. A body that tries to import modules, access attributes, or reference undeclared names is rejected.
|
|
159
166
|
|
|
160
167
|
## Failure Modes
|
|
161
168
|
|
|
162
169
|
- Registration fails when the tool spec omits `parameters`.
|
|
170
|
+
- Registration fails when the tool spec omits `__call__`.
|
|
171
|
+
- Registration fails when the `__call__` body uses unsafe syntax or disallowed names.
|
|
163
172
|
- Registration fails when the tool name collides with a protected management tool.
|
|
173
|
+
- Execution failures inside an accepted runtime tool return a structured error payload instead of propagating a hard exception or terminating the host process.
|
|
164
174
|
- Replacement fails when the target tool is missing, reserved, or renamed to an existing tool.
|
|
165
175
|
- Unregistration fails when the target is reserved or absent.
|
|
166
176
|
|
|
@@ -169,3 +179,5 @@ If the target tool defines extra fields, include them in the spec and the toolki
|
|
|
169
179
|
`swarmauri_toolkit_runtime` is released under the Apache 2.0 License. See `LICENSE` for details.
|
|
170
180
|
|
|
171
181
|
|
|
182
|
+
|
|
183
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
swarmauri_toolkit_runtime/InspectRuntimeTool.py,sha256=fuSWqjXl0X83467ATB6jZ-3bAFMFTgvmI3pPQuWZsM0,1356
|
|
2
|
+
swarmauri_toolkit_runtime/ListRuntimeTools.py,sha256=AF6Vx_aZRTAqzAFXXKvFBjVFp6ThuKq-wqn75yHBT14,1137
|
|
3
|
+
swarmauri_toolkit_runtime/RegisterRuntimeTool.py,sha256=yLAjo2CI9vbX7yTv2bVor3pr-2ANtgmFqpSzti2Floc,2136
|
|
4
|
+
swarmauri_toolkit_runtime/ReplaceRuntimeTool.py,sha256=9biwfk7dvROMLkcbONCY4wHLllDqs5YiXKPq6IqixkM,2959
|
|
5
|
+
swarmauri_toolkit_runtime/RuntimeToolkit.py,sha256=ke-1h0OvmyXtVBoBh7TSBfpZihuUFc18vRohyOx301k,1638
|
|
6
|
+
swarmauri_toolkit_runtime/UnregisterRuntimeTool.py,sha256=xNJDeSY_piz2hAySBxIYnVE2cpvNlKUj3vMQ9wpBqeM,1603
|
|
7
|
+
swarmauri_toolkit_runtime/__init__.py,sha256=Ing0vOmvJXTiLCe8RMLXkOMN8-3DAt0NDDCnfumZoNA,471
|
|
8
|
+
swarmauri_toolkit_runtime/_dynamic_runtime_tool.py,sha256=-UuXXRhDgZQgybapAJGI3NlLcdNcX03Kef64dz4dqyI,5370
|
|
9
|
+
swarmauri_toolkit_runtime/_tool_factory.py,sha256=XNs-U84cOMgsUZogfjmWigXvQU_eEZInctrSmeh17oM,1776
|
|
10
|
+
swarmauri_toolkit_runtime-0.11.0.dev1.dist-info/METADATA,sha256=Zu2Y8TWOojxX2TqL4b6KQCSmR2BFGvmcis9-wX9O-sY,7858
|
|
11
|
+
swarmauri_toolkit_runtime-0.11.0.dev1.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
12
|
+
swarmauri_toolkit_runtime-0.11.0.dev1.dist-info/entry_points.txt,sha256=pB0M21BP9R-2_M52C_i5JBCsU1TLQD5m7P50IJs_lXA,421
|
|
13
|
+
swarmauri_toolkit_runtime-0.11.0.dev1.dist-info/licenses/LICENSE,sha256=djUXOlCxLVszShEpZXshZ7v33G-2qIC_j9KXpWKZSzQ,11359
|
|
14
|
+
swarmauri_toolkit_runtime-0.11.0.dev1.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
swarmauri_toolkit_runtime/InspectRuntimeTool.py,sha256=vy4XllPb46TfUxGJVN_ggYm-TEIHpTpI8BAiQJBrBF0,1341
|
|
2
|
-
swarmauri_toolkit_runtime/ListRuntimeTools.py,sha256=D6tiHi8d1O95fd81fgVDyUdhNT4Tjqp4RWO3Na8b2Sk,1106
|
|
3
|
-
swarmauri_toolkit_runtime/RegisterRuntimeTool.py,sha256=z3Uhr7iKCrCAAgdaolLlP0x-XUBMae36LptkFCzxF5M,1953
|
|
4
|
-
swarmauri_toolkit_runtime/ReplaceRuntimeTool.py,sha256=5uZutD2jrIgVKHYWAlxQ5IEKZiW8XxL3vIlvQq2Jx0g,2708
|
|
5
|
-
swarmauri_toolkit_runtime/RuntimeToolkit.py,sha256=q_SKZ6BADVPP6Zt5JdMmfqG8ac_xeDmek19pGRHgBgg,1683
|
|
6
|
-
swarmauri_toolkit_runtime/UnregisterRuntimeTool.py,sha256=6dWC6CEgZ2eHSlgohgRB6c-mxPCoPBDs4TLT_kWYjps,1573
|
|
7
|
-
swarmauri_toolkit_runtime/__init__.py,sha256=Ing0vOmvJXTiLCe8RMLXkOMN8-3DAt0NDDCnfumZoNA,471
|
|
8
|
-
swarmauri_toolkit_runtime/_tool_factory.py,sha256=ZUCyxu9BXPtg8aVKRs66rQxpAJjokhCLp07qPSABXLs,1832
|
|
9
|
-
swarmauri_toolkit_runtime-0.1.1.dev2.dist-info/METADATA,sha256=lWjpAsmmkhAs3_28jOJ_c5a6fPO68Rx7bZwgKvmETQg,6715
|
|
10
|
-
swarmauri_toolkit_runtime-0.1.1.dev2.dist-info/WHEEL,sha256=Vz2fHgx6HFtSwhs8KvkHLqH5Ea4w1_rner5uNVGCeIE,88
|
|
11
|
-
swarmauri_toolkit_runtime-0.1.1.dev2.dist-info/entry_points.txt,sha256=pB0M21BP9R-2_M52C_i5JBCsU1TLQD5m7P50IJs_lXA,421
|
|
12
|
-
swarmauri_toolkit_runtime-0.1.1.dev2.dist-info/licenses/LICENSE,sha256=djUXOlCxLVszShEpZXshZ7v33G-2qIC_j9KXpWKZSzQ,11359
|
|
13
|
-
swarmauri_toolkit_runtime-0.1.1.dev2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|