├── .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 | SuperAGI logo 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 | SuperAGI forks 22 | 23 | 24 | 25 | SuperAGI stars 26 | 27 | 28 | 29 | 30 | 31 | 32 | SuperAGI Commits 33 | 34 |

35 | 36 |

Follow SuperAGI

37 | 38 |

39 | 40 | Follow _superAGI 41 | 42 | 43 | 44 | 45 | Join SuperAGI Discord Community 46 | 47 | 48 |

49 | 50 |

Connect with the Creator

51 | 52 |

53 | 54 | Follow ishaanbhola 55 | 56 |

57 | 58 |

Share SuperAGI Repository

59 | 60 |

61 | 62 | 63 | Follow _superAGI 64 | Share on Telegram 65 | 67 | Share on Reddit 68 | Buy Me A Coffee 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 | SuperAGI logo 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 | SuperAGI forks 31 | 32 | 33 | 34 | SuperAGI stars 35 | 36 | 37 | 38 | 39 | 40 | 41 | SuperAGI Commits 42 | 43 |

44 | 45 |

Follow SuperAGI

46 | 47 |

48 | 49 | Follow _superAGI 50 | 51 | 52 | 53 | 54 | Join SuperAGI Discord Community 55 | 56 | 57 |

58 | 59 |

Connect with the Creator

60 | 61 |

62 | 63 | Follow ishaanbhola 64 | 65 |

66 | 67 |

Share SuperAGI Repository

68 | 69 |

70 | 71 | 72 | Follow _superAGI 73 | Share on Telegram 74 | 76 | Share on Reddit 77 | Buy Me A Coffee 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 | --------------------------------------------------------------------------------