1+ import os , sys
2+ from typing import Optional , Any , List
13import importlib .resources
24from pathlib import Path
35import json
4- import sys
5- from typing import Optional , List
6+ import shutil
7+ import fileinput
68from pydantic import BaseModel , ValidationError
79
10+ from agentstack .utils import get_package_path
811from .gen_utils import insert_code_after_tag , string_in_file
912from ..utils import open_json_file , get_framework , term_color
10- import os
11- import shutil
12- import fileinput
13+
1314
1415TOOL_INIT_FILENAME = "src/tools/__init__.py"
15- TOOLS_DATA_PATH : Path = importlib .resources .files ('agentstack.tools' ) / 'tools.json'
1616AGENTSTACK_JSON_FILENAME = "agentstack.json"
1717FRAMEWORK_FILENAMES : dict [str , str ] = {
1818 'crewai' : 'src/crew.py' ,
1919}
2020
2121def get_framework_filename (framework : str , path : str = '' ):
22+ if path :
23+ path = path .endswith ('/' ) and path or path + '/'
24+ else :
25+ path = './'
2226 try :
23- return FRAMEWORK_FILENAMES [framework ]
27+ return f" { path } { FRAMEWORK_FILENAMES [framework ]} "
2428 except KeyError :
2529 print (term_color (f'Unknown framework: { framework } ' , 'red' ))
2630 sys .exit (1 )
2731
28- def assert_tool_exists (name : str ):
29- tools_data = open_json_file (TOOLS_DATA_PATH )
30- for category , tools in tools_data .items ():
31- for tool_dict in tools :
32- if tool_dict ['name' ] == name :
33- return
34- print (term_color (f'No known agentstack tool: { name } ' , 'red' ))
35- sys .exit (1 )
36-
3732class ToolConfig (BaseModel ):
3833 name : str
34+ category : str
3935 tools : list [str ]
36+ url : Optional [str ] = None
4037 tools_bundled : bool = False
4138 cta : Optional [str ] = None
42- env : Optional [str ] = None
39+ env : Optional [dict ] = None
4340 packages : Optional [List [str ]] = None
4441 post_install : Optional [str ] = None
4542 post_remove : Optional [str ] = None
4643
4744 @classmethod
4845 def from_tool_name (cls , name : str ) -> 'ToolConfig' :
49- assert_tool_exists (name )
50- return cls .from_json (importlib .resources .files ('agentstack.tools' ) / f'{ name } .json' )
46+ path = get_package_path () / f'tools/{ name } .json'
47+ if not os .path .exists (path ):
48+ print (term_color (f'No known agentstack tool: { name } ' , 'red' ))
49+ sys .exit (1 )
50+ return cls .from_json (path )
5151
5252 @classmethod
5353 def from_json (cls , path : Path ) -> 'ToolConfig' :
@@ -62,6 +62,23 @@ def from_json(cls, path: Path) -> 'ToolConfig':
6262
6363 def get_import_statement (self ) -> str :
6464 return f"from .{ self .name } _tool import { ', ' .join (self .tools )} "
65+
66+ def get_impl_file_path (self , framework : str ) -> Path :
67+ return get_package_path () / f'templates/{ framework } /tools/{ self .name } _tool.py'
68+
69+ def get_all_tool_paths () -> list [Path ]:
70+ paths = []
71+ tools_dir = get_package_path () / 'tools'
72+ for file in tools_dir .iterdir ():
73+ if file .is_file () and file .suffix == '.json' :
74+ paths .append (file )
75+ return paths
76+
77+ def get_all_tool_names () -> list [str ]:
78+ return [path .stem for path in get_all_tool_paths ()]
79+
80+ def get_all_tools () -> list [ToolConfig ]:
81+ return [ToolConfig .from_json (path ) for path in get_all_tool_paths ()]
6582
6683def add_tool (tool_name : str , path : Optional [str ] = None ):
6784 if path :
@@ -77,18 +94,23 @@ def add_tool(tool_name: str, path: Optional[str] = None):
7794 sys .exit (1 )
7895
7996 tool_data = ToolConfig .from_tool_name (tool_name )
80- tool_file_path = importlib . resources . files ( f'agentstack.templates. { framework } .tools' ) / f' { tool_name } _tool.py'
97+ tool_file_path = tool_data . get_impl_file_path ( framework )
8198 if tool_data .packages :
8299 os .system (f"poetry add { ' ' .join (tool_data .packages )} " ) # Install packages
83100 shutil .copy (tool_file_path , f'{ path } src/tools/{ tool_name } _tool.py' ) # Move tool from package to project
84101 add_tool_to_tools_init (tool_data , path ) # Export tool from tools dir
85102 add_tool_to_agent_definition (framework , tool_data , path ) # Add tool to agent definition
86103 if tool_data .env : # if the env vars aren't in the .env files, add them
87- first_var_name = tool_data .env .split ('=' )[0 ]
88- if not string_in_file (f'{ path } .env' , first_var_name ):
89- insert_code_after_tag (f'{ path } .env' , '# Tools' , [tool_data .env ], next_line = True ) # Add env var
90- if not string_in_file (f'{ path } .env.example' , first_var_name ):
91- insert_code_after_tag (f'{ path } .env.example' , '# Tools' , [tool_data .env ], next_line = True ) # Add env var
104+ # tool_data.env is a dict, key is the env var name, value is the value
105+ for var , value in tool_data .env .items ():
106+ env_var = f'{ var } ={ value } '
107+ if not string_in_file (f'{ path } .env' , env_var ):
108+ insert_code_after_tag (f'{ path } .env' , '# Tools' , [env_var , ])
109+ if not string_in_file (f'{ path } .env.example' , env_var ):
110+ insert_code_after_tag (f'{ path } .env.example' , '# Tools' , [env_var , ])
111+
112+ if tool_data .post_install :
113+ os .system (tool_data .post_install )
92114
93115 if tool_data .post_install :
94116 os .system (tool_data .post_install )
0 commit comments