|
3 | 3 | import sys |
4 | 4 | from typing import Optional |
5 | 5 |
|
6 | | -from .gen_utils import insert_code_after_tag |
| 6 | +from .gen_utils import insert_code_after_tag, string_in_file |
7 | 7 | from ..utils import open_json_file, get_framework, term_color |
8 | 8 | import os |
9 | 9 | import shutil |
10 | 10 | import fileinput |
11 | 11 |
|
| 12 | +TOOL_INIT_FILENAME = "src/tools/__init__.py" |
| 13 | +AGENTSTACK_JSON_FILENAME = "agentstack.json" |
| 14 | + |
12 | 15 |
|
13 | 16 | def add_tool(tool_name: str, path: Optional[str] = None): |
| 17 | + if path: |
| 18 | + path = path.endswith('/') and path or path + '/' |
| 19 | + else: |
| 20 | + path = './' |
14 | 21 | with importlib.resources.path(f'agentstack.tools', 'tools.json') as tools_data_path: |
15 | 22 | tools = open_json_file(tools_data_path) |
16 | 23 | framework = get_framework(path) |
17 | 24 | assert_tool_exists(tool_name, tools) |
| 25 | + agentstack_json = open_json_file(f'{path}{AGENTSTACK_JSON_FILENAME}') |
| 26 | + |
| 27 | + if tool_name in agentstack_json.get('tools', []): |
| 28 | + print(term_color(f'Tool {tool_name} is already installed', 'red')) |
| 29 | + sys.exit(1) |
18 | 30 |
|
19 | 31 | with importlib.resources.path(f'agentstack.tools', f"{tool_name}.json") as tool_data_path: |
20 | 32 | tool_data = open_json_file(tool_data_path) |
21 | 33 |
|
22 | 34 | with importlib.resources.path(f'agentstack.templates.{framework}.tools', f"{tool_name}_tool.py") as tool_file_path: |
23 | | - os.system(tool_data['package']) # Install package |
24 | | - shutil.copy(tool_file_path, f'{path + "/" if path else ""}src/tools/{tool_name}_tool.py') # Move tool from package to project |
| 35 | + if tool_data.get('packages'): |
| 36 | + os.system(f"poetry add {' '.join(tool_data['packages'])}") # Install packages |
| 37 | + shutil.copy(tool_file_path, f'{path}src/tools/{tool_name}_tool.py') # Move tool from package to project |
25 | 38 | add_tool_to_tools_init(tool_data, path) # Export tool from tools dir |
26 | | - add_tool_to_agent_definition(framework, tool_data, path) |
27 | | - insert_code_after_tag(f'{path + "/" if path else ""}.env', '# Tools', [tool_data['env']], next_line=True) # Add env var |
28 | | - insert_code_after_tag(f'{path + "/" if path else ""}.env.example', '# Tools', [tool_data['env']], next_line=True) # Add env var |
29 | | - |
30 | | - agentstack_json = open_json_file(f'{path + "/" if path else ""}agentstack.json') |
| 39 | + add_tool_to_agent_definition(framework, tool_data, path) # Add tool to agent definition |
| 40 | + if tool_data.get('env'): # if the env vars aren't in the .env files, add them |
| 41 | + first_var_name = tool_data['env'].split('=')[0] |
| 42 | + if not string_in_file(f'{path}.env', first_var_name): |
| 43 | + insert_code_after_tag(f'{path}.env', '# Tools', [tool_data['env']], next_line=True) # Add env var |
| 44 | + if not string_in_file(f'{path}.env.example', first_var_name): |
| 45 | + insert_code_after_tag(f'{path}.env.example', '# Tools', [tool_data['env']], next_line=True) # Add env var |
| 46 | + |
31 | 47 | if not agentstack_json.get('tools'): |
32 | 48 | agentstack_json['tools'] = [] |
33 | 49 | agentstack_json['tools'].append(tool_name) |
34 | 50 |
|
35 | | - with open(f'{path + "/" if path else ""}agentstack.json', 'w') as f: |
| 51 | + with open(f'{path}{AGENTSTACK_JSON_FILENAME}', 'w') as f: |
36 | 52 | json.dump(agentstack_json, f, indent=4) |
37 | 53 |
|
38 | 54 | print(term_color(f'🔨 Tool {tool_name} added to agentstack project successfully', 'green')) |
39 | 55 | if tool_data.get('cta'): |
40 | 56 | print(term_color(f'🪩 {tool_data["cta"]}', 'blue')) |
41 | 57 |
|
42 | 58 |
|
43 | | -def add_tool_to_tools_init(tool_data: dict, path: Optional[str] = None): |
44 | | - file_path = f'{path + "/" if path else ""}src/tools/__init__.py' |
| 59 | +def remove_tool(tool_name: str, path: Optional[str] = None): |
| 60 | + if path: |
| 61 | + path = path.endswith('/') and path or path + '/' |
| 62 | + else: |
| 63 | + path = './' |
| 64 | + with importlib.resources.path(f'agentstack.tools', 'tools.json') as tools_data_path: |
| 65 | + tools = open_json_file(tools_data_path) |
| 66 | + framework = get_framework() |
| 67 | + assert_tool_exists(tool_name, tools) |
| 68 | + agentstack_json = open_json_file(f'{path}{AGENTSTACK_JSON_FILENAME}') |
| 69 | + |
| 70 | + if not tool_name in agentstack_json.get('tools', []): |
| 71 | + print(term_color(f'Tool {tool_name} is not installed', 'red')) |
| 72 | + sys.exit(1) |
| 73 | + |
| 74 | + with importlib.resources.path(f'agentstack.tools', f"{tool_name}.json") as tool_data_path: |
| 75 | + tool_data = open_json_file(tool_data_path) |
| 76 | + if tool_data.get('packages'): |
| 77 | + os.system(f"poetry remove {' '.join(tool_data['packages'])}") # Uninstall packages |
| 78 | + os.remove(f'{path}src/tools/{tool_name}_tool.py') |
| 79 | + remove_tool_from_tools_init(tool_data, path) |
| 80 | + remove_tool_from_agent_definition(framework, tool_data, path) |
| 81 | + # We don't remove the .env variables to preserve user data. |
| 82 | + |
| 83 | + agentstack_json['tools'].remove(tool_name) |
| 84 | + with open(f'{path}{AGENTSTACK_JSON_FILENAME}', 'w') as f: |
| 85 | + json.dump(agentstack_json, f, indent=4) |
| 86 | + |
| 87 | + print(term_color(f'🔨 Tool {tool_name}', 'green'), term_color('removed', 'red'), term_color('from agentstack project successfully', 'green')) |
| 88 | + |
| 89 | + |
| 90 | +def _format_tool_import_statement(tool_data: dict): |
| 91 | + return f"from .{tool_data['name']}_tool import {', '.join([tool_name for tool_name in tool_data['tools']])}" |
| 92 | + |
| 93 | + |
| 94 | +def add_tool_to_tools_init(tool_data: dict, path: str = ''): |
| 95 | + file_path = f'{path}{TOOL_INIT_FILENAME}' |
45 | 96 | tag = '# tool import' |
46 | | - code_to_insert = [ |
47 | | - f"from .{tool_data['name']}_tool import {', '.join([tool_name for tool_name in tool_data['tools']])}" |
48 | | - ] |
| 97 | + code_to_insert = [_format_tool_import_statement(tool_data), ] |
49 | 98 | insert_code_after_tag(file_path, tag, code_to_insert, next_line=True) |
50 | 99 |
|
51 | 100 |
|
52 | | -def add_tool_to_agent_definition(framework: str, tool_data: dict, path: Optional[str] = None): |
53 | | - filename = '' |
| 101 | +def remove_tool_from_tools_init(tool_data: dict, path: str = ''): |
| 102 | + """Search for the import statement in the init and remove it.""" |
| 103 | + file_path = f'{path}{TOOL_INIT_FILENAME}' |
| 104 | + import_statement = _format_tool_import_statement(tool_data) |
| 105 | + with fileinput.input(files=file_path, inplace=True) as f: |
| 106 | + for line in f: |
| 107 | + if line.strip() != import_statement: |
| 108 | + print(line, end='') |
| 109 | + |
| 110 | + |
| 111 | +def _framework_filename(framework: str, path: str = ''): |
54 | 112 | if framework == 'crewai': |
55 | | - filename = 'src/crew.py' |
| 113 | + return f'{path}src/crew.py' |
56 | 114 |
|
57 | | - if path: |
58 | | - filename = f'{path}/{filename}' |
| 115 | + print(term_color(f'Unknown framework: {framework}', 'red')) |
| 116 | + sys.exit(1) |
59 | 117 |
|
| 118 | + |
| 119 | +def add_tool_to_agent_definition(framework: str, tool_data: dict, path: str = ''): |
| 120 | + filename = _framework_filename(framework, path) |
60 | 121 | with fileinput.input(files=filename, inplace=True) as f: |
61 | 122 | for line in f: |
62 | 123 | print(line.replace('tools=[', f'tools=[{"*" if tool_data.get("tools_bundled") else ""}tools.{", tools.".join([tool_name for tool_name in tool_data["tools"]])}, '), end='') |
63 | 124 |
|
64 | 125 |
|
| 126 | +def remove_tool_from_agent_definition(framework: str, tool_data: dict, path: str = ''): |
| 127 | + filename = _framework_filename(framework, path) |
| 128 | + with fileinput.input(files=filename, inplace=True) as f: |
| 129 | + for line in f: |
| 130 | + print(line.replace(f'{", ".join([f"tools.{tool_name}" for tool_name in tool_data["tools"]])}, ', ''), end='') |
| 131 | + |
| 132 | + |
65 | 133 | def assert_tool_exists(tool_name: str, tools: dict): |
66 | 134 | for cat in tools.keys(): |
67 | 135 | for tool_dict in tools[cat]: |
68 | 136 | if tool_dict['name'] == tool_name: |
69 | 137 | return |
70 | 138 |
|
71 | | - print(f"\033[31mNo known AgentStack tool: '{tool_name}'\033[0m") |
| 139 | + print(term_color(f'No known agentstack tool: {tool_name}', 'red')) |
72 | 140 | sys.exit(1) |
73 | 141 |
|
0 commit comments