├── .gitignore
├── superagi
├── __init__.py
├── tools
│ ├── __init__.py
│ └── base_tool.py
└── types
│ ├── __init__.py
│ └── key_type.py
├── superagi_tools.egg-info
├── dependency_links.txt
├── top_level.txt
├── requires.txt
├── SOURCES.txt
└── PKG-INFO
├── dist
├── superagi_tools-1.0.8.tar.gz
└── superagi_tools-1.0.8-py3-none-any.whl
├── setup.py
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/superagi/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/superagi/tools/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/superagi/types/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/superagi_tools.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/superagi_tools.egg-info/top_level.txt:
--------------------------------------------------------------------------------
1 | superagi
2 |
--------------------------------------------------------------------------------
/superagi_tools.egg-info/requires.txt:
--------------------------------------------------------------------------------
1 | pydantic==1.10.8
2 | PyYAML==6.0
3 |
--------------------------------------------------------------------------------
/dist/superagi_tools-1.0.8.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TransformerOptimus/superAGI-tools-common/HEAD/dist/superagi_tools-1.0.8.tar.gz
--------------------------------------------------------------------------------
/dist/superagi_tools-1.0.8-py3-none-any.whl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TransformerOptimus/superAGI-tools-common/HEAD/dist/superagi_tools-1.0.8-py3-none-any.whl
--------------------------------------------------------------------------------
/superagi_tools.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------
1 | LICENSE
2 | README.md
3 | setup.py
4 | superagi/__init__.py
5 | superagi/tools/__init__.py
6 | superagi/tools/base_tool.py
7 | superagi/types/__init__.py
8 | superagi/types/key_type.py
9 | superagi_tools.egg-info/PKG-INFO
10 | superagi_tools.egg-info/SOURCES.txt
11 | superagi_tools.egg-info/dependency_links.txt
12 | superagi_tools.egg-info/requires.txt
13 | superagi_tools.egg-info/top_level.txt
--------------------------------------------------------------------------------
/superagi/types/key_type.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class ToolConfigKeyType(Enum):
5 | STRING = 'string'
6 | FILE = 'file'
7 | INT = 'int'
8 |
9 | @classmethod
10 | def get_key_type(cls, store):
11 | store = store.upper()
12 | if store in cls.__members__:
13 | return cls[store]
14 | raise ValueError(f"{store} is not a valid key type.")
15 |
16 | def __str__(self):
17 | return self.value
18 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | with open("README.md", "r", encoding="utf-8") as readme_file:
4 | long_description = readme_file.read()
5 |
6 | setup(
7 | name='superagi_tools',
8 | version='1.0.8',
9 | description='superagi-tools is a python library specifically designed for developers working with SuperAGI. The library offers the BaseToolkit and BaseTool classes, requierd for development of custom tools and toolkits for SuperAGI.',
10 | author='superagi',
11 | author_email='mukunda@superagi.com',
12 | packages=find_packages(),
13 | install_requires=['pydantic==1.10.8', 'PyYAML==6.0'],
14 | long_description=long_description, # Include the README.md contents here
15 | long_description_content_type="text/markdown", # Specify the content type
16 |
17 | )
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020 Crown Copyright
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Open-source framework to build, manage and run useful Autonomous AI Agents
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Follow SuperAGI
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Connect with the Creator
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | Share SuperAGI Repository
59 |
60 |
61 |
62 |
63 |
64 |
65 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | ## 🛠️SuperAGI Tools
76 |
77 | `superagi-tools` is a python library specifically designed for developers working with SuperAGI. The library offers the `BaseToolkit` and `BaseTool` classes, requierd for development of custom tools and toolkits for SuperAGI.
78 |
79 | You can install the SuperAGI Tools library through:
80 |
81 | ```pip install superagi-tools```
82 |
83 | **For detailed instructions on building custom tools and toolkits in SuperAGI, please refer to our [comprehensive guide](https://superagi.com/building-your-own-custom-tool-in-superagi/).**
84 |
85 |
86 | ## 📃Documentation
87 |
88 | The official documentation is hosted on [https://superagi.com/docs](https://superagi.com/docs/)
89 |
90 | ## 💁♀️Get Help
91 |
92 | Join SuperAGI Discord for any queries: [https://discord.gg/dXbRe5BHJC](https://discord.gg/dXbRe5BHJC)
93 |
94 | ## 👩💻Discussion and Development
95 | Most development discussions take place on GitHub in the [SuperAGI Repository](https://github.com/TransformerOptimus/SuperAGI)
96 |
97 | https://pypi.org/project/superagi-tools/ Checkout here!
98 |
--------------------------------------------------------------------------------
/superagi_tools.egg-info/PKG-INFO:
--------------------------------------------------------------------------------
1 | Metadata-Version: 2.1
2 | Name: superagi-tools
3 | Version: 1.0.8
4 | Summary: superagi-tools is a python library specifically designed for developers working with SuperAGI. The library offers the BaseToolkit and BaseTool classes, requierd for development of custom tools and toolkits for SuperAGI.
5 | Author: superagi
6 | Author-email: mukunda@superagi.com
7 | Description-Content-Type: text/markdown
8 | License-File: LICENSE
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Open-source framework to build, manage and run useful Autonomous AI Agents
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Follow SuperAGI
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Connect with the Creator
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Share SuperAGI Repository
68 |
69 |
70 |
71 |
72 |
73 |
74 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | ## 🛠️SuperAGI Tools
85 |
86 | `superagi-tools` is a python library specifically designed for developers working with SuperAGI. The library offers the `BaseToolkit` and `BaseTool` classes, requierd for development of custom tools and toolkits for SuperAGI.
87 |
88 | You can install the SuperAGI Tools library through:
89 |
90 | ```pip install superagi-tools```
91 |
92 | **For detailed instructions on building custom tools and toolkits in SuperAGI, please refer to our [comprehensive guide](https://superagi.com/building-your-own-custom-tool-in-superagi/).**
93 |
94 |
95 | ## 📃Documentation
96 |
97 | The official documentation is hosted on [https://superagi.com/docs](https://superagi.com/docs/)
98 |
99 | ## 💁♀️Get Help
100 |
101 | Join SuperAGI Discord for any queries: [https://discord.gg/dXbRe5BHJC](https://discord.gg/dXbRe5BHJC)
102 |
103 | ## 👩💻Discussion and Development
104 | Most development discussions take place on GitHub in the [SuperAGI Repository](https://github.com/TransformerOptimus/SuperAGI)
105 |
106 |
--------------------------------------------------------------------------------
/superagi/tools/base_tool.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | import os
3 | from abc import abstractmethod
4 | from functools import wraps
5 | from inspect import signature
6 | from typing import List
7 | from typing import Optional, Type, Callable, Any, Union, Dict, Tuple
8 |
9 | import yaml
10 | from pydantic import BaseModel, create_model, validate_arguments, Extra
11 |
12 | from superagi.types.key_type import ToolConfigKeyType
13 |
14 |
15 | class SchemaSettings:
16 | """Configuration for the pydantic model."""
17 | extra = Extra.forbid
18 | arbitrary_types_allowed = True
19 |
20 |
21 | def extract_valid_parameters(
22 | inferred_type: Type[BaseModel],
23 | function: Callable,
24 | ) -> dict:
25 | """Get the arguments from a function's signature."""
26 | schema = inferred_type.schema()["properties"]
27 | valid_params = signature(function).parameters
28 | return {param: schema[param] for param in valid_params if param != "run_manager"}
29 |
30 |
31 | def _construct_model_subset(
32 | model_name: str, original_model: BaseModel, required_fields: list
33 | ) -> Type[BaseModel]:
34 | """Create a pydantic model with only a subset of model's fields."""
35 | fields = {
36 | field: (
37 | original_model.__fields__[field].type_,
38 | original_model.__fields__[field].default,
39 | )
40 | for field in required_fields
41 | if field in original_model.__fields__
42 | }
43 | return create_model(model_name, **fields) # type: ignore
44 |
45 |
46 | def create_function_schema(
47 | schema_name: str,
48 | function: Callable,
49 | ) -> Type[BaseModel]:
50 | """Create a pydantic schema from a function's signature."""
51 | validated = validate_arguments(function, config=SchemaSettings) # type: ignore
52 | inferred_type = validated.model # type: ignore
53 | if "run_manager" in inferred_type.__fields__:
54 | del inferred_type.__fields__["run_manager"]
55 | valid_parameters = extract_valid_parameters(inferred_type, function)
56 | return _construct_model_subset(
57 | f"{schema_name}Schema", inferred_type, list(valid_parameters)
58 | )
59 |
60 |
61 | class BaseToolkitConfiguration:
62 |
63 | def get_tool_config(self, key: str, module_dir):
64 | config_path = self.__find_config_file(module_dir)
65 | config = self.__load_config(config_path)
66 | return config.get(key)
67 |
68 | def __find_config_file(self, module_dir):
69 | root_dir = module_dir
70 | while True:
71 | config_path = os.path.join(root_dir, "config.yaml")
72 | if os.path.isfile(config_path):
73 | # Found the config.yaml file in the current directory
74 | return config_path
75 | parent_dir = os.path.dirname(root_dir)
76 | if parent_dir == root_dir:
77 | # Reached the root directory without finding the config.yaml file
78 | raise FileNotFoundError("config.yaml file not found")
79 | root_dir = parent_dir
80 |
81 | def __load_config(self, config_path):
82 | with open(config_path) as file:
83 | config = yaml.safe_load(file)
84 | return config
85 |
86 |
87 | class BaseTool(BaseModel):
88 | name: str = None
89 | description: str
90 | args_schema: Type[BaseModel] = None
91 | permission_required: bool = True
92 | toolkit_config: BaseToolkitConfiguration = BaseToolkitConfiguration()
93 |
94 | class Config:
95 | arbitrary_types_allowed = True
96 |
97 | @property
98 | def args(self):
99 | if self.args_schema is not None:
100 | return self.args_schema.schema()["properties"]
101 | else:
102 | name = self.name
103 | args_schema = create_function_schema(f"{name}Schema", self.execute)
104 | return args_schema.schema()["properties"]
105 |
106 | @abstractmethod
107 | def _execute(self, *args: Any, **kwargs: Any):
108 | pass
109 |
110 | @property
111 | def max_token_limit(self):
112 | return 600
113 |
114 | def _parse_input(
115 | self,
116 | tool_input: Union[str, Dict],
117 | ) -> Union[str, Dict[str, Any]]:
118 | """Convert tool input to pydantic model."""
119 | input_args = self.args_schema
120 | if isinstance(tool_input, str):
121 | if input_args is not None:
122 | key_ = next(iter(input_args.__fields__.keys()))
123 | input_args.validate({key_: tool_input})
124 | return tool_input
125 | else:
126 | if input_args is not None:
127 | result = input_args.parse_obj(tool_input)
128 | return {k: v for k, v in result.dict().items() if k in tool_input}
129 | return tool_input
130 |
131 | def _to_args_and_kwargs(self, tool_input: Union[str, Dict]) -> Tuple[Tuple, Dict]:
132 | # For backwards compatibility, if run_input is a string,
133 | # pass as a positional argument.
134 | if isinstance(tool_input, str):
135 | return (tool_input,), {}
136 | else:
137 | return (), tool_input
138 |
139 | def execute(
140 | self,
141 | tool_input: Union[str, Dict],
142 | **kwargs: Any
143 | ) -> Any:
144 | """Run the tool."""
145 | parsed_input = self._parse_input(tool_input)
146 |
147 | try:
148 | tool_args, tool_kwargs = self._to_args_and_kwargs(parsed_input)
149 | observation = (
150 | self._execute(*tool_args, **tool_kwargs)
151 | )
152 | except (Exception, KeyboardInterrupt) as e:
153 | raise e
154 | return observation
155 |
156 | @classmethod
157 | def from_function(cls, func: Callable, args_schema: Type[BaseModel] = None):
158 | if args_schema:
159 | return cls(description=func.__doc__, args_schema=args_schema)
160 | else:
161 | return cls(description=func.__doc__)
162 |
163 | def get_tool_config(self, key):
164 | caller_frame = inspect.currentframe().f_back
165 | caller_module = inspect.getmodule(caller_frame)
166 | caller_file = inspect.getfile(caller_module)
167 | caller_dir = os.path.dirname(os.path.abspath(caller_file))
168 | return self.toolkit_config.get_tool_config(key=key, module_dir=caller_dir)
169 |
170 |
171 | class FunctionalTool(BaseTool):
172 | name: str = None
173 | description: str
174 | func: Callable
175 | args_schema: Type[BaseModel] = None
176 |
177 | @property
178 | def args(self):
179 | if self.args_schema is not None:
180 | return self.args_schema.schema()["properties"]
181 | else:
182 | name = self.name
183 | args_schema = create_function_schema(f"{name}Schema", self.execute)
184 | return args_schema.schema()["properties"]
185 |
186 | def _execute(self, *args: Any, **kwargs: Any):
187 | return self.func(*args, kwargs)
188 |
189 | @classmethod
190 | def from_function(cls, func: Callable, args_schema: Type[BaseModel] = None):
191 | if args_schema:
192 | return cls(description=func.__doc__, args_schema=args_schema)
193 | else:
194 | return cls(description=func.__doc__)
195 |
196 | def registerTool(cls):
197 | cls.__registerTool__ = True
198 | return cls
199 |
200 |
201 | def tool(*args: Union[str, Callable], return_direct: bool = False,
202 | args_schema: Optional[Type[BaseModel]] = None) -> Callable:
203 | def decorator(func: Callable) -> Callable:
204 | nonlocal args_schema
205 |
206 | tool_instance = FunctionalTool.from_function(func, args_schema)
207 |
208 | @wraps(func)
209 | def wrapper(*tool_args, **tool_kwargs):
210 | if return_direct:
211 | return tool_instance._exec(*tool_args, **tool_kwargs)
212 | else:
213 | return tool_instance
214 |
215 | return wrapper
216 |
217 | if len(args) == 1 and callable(args[0]):
218 | return decorator(args[0])
219 | else:
220 | return decorator
221 |
222 |
223 | class BaseToolkit(BaseModel):
224 | name: str
225 | description: str
226 |
227 | @abstractmethod
228 | def get_tools(self) -> List[BaseTool]:
229 | # Add file related tools object here
230 | pass
231 |
232 | @abstractmethod
233 | def get_env_keys(self) -> List[str]:
234 | # Add file related config keys here
235 | pass
236 |
237 |
238 | class ToolConfiguration:
239 |
240 | def __init__(self, key: str, key_type: str = None, is_required: bool = False, is_secret: bool = False):
241 | self.key = key
242 | if is_secret is None:
243 | self.is_secret = False
244 | elif isinstance(is_secret, bool):
245 | self.is_secret = is_secret
246 | else:
247 | raise ValueError("is_secret should be a boolean value")
248 | if is_required is None:
249 | self.is_required = False
250 | elif isinstance(is_required, bool):
251 | self.is_required = is_required
252 | else:
253 | raise ValueError("is_required should be a boolean value")
254 |
255 | if key_type is None:
256 | self.key_type = ToolConfigKeyType.STRING
257 | elif isinstance(key_type, ToolConfigKeyType):
258 | self.key_type = key_type
259 | else:
260 | raise ValueError("key_type should be string/file/integer")
261 |
--------------------------------------------------------------------------------