swarmauri_toolkit_runtime 0.11.0.dev1__tar.gz → 0.11.0.dev4__tar.gz
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-0.11.0.dev1 → swarmauri_toolkit_runtime-0.11.0.dev4}/PKG-INFO +9 -4
- {swarmauri_toolkit_runtime-0.11.0.dev1 → swarmauri_toolkit_runtime-0.11.0.dev4}/pyproject.toml +14 -11
- swarmauri_toolkit_runtime-0.11.0.dev4/swarmauri_toolkit_runtime/InspectRuntimeTool.py +3 -0
- swarmauri_toolkit_runtime-0.11.0.dev4/swarmauri_toolkit_runtime/ListRuntimeTools.py +3 -0
- swarmauri_toolkit_runtime-0.11.0.dev4/swarmauri_toolkit_runtime/RecipeTool.py +47 -0
- swarmauri_toolkit_runtime-0.11.0.dev4/swarmauri_toolkit_runtime/RegisterRuntimeTool.py +3 -0
- swarmauri_toolkit_runtime-0.11.0.dev4/swarmauri_toolkit_runtime/ReplaceRuntimeTool.py +3 -0
- {swarmauri_toolkit_runtime-0.11.0.dev1 → swarmauri_toolkit_runtime-0.11.0.dev4}/swarmauri_toolkit_runtime/RuntimeToolkit.py +34 -2
- swarmauri_toolkit_runtime-0.11.0.dev4/swarmauri_toolkit_runtime/UnregisterRuntimeTool.py +3 -0
- swarmauri_toolkit_runtime-0.11.0.dev4/swarmauri_toolkit_runtime/_dynamic_runtime_tool.py +3 -0
- swarmauri_toolkit_runtime-0.11.0.dev4/swarmauri_toolkit_runtime/_tool_factory.py +3 -0
- swarmauri_toolkit_runtime-0.11.0.dev1/swarmauri_toolkit_runtime/InspectRuntimeTool.py +0 -38
- swarmauri_toolkit_runtime-0.11.0.dev1/swarmauri_toolkit_runtime/ListRuntimeTools.py +0 -33
- swarmauri_toolkit_runtime-0.11.0.dev1/swarmauri_toolkit_runtime/RegisterRuntimeTool.py +0 -63
- swarmauri_toolkit_runtime-0.11.0.dev1/swarmauri_toolkit_runtime/ReplaceRuntimeTool.py +0 -83
- swarmauri_toolkit_runtime-0.11.0.dev1/swarmauri_toolkit_runtime/UnregisterRuntimeTool.py +0 -45
- swarmauri_toolkit_runtime-0.11.0.dev1/swarmauri_toolkit_runtime/_dynamic_runtime_tool.py +0 -176
- swarmauri_toolkit_runtime-0.11.0.dev1/swarmauri_toolkit_runtime/_tool_factory.py +0 -46
- {swarmauri_toolkit_runtime-0.11.0.dev1 → swarmauri_toolkit_runtime-0.11.0.dev4}/LICENSE +0 -0
- {swarmauri_toolkit_runtime-0.11.0.dev1 → swarmauri_toolkit_runtime-0.11.0.dev4}/README.md +0 -0
- {swarmauri_toolkit_runtime-0.11.0.dev1 → swarmauri_toolkit_runtime-0.11.0.dev4}/swarmauri_toolkit_runtime/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarmauri_toolkit_runtime
|
|
3
|
-
Version: 0.11.0.
|
|
3
|
+
Version: 0.11.0.dev4
|
|
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
|
|
@@ -22,9 +22,14 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.14
|
|
24
24
|
Requires-Dist: pydantic
|
|
25
|
-
Requires-Dist: swarmauri_base
|
|
26
|
-
Requires-Dist: swarmauri_core
|
|
27
|
-
Requires-Dist: swarmauri_standard
|
|
25
|
+
Requires-Dist: swarmauri_base (>=0.11.0.dev6,<0.12)
|
|
26
|
+
Requires-Dist: swarmauri_core (>=0.11.0.dev6,<0.12)
|
|
27
|
+
Requires-Dist: swarmauri_standard (>=0.11.0.dev8,<0.12)
|
|
28
|
+
Requires-Dist: swarmauri_tool_inspectruntimetool (>=0.1.0,<0.2)
|
|
29
|
+
Requires-Dist: swarmauri_tool_listruntimetools (>=0.1.0,<0.2)
|
|
30
|
+
Requires-Dist: swarmauri_tool_registerruntimetool (>=0.1.0,<0.2)
|
|
31
|
+
Requires-Dist: swarmauri_tool_replaceruntimetool (>=0.1.0,<0.2)
|
|
32
|
+
Requires-Dist: swarmauri_tool_unregisterruntimetool (>=0.1.0,<0.2)
|
|
28
33
|
Description-Content-Type: text/markdown
|
|
29
34
|
|
|
30
35
|

|
{swarmauri_toolkit_runtime-0.11.0.dev1 → swarmauri_toolkit_runtime-0.11.0.dev4}/pyproject.toml
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "swarmauri_toolkit_runtime"
|
|
3
|
-
version = "0.11.0.
|
|
3
|
+
version = "0.11.0.dev4"
|
|
4
4
|
description = "Runtime toolkit for explicit-parameter registration, inspection, replacement, listing, and removal of Swarmauri tools during agent execution."
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
@@ -23,10 +23,15 @@ classifiers = [
|
|
|
23
23
|
]
|
|
24
24
|
authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
|
|
25
25
|
dependencies = [
|
|
26
|
+
"swarmauri_tool_registerruntimetool>=0.1.0,<0.2",
|
|
27
|
+
"swarmauri_tool_inspectruntimetool>=0.1.0,<0.2",
|
|
28
|
+
"swarmauri_tool_listruntimetools>=0.1.0,<0.2",
|
|
29
|
+
"swarmauri_tool_replaceruntimetool>=0.1.0,<0.2",
|
|
30
|
+
"swarmauri_tool_unregisterruntimetool>=0.1.0,<0.2",
|
|
26
31
|
"pydantic",
|
|
27
|
-
"swarmauri_core",
|
|
28
|
-
"swarmauri_base",
|
|
29
|
-
"swarmauri_standard",
|
|
32
|
+
"swarmauri_core>=0.11.0.dev6,<0.12",
|
|
33
|
+
"swarmauri_base>=0.11.0.dev6,<0.12",
|
|
34
|
+
"swarmauri_standard>=0.11.0.dev8,<0.12",
|
|
30
35
|
]
|
|
31
36
|
keywords = [
|
|
32
37
|
"swarmauri",
|
|
@@ -49,6 +54,11 @@ keywords = [
|
|
|
49
54
|
]
|
|
50
55
|
|
|
51
56
|
[tool.uv.sources]
|
|
57
|
+
swarmauri_tool_registerruntimetool = { workspace = true }
|
|
58
|
+
swarmauri_tool_inspectruntimetool = { workspace = true }
|
|
59
|
+
swarmauri_tool_listruntimetools = { workspace = true }
|
|
60
|
+
swarmauri_tool_replaceruntimetool = { workspace = true }
|
|
61
|
+
swarmauri_tool_unregisterruntimetool = { workspace = true }
|
|
52
62
|
swarmauri_core = { workspace = true }
|
|
53
63
|
swarmauri_base = { workspace = true }
|
|
54
64
|
swarmauri_standard = { workspace = true }
|
|
@@ -76,13 +86,6 @@ asyncio_default_fixture_loop_scope = "function"
|
|
|
76
86
|
[project.entry-points.'swarmauri.toolkits']
|
|
77
87
|
RuntimeToolkit = "swarmauri_toolkit_runtime:RuntimeToolkit"
|
|
78
88
|
|
|
79
|
-
[project.entry-points.'swarmauri.tools']
|
|
80
|
-
RegisterRuntimeTool = "swarmauri_toolkit_runtime:RegisterRuntimeTool"
|
|
81
|
-
InspectRuntimeTool = "swarmauri_toolkit_runtime:InspectRuntimeTool"
|
|
82
|
-
ListRuntimeTools = "swarmauri_toolkit_runtime:ListRuntimeTools"
|
|
83
|
-
UnregisterRuntimeTool = "swarmauri_toolkit_runtime:UnregisterRuntimeTool"
|
|
84
|
-
ReplaceRuntimeTool = "swarmauri_toolkit_runtime:ReplaceRuntimeTool"
|
|
85
|
-
|
|
86
89
|
[build-system]
|
|
87
90
|
requires = ["poetry-core>=1.0.0"]
|
|
88
91
|
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
from swarmauri_base.ComponentBase import ComponentBase
|
|
6
|
+
from swarmauri_base.tools.ToolBase import ToolBase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RecipeStep(BaseModel):
|
|
10
|
+
"""Call one installed tool with literals or input references."""
|
|
11
|
+
|
|
12
|
+
tool: str
|
|
13
|
+
arguments: dict[str, Any] = Field(default_factory=dict)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@ComponentBase.register_type(ToolBase, "RecipeTool")
|
|
17
|
+
class RecipeTool(ToolBase):
|
|
18
|
+
"""Run a sequence of existing tool calls through a host dispatcher."""
|
|
19
|
+
|
|
20
|
+
type: Literal["RecipeTool"] = "RecipeTool"
|
|
21
|
+
steps: list[RecipeStep] = Field(min_length=1, max_length=32)
|
|
22
|
+
dispatch: Callable[[str, dict[str, Any]], dict[str, Any]] = Field(
|
|
23
|
+
exclude=True
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def __call__(self, **inputs):
|
|
27
|
+
declared = {parameter.name for parameter in self.parameters}
|
|
28
|
+
required = {
|
|
29
|
+
parameter.name
|
|
30
|
+
for parameter in self.parameters
|
|
31
|
+
if parameter.required
|
|
32
|
+
}
|
|
33
|
+
if inputs.keys() - declared or required - inputs.keys():
|
|
34
|
+
raise ValueError("Recipe inputs do not match its parameters")
|
|
35
|
+
results = []
|
|
36
|
+
for step in self.steps:
|
|
37
|
+
arguments = {
|
|
38
|
+
key: inputs[value["input"]]
|
|
39
|
+
if isinstance(value, dict) and value.keys() == {"input"}
|
|
40
|
+
else value
|
|
41
|
+
for key, value in step.arguments.items()
|
|
42
|
+
}
|
|
43
|
+
result = self.dispatch(step.tool, arguments)
|
|
44
|
+
results.append(result)
|
|
45
|
+
if result.get("status") != "completed":
|
|
46
|
+
break
|
|
47
|
+
return {"steps": results}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from collections.abc import Callable, Mapping
|
|
1
2
|
from typing import Dict, Literal
|
|
2
3
|
|
|
4
|
+
from pydantic import Field, PrivateAttr
|
|
5
|
+
|
|
3
6
|
from swarmauri_base.ComponentBase import ComponentBase
|
|
4
7
|
from swarmauri_base.tools.ToolBase import ToolBase
|
|
5
8
|
from swarmauri_base.toolkits.ToolkitBase import ToolkitBase
|
|
@@ -9,12 +12,16 @@ from .InspectRuntimeTool import InspectRuntimeTool
|
|
|
9
12
|
from .ListRuntimeTools import ListRuntimeTools
|
|
10
13
|
from .UnregisterRuntimeTool import UnregisterRuntimeTool
|
|
11
14
|
from .ReplaceRuntimeTool import ReplaceRuntimeTool
|
|
15
|
+
from .RecipeTool import RecipeTool
|
|
16
|
+
from ._tool_factory import build_tool_from_spec
|
|
12
17
|
|
|
13
18
|
|
|
14
19
|
@ComponentBase.register_type(ToolkitBase, "RuntimeToolkit")
|
|
15
20
|
class RuntimeToolkit(ToolkitBase):
|
|
16
21
|
type: Literal["RuntimeToolkit"] = "RuntimeToolkit"
|
|
17
22
|
tools: Dict[str, ToolBase] = {}
|
|
23
|
+
revision: int = Field(default=0, ge=0)
|
|
24
|
+
_recipe_dispatch: Callable | None = PrivateAttr(default=None)
|
|
18
25
|
|
|
19
26
|
def __init__(self, **kwargs):
|
|
20
27
|
super().__init__(**kwargs)
|
|
@@ -30,7 +37,9 @@ class RuntimeToolkit(ToolkitBase):
|
|
|
30
37
|
}
|
|
31
38
|
return {
|
|
32
39
|
"RegisterRuntimeTool": RegisterRuntimeTool(
|
|
33
|
-
toolkit=self,
|
|
40
|
+
toolkit=self,
|
|
41
|
+
protected_tool_names=protected_tool_names,
|
|
42
|
+
factory=self._build_tool,
|
|
34
43
|
),
|
|
35
44
|
"InspectRuntimeTool": InspectRuntimeTool(toolkit=self),
|
|
36
45
|
"ListRuntimeTools": ListRuntimeTools(toolkit=self),
|
|
@@ -38,6 +47,29 @@ class RuntimeToolkit(ToolkitBase):
|
|
|
38
47
|
toolkit=self, protected_tool_names=protected_tool_names
|
|
39
48
|
),
|
|
40
49
|
"ReplaceRuntimeTool": ReplaceRuntimeTool(
|
|
41
|
-
toolkit=self,
|
|
50
|
+
toolkit=self,
|
|
51
|
+
protected_tool_names=protected_tool_names,
|
|
52
|
+
factory=self._build_tool,
|
|
42
53
|
),
|
|
43
54
|
}
|
|
55
|
+
|
|
56
|
+
def bind_recipe_dispatch(self, dispatch: Callable) -> None:
|
|
57
|
+
"""Configure the application dispatcher for recipe steps."""
|
|
58
|
+
self._recipe_dispatch = dispatch
|
|
59
|
+
|
|
60
|
+
def _build_tool(self, spec):
|
|
61
|
+
if isinstance(spec, Mapping) and spec.get("type") == "RecipeTool":
|
|
62
|
+
if self._recipe_dispatch is None:
|
|
63
|
+
raise ValueError("Recipe dispatch is not configured")
|
|
64
|
+
return RecipeTool.model_validate(
|
|
65
|
+
{**spec, "dispatch": self._recipe_dispatch}
|
|
66
|
+
)
|
|
67
|
+
return build_tool_from_spec(spec)
|
|
68
|
+
|
|
69
|
+
def add_tool(self, tool):
|
|
70
|
+
super().add_tool(tool)
|
|
71
|
+
self.revision += 1
|
|
72
|
+
|
|
73
|
+
def remove_tool(self, tool_name):
|
|
74
|
+
super().remove_tool(tool_name)
|
|
75
|
+
self.revision += 1
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from typing import Any, Dict, List, Literal
|
|
2
|
-
|
|
3
|
-
from pydantic import Field
|
|
4
|
-
|
|
5
|
-
from swarmauri_base.ComponentBase import ComponentBase
|
|
6
|
-
from swarmauri_base.tools.ToolBase import ToolBase
|
|
7
|
-
from swarmauri_base.toolkits.ToolkitBase import ToolkitBase
|
|
8
|
-
from swarmauri_standard.tools.Parameter import Parameter
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@ComponentBase.register_type(ToolBase, "InspectRuntimeTool")
|
|
12
|
-
class InspectRuntimeTool(ToolBase):
|
|
13
|
-
version: str = "0.1.0"
|
|
14
|
-
name: str = "InspectRuntimeTool"
|
|
15
|
-
description: str = "Read one tool from the active toolkit by name."
|
|
16
|
-
type: Literal["InspectRuntimeTool"] = "InspectRuntimeTool"
|
|
17
|
-
toolkit: ToolkitBase | None = Field(default=None, exclude=True, repr=False)
|
|
18
|
-
parameters: List[Parameter] = Field(
|
|
19
|
-
default_factory=lambda: [
|
|
20
|
-
Parameter(
|
|
21
|
-
name="tool_name",
|
|
22
|
-
input_type="string",
|
|
23
|
-
description="Name of the tool to fetch from the toolkit.",
|
|
24
|
-
required=True,
|
|
25
|
-
)
|
|
26
|
-
]
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
def __call__(self, tool_name: str) -> Dict[str, Any]:
|
|
30
|
-
if self.toolkit is None:
|
|
31
|
-
raise ValueError("toolkit is not configured")
|
|
32
|
-
|
|
33
|
-
tool = self.toolkit.get_tool_by_name(tool_name)
|
|
34
|
-
return {
|
|
35
|
-
"tool_name": tool_name,
|
|
36
|
-
"tool_type": tool.type,
|
|
37
|
-
"tool": tool.model_dump(exclude_none=True, by_alias=True),
|
|
38
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
from typing import Any, Dict, List, Literal
|
|
2
|
-
|
|
3
|
-
from pydantic import Field
|
|
4
|
-
|
|
5
|
-
from swarmauri_base.ComponentBase import ComponentBase
|
|
6
|
-
from swarmauri_base.tools.ToolBase import ToolBase
|
|
7
|
-
from swarmauri_base.toolkits.ToolkitBase import ToolkitBase
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@ComponentBase.register_type(ToolBase, "ListRuntimeTools")
|
|
11
|
-
class ListRuntimeTools(ToolBase):
|
|
12
|
-
version: str = "0.1.0"
|
|
13
|
-
name: str = "ListRuntimeTools"
|
|
14
|
-
description: str = (
|
|
15
|
-
"List all tools currently available in the active toolkit."
|
|
16
|
-
)
|
|
17
|
-
type: Literal["ListRuntimeTools"] = "ListRuntimeTools"
|
|
18
|
-
toolkit: ToolkitBase | None = Field(default=None, exclude=True, repr=False)
|
|
19
|
-
parameters: List[Any] = Field(default_factory=list)
|
|
20
|
-
|
|
21
|
-
def __call__(self) -> Dict[str, Any]:
|
|
22
|
-
if self.toolkit is None:
|
|
23
|
-
raise ValueError("toolkit is not configured")
|
|
24
|
-
|
|
25
|
-
tools = {
|
|
26
|
-
name: tool.model_dump(exclude_none=True, by_alias=True)
|
|
27
|
-
for name, tool in self.toolkit.tools.items()
|
|
28
|
-
}
|
|
29
|
-
return {
|
|
30
|
-
"tool_count": len(self.toolkit),
|
|
31
|
-
"tool_names": list(self.toolkit.tools.keys()),
|
|
32
|
-
"tools": tools,
|
|
33
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
from typing import Any, Dict, List, Literal, Mapping
|
|
2
|
-
|
|
3
|
-
from pydantic import Field
|
|
4
|
-
|
|
5
|
-
from swarmauri_base.ComponentBase import ComponentBase
|
|
6
|
-
from swarmauri_base.tools.ToolBase import ToolBase
|
|
7
|
-
from swarmauri_base.toolkits.ToolkitBase import ToolkitBase
|
|
8
|
-
from swarmauri_standard.tools.Parameter import Parameter
|
|
9
|
-
|
|
10
|
-
from ._tool_factory import build_tool_from_spec
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@ComponentBase.register_type(ToolBase, "RegisterRuntimeTool")
|
|
14
|
-
class RegisterRuntimeTool(ToolBase):
|
|
15
|
-
version: str = "0.1.0"
|
|
16
|
-
name: str = "RegisterRuntimeTool"
|
|
17
|
-
description: str = (
|
|
18
|
-
"Create a tool from a serialized tool spec and add it to the "
|
|
19
|
-
"active toolkit."
|
|
20
|
-
)
|
|
21
|
-
type: Literal["RegisterRuntimeTool"] = "RegisterRuntimeTool"
|
|
22
|
-
toolkit: ToolkitBase | None = Field(default=None, exclude=True, repr=False)
|
|
23
|
-
protected_tool_names: set[str] = Field(
|
|
24
|
-
default_factory=set, exclude=True, repr=False
|
|
25
|
-
)
|
|
26
|
-
parameters: List[Parameter] = Field(
|
|
27
|
-
default_factory=lambda: [
|
|
28
|
-
Parameter(
|
|
29
|
-
name="tool_spec",
|
|
30
|
-
input_type="object",
|
|
31
|
-
description=(
|
|
32
|
-
"Serialized tool payload with 'type', 'parameters', and "
|
|
33
|
-
"'__call__' fields."
|
|
34
|
-
),
|
|
35
|
-
required=True,
|
|
36
|
-
)
|
|
37
|
-
]
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
def __call__(
|
|
41
|
-
self, tool_spec: Mapping[str, Any] | ToolBase
|
|
42
|
-
) -> Dict[str, Any]:
|
|
43
|
-
if self.toolkit is None:
|
|
44
|
-
raise ValueError("toolkit is not configured")
|
|
45
|
-
|
|
46
|
-
tool = build_tool_from_spec(tool_spec)
|
|
47
|
-
if tool.name in self.protected_tool_names:
|
|
48
|
-
raise ValueError(
|
|
49
|
-
f"Tool '{tool.name}' is reserved by RuntimeToolkit"
|
|
50
|
-
)
|
|
51
|
-
if tool.name in self.toolkit.tools:
|
|
52
|
-
raise ValueError(
|
|
53
|
-
f"Tool '{tool.name}' already exists in the toolkit"
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
self.toolkit.add_tool(tool)
|
|
57
|
-
return {
|
|
58
|
-
"status": "created",
|
|
59
|
-
"tool_name": tool.name,
|
|
60
|
-
"tool_type": tool.type,
|
|
61
|
-
"tool": tool.model_dump(exclude_none=True, by_alias=True),
|
|
62
|
-
"tool_count": len(self.toolkit),
|
|
63
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
from typing import Any, Dict, List, Literal, Mapping
|
|
2
|
-
|
|
3
|
-
from pydantic import Field
|
|
4
|
-
|
|
5
|
-
from swarmauri_base.ComponentBase import ComponentBase
|
|
6
|
-
from swarmauri_base.tools.ToolBase import ToolBase
|
|
7
|
-
from swarmauri_base.toolkits.ToolkitBase import ToolkitBase
|
|
8
|
-
from swarmauri_standard.tools.Parameter import Parameter
|
|
9
|
-
|
|
10
|
-
from ._tool_factory import build_tool_from_spec
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@ComponentBase.register_type(ToolBase, "ReplaceRuntimeTool")
|
|
14
|
-
class ReplaceRuntimeTool(ToolBase):
|
|
15
|
-
version: str = "0.1.0"
|
|
16
|
-
name: str = "ReplaceRuntimeTool"
|
|
17
|
-
description: str = (
|
|
18
|
-
"Replace an existing non-reserved tool in the active toolkit "
|
|
19
|
-
"with a new serialized tool spec."
|
|
20
|
-
)
|
|
21
|
-
type: Literal["ReplaceRuntimeTool"] = "ReplaceRuntimeTool"
|
|
22
|
-
toolkit: ToolkitBase | None = Field(default=None, exclude=True, repr=False)
|
|
23
|
-
protected_tool_names: set[str] = Field(
|
|
24
|
-
default_factory=set, exclude=True, repr=False
|
|
25
|
-
)
|
|
26
|
-
parameters: List[Parameter] = Field(
|
|
27
|
-
default_factory=lambda: [
|
|
28
|
-
Parameter(
|
|
29
|
-
name="tool_name",
|
|
30
|
-
input_type="string",
|
|
31
|
-
description="Existing tool name to replace.",
|
|
32
|
-
required=True,
|
|
33
|
-
),
|
|
34
|
-
Parameter(
|
|
35
|
-
name="tool_spec",
|
|
36
|
-
input_type="object",
|
|
37
|
-
description=(
|
|
38
|
-
"Serialized replacement tool payload with 'type', "
|
|
39
|
-
"'parameters', and '__call__' fields."
|
|
40
|
-
),
|
|
41
|
-
required=True,
|
|
42
|
-
),
|
|
43
|
-
]
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
def __call__(
|
|
47
|
-
self, tool_name: str, tool_spec: Mapping[str, Any] | ToolBase
|
|
48
|
-
) -> Dict[str, Any]:
|
|
49
|
-
if self.toolkit is None:
|
|
50
|
-
raise ValueError("toolkit is not configured")
|
|
51
|
-
if tool_name in self.protected_tool_names:
|
|
52
|
-
raise ValueError(
|
|
53
|
-
f"Tool '{tool_name}' is reserved by RuntimeToolkit"
|
|
54
|
-
)
|
|
55
|
-
if tool_name not in self.toolkit.tools:
|
|
56
|
-
raise ValueError(f"Tool '{tool_name}' not found in the toolkit")
|
|
57
|
-
|
|
58
|
-
replacement = build_tool_from_spec(tool_spec)
|
|
59
|
-
if (
|
|
60
|
-
replacement.name in self.protected_tool_names
|
|
61
|
-
and replacement.name != tool_name
|
|
62
|
-
):
|
|
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
|
-
)
|
|
73
|
-
|
|
74
|
-
self.toolkit.remove_tool(tool_name)
|
|
75
|
-
self.toolkit.add_tool(replacement)
|
|
76
|
-
return {
|
|
77
|
-
"status": "updated",
|
|
78
|
-
"previous_tool_name": tool_name,
|
|
79
|
-
"tool_name": replacement.name,
|
|
80
|
-
"tool_type": replacement.type,
|
|
81
|
-
"tool": replacement.model_dump(exclude_none=True, by_alias=True),
|
|
82
|
-
"tool_count": len(self.toolkit),
|
|
83
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
from typing import Any, Dict, List, Literal
|
|
2
|
-
|
|
3
|
-
from pydantic import Field
|
|
4
|
-
|
|
5
|
-
from swarmauri_base.ComponentBase import ComponentBase
|
|
6
|
-
from swarmauri_base.tools.ToolBase import ToolBase
|
|
7
|
-
from swarmauri_base.toolkits.ToolkitBase import ToolkitBase
|
|
8
|
-
from swarmauri_standard.tools.Parameter import Parameter
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@ComponentBase.register_type(ToolBase, "UnregisterRuntimeTool")
|
|
12
|
-
class UnregisterRuntimeTool(ToolBase):
|
|
13
|
-
version: str = "0.1.0"
|
|
14
|
-
name: str = "UnregisterRuntimeTool"
|
|
15
|
-
description: str = "Delete a non-reserved tool from the active toolkit."
|
|
16
|
-
type: Literal["UnregisterRuntimeTool"] = "UnregisterRuntimeTool"
|
|
17
|
-
toolkit: ToolkitBase | None = Field(default=None, exclude=True, repr=False)
|
|
18
|
-
protected_tool_names: set[str] = Field(
|
|
19
|
-
default_factory=set, exclude=True, repr=False
|
|
20
|
-
)
|
|
21
|
-
parameters: List[Parameter] = Field(
|
|
22
|
-
default_factory=lambda: [
|
|
23
|
-
Parameter(
|
|
24
|
-
name="tool_name",
|
|
25
|
-
input_type="string",
|
|
26
|
-
description="Name of the tool to remove from the toolkit.",
|
|
27
|
-
required=True,
|
|
28
|
-
)
|
|
29
|
-
]
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
def __call__(self, tool_name: str) -> Dict[str, Any]:
|
|
33
|
-
if self.toolkit is None:
|
|
34
|
-
raise ValueError("toolkit is not configured")
|
|
35
|
-
if tool_name in self.protected_tool_names:
|
|
36
|
-
raise ValueError(
|
|
37
|
-
f"Tool '{tool_name}' is reserved by RuntimeToolkit"
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
self.toolkit.remove_tool(tool_name)
|
|
41
|
-
return {
|
|
42
|
-
"status": "deleted",
|
|
43
|
-
"tool_name": tool_name,
|
|
44
|
-
"tool_count": len(self.toolkit),
|
|
45
|
-
}
|
|
@@ -1,176 +0,0 @@
|
|
|
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,46 +0,0 @@
|
|
|
1
|
-
from typing import Any, Mapping
|
|
2
|
-
|
|
3
|
-
from swarmauri_base.tools.ToolBase import ToolBase
|
|
4
|
-
from swarmauri_standard.tools.Parameter import Parameter
|
|
5
|
-
|
|
6
|
-
from ._dynamic_runtime_tool import DynamicRuntimeTool
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def build_tool_from_spec(tool_spec: Mapping[str, Any] | ToolBase) -> ToolBase:
|
|
10
|
-
if isinstance(tool_spec, ToolBase):
|
|
11
|
-
if not tool_spec.parameters:
|
|
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
|
-
)
|
|
19
|
-
return tool_spec
|
|
20
|
-
if not isinstance(tool_spec, Mapping):
|
|
21
|
-
raise TypeError("tool_spec must be a mapping or ToolBase instance")
|
|
22
|
-
|
|
23
|
-
tool_data = dict(tool_spec)
|
|
24
|
-
tool_type = tool_data.get("type")
|
|
25
|
-
if not isinstance(tool_type, str) or not tool_type:
|
|
26
|
-
raise ValueError(
|
|
27
|
-
"tool_spec must include a non-empty string 'type' field"
|
|
28
|
-
)
|
|
29
|
-
declared_parameters = tool_data.get("parameters")
|
|
30
|
-
if not isinstance(declared_parameters, list) or not declared_parameters:
|
|
31
|
-
raise ValueError(
|
|
32
|
-
"Runtime tool registration requires a non-empty 'parameters' list"
|
|
33
|
-
)
|
|
34
|
-
call_source = tool_data.get("__call__")
|
|
35
|
-
if not isinstance(call_source, str) or not call_source.strip():
|
|
36
|
-
raise ValueError(
|
|
37
|
-
"Runtime tool registration requires a non-empty '__call__' string"
|
|
38
|
-
)
|
|
39
|
-
|
|
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()
|
|
File without changes
|
|
File without changes
|