|
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