smartify-ai 0.1.0__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.
- smartify/__init__.py +3 -0
- smartify/agents/__init__.py +0 -0
- smartify/agents/adapters/__init__.py +13 -0
- smartify/agents/adapters/anthropic.py +253 -0
- smartify/agents/adapters/openai.py +289 -0
- smartify/api/__init__.py +26 -0
- smartify/api/auth.py +352 -0
- smartify/api/errors.py +380 -0
- smartify/api/events.py +345 -0
- smartify/api/server.py +992 -0
- smartify/cli/__init__.py +1 -0
- smartify/cli/main.py +430 -0
- smartify/engine/__init__.py +64 -0
- smartify/engine/approval.py +479 -0
- smartify/engine/orchestrator.py +1365 -0
- smartify/engine/scheduler.py +380 -0
- smartify/engine/spark.py +294 -0
- smartify/guardrails/__init__.py +22 -0
- smartify/guardrails/breakers.py +409 -0
- smartify/models/__init__.py +61 -0
- smartify/models/grid.py +625 -0
- smartify/notifications/__init__.py +22 -0
- smartify/notifications/webhook.py +556 -0
- smartify/state/__init__.py +46 -0
- smartify/state/checkpoint.py +558 -0
- smartify/state/resume.py +301 -0
- smartify/state/store.py +370 -0
- smartify/tools/__init__.py +17 -0
- smartify/tools/base.py +196 -0
- smartify/tools/builtin/__init__.py +79 -0
- smartify/tools/builtin/file.py +464 -0
- smartify/tools/builtin/http.py +195 -0
- smartify/tools/builtin/shell.py +137 -0
- smartify/tools/mcp/__init__.py +33 -0
- smartify/tools/mcp/adapter.py +157 -0
- smartify/tools/mcp/client.py +334 -0
- smartify/tools/mcp/registry.py +130 -0
- smartify/validator/__init__.py +0 -0
- smartify/validator/validate.py +271 -0
- smartify/workspace/__init__.py +5 -0
- smartify/workspace/manager.py +248 -0
- smartify_ai-0.1.0.dist-info/METADATA +201 -0
- smartify_ai-0.1.0.dist-info/RECORD +46 -0
- smartify_ai-0.1.0.dist-info/WHEEL +4 -0
- smartify_ai-0.1.0.dist-info/entry_points.txt +2 -0
- smartify_ai-0.1.0.dist-info/licenses/LICENSE +21 -0
smartify/tools/base.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Base tool interface and registry for Smartify.
|
|
2
|
+
|
|
3
|
+
Tools are the mechanism by which LLM nodes interact with the external world.
|
|
4
|
+
Each tool defines:
|
|
5
|
+
- A unique name
|
|
6
|
+
- A description (for LLM understanding)
|
|
7
|
+
- Input schema (JSON Schema for parameters)
|
|
8
|
+
- An async execute method
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any, Callable, Dict, List, Optional, Type
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class ToolResult:
|
|
21
|
+
"""Result from tool execution."""
|
|
22
|
+
success: bool
|
|
23
|
+
output: Any = None
|
|
24
|
+
error: Optional[str] = None
|
|
25
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
26
|
+
|
|
27
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
28
|
+
return {
|
|
29
|
+
"success": self.success,
|
|
30
|
+
"output": self.output,
|
|
31
|
+
"error": self.error,
|
|
32
|
+
**self.metadata,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Tool(ABC):
|
|
37
|
+
"""Base class for all Smartify tools."""
|
|
38
|
+
|
|
39
|
+
# Tool metadata - override in subclasses
|
|
40
|
+
name: str = "base_tool"
|
|
41
|
+
description: str = "Base tool description"
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def parameters(self) -> Dict[str, Any]:
|
|
46
|
+
"""JSON Schema for tool parameters.
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
{
|
|
50
|
+
"type": "object",
|
|
51
|
+
"properties": {
|
|
52
|
+
"path": {"type": "string", "description": "File path"},
|
|
53
|
+
},
|
|
54
|
+
"required": ["path"]
|
|
55
|
+
}
|
|
56
|
+
"""
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
async def execute(self, **kwargs) -> ToolResult:
|
|
61
|
+
"""Execute the tool with given parameters.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
**kwargs: Parameters matching the schema
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
ToolResult with success status and output/error
|
|
68
|
+
"""
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
def to_openai_format(self) -> Dict[str, Any]:
|
|
72
|
+
"""Convert to OpenAI function calling format."""
|
|
73
|
+
return {
|
|
74
|
+
"type": "function",
|
|
75
|
+
"function": {
|
|
76
|
+
"name": self.name,
|
|
77
|
+
"description": self.description,
|
|
78
|
+
"parameters": self.parameters,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def to_anthropic_format(self) -> Dict[str, Any]:
|
|
83
|
+
"""Convert to Anthropic tool format."""
|
|
84
|
+
return {
|
|
85
|
+
"name": self.name,
|
|
86
|
+
"description": self.description,
|
|
87
|
+
"input_schema": self.parameters,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class ToolRegistry:
|
|
92
|
+
"""Registry for available tools.
|
|
93
|
+
|
|
94
|
+
Manages tool registration, lookup, and execution.
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
registry = ToolRegistry()
|
|
98
|
+
registry.register(ShellTool())
|
|
99
|
+
registry.register(FileReadTool())
|
|
100
|
+
|
|
101
|
+
result = await registry.execute("shell", command="ls -la")
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self):
|
|
105
|
+
self._tools: Dict[str, Tool] = {}
|
|
106
|
+
|
|
107
|
+
def register(self, tool: Tool) -> None:
|
|
108
|
+
"""Register a tool."""
|
|
109
|
+
if tool.name in self._tools:
|
|
110
|
+
logger.warning(f"Overwriting existing tool: {tool.name}")
|
|
111
|
+
self._tools[tool.name] = tool
|
|
112
|
+
logger.debug(f"Registered tool: {tool.name}")
|
|
113
|
+
|
|
114
|
+
def register_all(self, tools: List[Tool]) -> None:
|
|
115
|
+
"""Register multiple tools."""
|
|
116
|
+
for tool in tools:
|
|
117
|
+
self.register(tool)
|
|
118
|
+
|
|
119
|
+
def get(self, name: str) -> Optional[Tool]:
|
|
120
|
+
"""Get a tool by name."""
|
|
121
|
+
return self._tools.get(name)
|
|
122
|
+
|
|
123
|
+
def list_tools(self) -> List[str]:
|
|
124
|
+
"""List all registered tool names."""
|
|
125
|
+
return list(self._tools.keys())
|
|
126
|
+
|
|
127
|
+
def get_all(self) -> List[Tool]:
|
|
128
|
+
"""Get all registered tools."""
|
|
129
|
+
return list(self._tools.values())
|
|
130
|
+
|
|
131
|
+
def to_openai_format(self, names: Optional[List[str]] = None) -> List[Dict[str, Any]]:
|
|
132
|
+
"""Get tools in OpenAI function calling format.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
names: Optional list of tool names to include. If None, includes all.
|
|
136
|
+
"""
|
|
137
|
+
tools = self._tools.values()
|
|
138
|
+
if names:
|
|
139
|
+
tools = [t for t in tools if t.name in names]
|
|
140
|
+
return [t.to_openai_format() for t in tools]
|
|
141
|
+
|
|
142
|
+
def to_anthropic_format(self, names: Optional[List[str]] = None) -> List[Dict[str, Any]]:
|
|
143
|
+
"""Get tools in Anthropic format."""
|
|
144
|
+
tools = self._tools.values()
|
|
145
|
+
if names:
|
|
146
|
+
tools = [t for t in tools if t.name in names]
|
|
147
|
+
return [t.to_anthropic_format() for t in tools]
|
|
148
|
+
|
|
149
|
+
async def execute(self, name: str, **kwargs) -> ToolResult:
|
|
150
|
+
"""Execute a tool by name.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
name: Tool name
|
|
154
|
+
**kwargs: Tool parameters
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
ToolResult
|
|
158
|
+
|
|
159
|
+
Raises:
|
|
160
|
+
ValueError: If tool not found
|
|
161
|
+
"""
|
|
162
|
+
tool = self._tools.get(name)
|
|
163
|
+
if not tool:
|
|
164
|
+
return ToolResult(
|
|
165
|
+
success=False,
|
|
166
|
+
error=f"Unknown tool: {name}. Available: {self.list_tools()}"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
logger.debug(f"Executing tool {name} with args: {kwargs}")
|
|
171
|
+
result = await tool.execute(**kwargs)
|
|
172
|
+
logger.debug(f"Tool {name} result: success={result.success}")
|
|
173
|
+
return result
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.error(f"Tool {name} execution error: {e}")
|
|
176
|
+
return ToolResult(
|
|
177
|
+
success=False,
|
|
178
|
+
error=f"Tool execution failed: {str(e)}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# Global default registry
|
|
183
|
+
_default_registry: Optional[ToolRegistry] = None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_default_registry() -> ToolRegistry:
|
|
187
|
+
"""Get or create the default tool registry."""
|
|
188
|
+
global _default_registry
|
|
189
|
+
if _default_registry is None:
|
|
190
|
+
_default_registry = ToolRegistry()
|
|
191
|
+
return _default_registry
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def register_tool(tool: Tool) -> None:
|
|
195
|
+
"""Register a tool in the default registry."""
|
|
196
|
+
get_default_registry().register(tool)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Builtin tools for Smartify runtime."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from smartify.tools.base import Tool, ToolRegistry
|
|
6
|
+
from smartify.tools.builtin.shell import ShellTool
|
|
7
|
+
from smartify.tools.builtin.file import (
|
|
8
|
+
FileReadTool,
|
|
9
|
+
FileWriteTool,
|
|
10
|
+
FileListTool,
|
|
11
|
+
FileDeleteTool,
|
|
12
|
+
)
|
|
13
|
+
from smartify.tools.builtin.http import HttpTool
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ShellTool",
|
|
18
|
+
"FileReadTool",
|
|
19
|
+
"FileWriteTool",
|
|
20
|
+
"FileListTool",
|
|
21
|
+
"FileDeleteTool",
|
|
22
|
+
"HttpTool",
|
|
23
|
+
"get_builtin_tools",
|
|
24
|
+
"create_builtin_registry",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_builtin_tools(
|
|
29
|
+
base_path: Optional[str] = None,
|
|
30
|
+
enable_shell: bool = True,
|
|
31
|
+
enable_file: bool = True,
|
|
32
|
+
enable_http: bool = True,
|
|
33
|
+
) -> List[Tool]:
|
|
34
|
+
"""Get all builtin tools with optional configuration.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
base_path: Base path for file operations (sandbox)
|
|
38
|
+
enable_shell: Enable shell command tool
|
|
39
|
+
enable_file: Enable file operation tools
|
|
40
|
+
enable_http: Enable HTTP request tool
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List of configured Tool instances
|
|
44
|
+
"""
|
|
45
|
+
tools = []
|
|
46
|
+
|
|
47
|
+
if enable_shell:
|
|
48
|
+
tools.append(ShellTool(working_dir=base_path))
|
|
49
|
+
|
|
50
|
+
if enable_file:
|
|
51
|
+
tools.extend([
|
|
52
|
+
FileReadTool(base_path=base_path),
|
|
53
|
+
FileWriteTool(base_path=base_path),
|
|
54
|
+
FileListTool(base_path=base_path),
|
|
55
|
+
FileDeleteTool(base_path=base_path),
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
if enable_http:
|
|
59
|
+
tools.append(HttpTool())
|
|
60
|
+
|
|
61
|
+
return tools
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def create_builtin_registry(
|
|
65
|
+
base_path: Optional[str] = None,
|
|
66
|
+
**kwargs
|
|
67
|
+
) -> ToolRegistry:
|
|
68
|
+
"""Create a ToolRegistry with all builtin tools.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
base_path: Base path for file operations
|
|
72
|
+
**kwargs: Passed to get_builtin_tools
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Configured ToolRegistry
|
|
76
|
+
"""
|
|
77
|
+
registry = ToolRegistry()
|
|
78
|
+
registry.register_all(get_builtin_tools(base_path=base_path, **kwargs))
|
|
79
|
+
return registry
|