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.
Files changed (46) hide show
  1. smartify/__init__.py +3 -0
  2. smartify/agents/__init__.py +0 -0
  3. smartify/agents/adapters/__init__.py +13 -0
  4. smartify/agents/adapters/anthropic.py +253 -0
  5. smartify/agents/adapters/openai.py +289 -0
  6. smartify/api/__init__.py +26 -0
  7. smartify/api/auth.py +352 -0
  8. smartify/api/errors.py +380 -0
  9. smartify/api/events.py +345 -0
  10. smartify/api/server.py +992 -0
  11. smartify/cli/__init__.py +1 -0
  12. smartify/cli/main.py +430 -0
  13. smartify/engine/__init__.py +64 -0
  14. smartify/engine/approval.py +479 -0
  15. smartify/engine/orchestrator.py +1365 -0
  16. smartify/engine/scheduler.py +380 -0
  17. smartify/engine/spark.py +294 -0
  18. smartify/guardrails/__init__.py +22 -0
  19. smartify/guardrails/breakers.py +409 -0
  20. smartify/models/__init__.py +61 -0
  21. smartify/models/grid.py +625 -0
  22. smartify/notifications/__init__.py +22 -0
  23. smartify/notifications/webhook.py +556 -0
  24. smartify/state/__init__.py +46 -0
  25. smartify/state/checkpoint.py +558 -0
  26. smartify/state/resume.py +301 -0
  27. smartify/state/store.py +370 -0
  28. smartify/tools/__init__.py +17 -0
  29. smartify/tools/base.py +196 -0
  30. smartify/tools/builtin/__init__.py +79 -0
  31. smartify/tools/builtin/file.py +464 -0
  32. smartify/tools/builtin/http.py +195 -0
  33. smartify/tools/builtin/shell.py +137 -0
  34. smartify/tools/mcp/__init__.py +33 -0
  35. smartify/tools/mcp/adapter.py +157 -0
  36. smartify/tools/mcp/client.py +334 -0
  37. smartify/tools/mcp/registry.py +130 -0
  38. smartify/validator/__init__.py +0 -0
  39. smartify/validator/validate.py +271 -0
  40. smartify/workspace/__init__.py +5 -0
  41. smartify/workspace/manager.py +248 -0
  42. smartify_ai-0.1.0.dist-info/METADATA +201 -0
  43. smartify_ai-0.1.0.dist-info/RECORD +46 -0
  44. smartify_ai-0.1.0.dist-info/WHEEL +4 -0
  45. smartify_ai-0.1.0.dist-info/entry_points.txt +2 -0
  46. 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