Skip to content

Commit 1736ff3

Browse files
authored
Merge pull request #20 from AgentOps-AI/fix-tools-wizard
Fix tools wizard
2 parents 59faf42 + 9a95d0d commit 1736ff3

File tree

4 files changed

+120
-108
lines changed

4 files changed

+120
-108
lines changed

agentstack/cli/agentstack_data.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def to_json(self):
6363

6464
class FrameworkData:
6565
def __init__(self,
66-
name: Optional[Literal["crewai"]] = None
66+
# name: Optional[Literal["crewai"]] = None
67+
name: str = None # TODO: better framework handling, Literal or Enum
6768
):
6869
self.name = name
6970

@@ -80,7 +81,8 @@ class CookiecutterData:
8081
def __init__(self,
8182
project_metadata: ProjectMetadata,
8283
structure: ProjectStructure,
83-
framework: Literal["crewai"],
84+
# framework: Literal["crewai"],
85+
framework: str,
8486
):
8587
self.project_metadata = project_metadata
8688
self.framework = framework

agentstack/cli/cli.py

Lines changed: 83 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
import inquirer
99
import os
1010
import webbrowser
11-
import subprocess
1211
import importlib.resources
1312
from cookiecutter.main import cookiecutter
1413

1514
from .agentstack_data import FrameworkData, ProjectMetadata, ProjectStructure, CookiecutterData
1615
from agentstack.logger import log
16+
from .. import generation
17+
from ..utils import open_json_file, term_color
1718

1819

1920
def init_project_builder(slug_name: Optional[str] = None, skip_wizard: bool = False):
@@ -26,27 +27,29 @@ def init_project_builder(slug_name: Optional[str] = None, skip_wizard: bool = Fa
2627
"license": "MIT"
2728
}
2829

29-
stack = {
30-
"framework": "CrewAI" # TODO: if --no-wizard, require a framework flag
31-
}
30+
framework = "CrewAI" # TODO: if --no-wizard, require a framework flag
3231

3332
design = {
3433
'agents': [],
3534
'tasks': []
3635
}
36+
37+
tools = []
3738
else:
3839
welcome_message()
3940
project_details = ask_project_details(slug_name)
4041
welcome_message()
41-
stack = ask_stack()
42+
framework = ask_framework()
4243
design = ask_design()
44+
tools = ask_tools()
4345

4446
log.debug(
4547
f"project_details: {project_details}"
46-
f"stack: {stack}"
48+
f"framework: {framework}"
4749
f"design: {design}"
4850
)
49-
insert_template(project_details, stack, design)
51+
insert_template(project_details, framework, design)
52+
add_tools(tools, project_details['name'])
5053

5154

5255
def welcome_message():
@@ -62,100 +65,37 @@ def welcome_message():
6265
print(border)
6366

6467

65-
def ask_stack():
66-
framework = inquirer.prompt(
67-
[
68-
inquirer.List(
69-
"framework",
70-
message="What agent framework do you want to use?",
71-
choices=["CrewAI", "Autogen", "LiteLLM", "Learn what these are (link)"],
72-
)
73-
]
68+
def ask_framework() -> str:
69+
framework = inquirer.list_input(
70+
message="What agent framework do you want to use?",
71+
choices=["CrewAI", "Autogen", "LiteLLM", "Learn what these are (link)"],
7472
)
75-
if framework["framework"] == "Learn what these are (link)":
73+
74+
if framework == "Learn what these are (link)":
7675
webbrowser.open("https://youtu.be/xvFZjo5PgG0")
77-
framework = inquirer.prompt(
78-
[
79-
inquirer.List(
80-
"framework",
81-
message="What agent framework do you want to use?",
82-
choices=["CrewAI", "Autogen", "LiteLLM"],
83-
)
84-
]
76+
framework = inquirer.list_input(
77+
message="What agent framework do you want to use?",
78+
choices=["CrewAI", "Autogen", "LiteLLM"],
8579
)
8680

87-
while framework["framework"] in ['Autogen', 'LiteLLM']:
88-
print(f"{framework['framework']} support coming soon!!")
89-
framework = inquirer.prompt(
90-
[
91-
inquirer.List(
92-
"framework",
93-
message="What agent framework do you want to use?",
94-
choices=["CrewAI", "Autogen", "LiteLLM"],
95-
)
96-
]
81+
while framework in ['Autogen', 'LiteLLM']:
82+
print(f"{framework} support coming soon!!")
83+
framework = inquirer.list_input(
84+
message="What agent framework do you want to use?",
85+
choices=["CrewAI", "Autogen", "LiteLLM"],
9786
)
9887

9988
print("Congrats! Your project is ready to go! Quickly add features now or skip to do it later.\n\n")
10089

101-
# TODO: add wizard tool selection back in
102-
# use_tools = inquirer.prompt(
103-
# [
104-
# inquirer.Confirm(
105-
# "use_tools",
106-
# message="Do you want to add browsing and RAG tools now? (you can do this later with `agentstack tools add <tool_name>`)",
107-
# )
108-
# ]
109-
# )
110-
111-
use_tools = {'use_tools': False}
112-
113-
# TODO: dynamically load tools #4
114-
browsing_tools = {}
115-
rag_tools = {}
116-
if use_tools["use_tools"]:
117-
browsing_tools = inquirer.prompt(
118-
[
119-
inquirer.Checkbox(
120-
"browsing_tools",
121-
message="Select browsing tools",
122-
choices=[
123-
"browserbasehq",
124-
"firecrawl",
125-
"MultiOn_AI",
126-
"Crawl4AI",
127-
],
128-
)
129-
]
130-
)
131-
132-
rag_tools = inquirer.prompt(
133-
[
134-
inquirer.Checkbox(
135-
"rag",
136-
message="RAG/document loading",
137-
choices=[
138-
"Mem0ai",
139-
"llama_index",
140-
],
141-
)
142-
]
143-
)
144-
145-
return {**framework, **browsing_tools, **rag_tools}
90+
return framework
14691

14792

14893
def ask_design() -> dict:
149-
use_wizard = inquirer.prompt(
150-
[
151-
inquirer.Confirm(
152-
"use_wizard",
153-
message="Would you like to use the CLI wizard to set up agents and tasks?",
154-
)
155-
]
94+
use_wizard = inquirer.confirm(
95+
message="Would you like to use the CLI wizard to set up agents and tasks?",
15696
)
15797

158-
if not use_wizard['use_wizard']:
98+
if not use_wizard:
15999
return {
160100
'agents': [],
161101
'tasks': []
@@ -237,6 +177,47 @@ def ask_design() -> dict:
237177
return {'tasks': tasks, 'agents': agents}
238178

239179

180+
def ask_tools() -> list:
181+
use_tools = inquirer.confirm(
182+
message="Do you want to add agent tools now? (you can do this later with `agentstack tools add <tool_name>`)",
183+
)
184+
185+
if not use_tools:
186+
return []
187+
188+
tools_to_add = []
189+
190+
adding_tools = True
191+
script_dir = os.path.dirname(os.path.abspath(__file__))
192+
tools_json_path = os.path.join(script_dir, '..', 'tools', 'tools.json')
193+
194+
# Load the JSON data
195+
tools_data = open_json_file(tools_json_path)
196+
197+
while adding_tools:
198+
199+
tool_type = inquirer.list_input(
200+
message="What category tool do you want to add?",
201+
choices=list(tools_data.keys()) + ["~~ Stop adding tools ~~"]
202+
)
203+
204+
tools_in_cat = [f"{t['name']} - {t['url']}" for t in tools_data[tool_type] if t not in tools_to_add]
205+
tool_selection = inquirer.list_input(
206+
message="Select your tool",
207+
choices=tools_in_cat
208+
)
209+
210+
tools_to_add.append(tool_selection.split(' - ')[0])
211+
212+
print("Adding tools:")
213+
for t in tools_to_add:
214+
print(f' - {t}')
215+
print('')
216+
adding_tools = inquirer.confirm("Add another tool?")
217+
218+
return tools_to_add
219+
220+
240221
def ask_project_details(slug_name: Optional[str] = None) -> dict:
241222
questions = [
242223
inquirer.Text("name", message="What's the name of your project (snake_case)", default=slug_name or ''),
@@ -258,8 +239,8 @@ def ask_project_details(slug_name: Optional[str] = None) -> dict:
258239
return inquirer.prompt(questions)
259240

260241

261-
def insert_template(project_details: dict, stack: dict, design: dict):
262-
framework = FrameworkData(stack["framework"].lower())
242+
def insert_template(project_details: dict, framework_name: str, design: dict):
243+
framework = FrameworkData(framework_name.lower())
263244
project_metadata = ProjectMetadata(project_name=project_details["name"],
264245
description=project_details["description"],
265246
author_name=project_details["author"],
@@ -273,7 +254,7 @@ def insert_template(project_details: dict, stack: dict, design: dict):
273254

274255
cookiecutter_data = CookiecutterData(project_metadata=project_metadata,
275256
structure=project_structure,
276-
framework=stack["framework"].lower())
257+
framework=framework_name.lower())
277258

278259
with importlib.resources.path(f'agentstack.templates', str(framework.name)) as template_path:
279260
with open(f"{template_path}/cookiecutter.json", "w") as json_file:
@@ -283,6 +264,11 @@ def insert_template(project_details: dict, stack: dict, design: dict):
283264
shutil.copy(
284265
f'{template_path}/{"{{cookiecutter.project_metadata.project_slug}}"}/.env.example',
285266
f'{template_path}/{"{{cookiecutter.project_metadata.project_slug}}"}/.env')
267+
268+
if os.path.isdir(project_details['name']):
269+
print(term_color(f"Directory {template_path} already exists. Please check this and try again", "red"))
270+
return
271+
286272
cookiecutter(str(template_path), no_input=True, extra_context=None)
287273

288274
# TODO: inits a git repo in the directory the command was run in
@@ -310,15 +296,19 @@ def insert_template(project_details: dict, stack: dict, design: dict):
310296
)
311297

312298

299+
def add_tools(tools: list, project_name: str):
300+
for tool in tools:
301+
generation.add_tool(tool, project_name)
302+
303+
313304
def list_tools():
314305
try:
315306
# Determine the path to the tools.json file
316307
script_dir = os.path.dirname(os.path.abspath(__file__))
317308
tools_json_path = os.path.join(script_dir, '..', 'tools', 'tools.json')
318309

319310
# Load the JSON data
320-
with open(tools_json_path, 'r') as f:
321-
tools_data = json.load(f)
311+
tools_data = open_json_file(tools_json_path)
322312

323313
# Display the tools
324314
print("\n\nAvailable AgentStack Tools:")

agentstack/generation/tool_generation.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,49 @@
11
import sys
2+
from typing import Optional
3+
24
from .gen_utils import insert_code_after_tag
35
from ..utils import snake_to_camel, open_json_file, get_framework
46
import os
57
import shutil
68
import fileinput
79

810

9-
def add_tool(tool_name: str):
11+
def add_tool(tool_name: str, path: Optional[str] = None):
1012
script_dir = os.path.dirname(os.path.abspath(__file__))
1113
tools = open_json_file(os.path.join(script_dir, '..', 'tools', 'tools.json'))
12-
framework = get_framework()
14+
framework = get_framework(path)
1315
assert_tool_exists(tool_name, tools)
1416

1517
tool_data = open_json_file(os.path.join(script_dir, '..', 'tools', f'{tool_name}.json'))
1618
tool_file_route = os.path.join(script_dir, '..', 'templates', framework, 'tools', f'{tool_name}.py')
1719

1820
os.system(tool_data['package']) # Install package
19-
shutil.copy(tool_file_route, f'src/tools/{tool_name}.py') # Move tool from package to project
20-
add_tool_to_tools_init(tool_data) # Export tool from tools dir
21-
add_tool_to_agent_definition(framework, tool_data)
22-
insert_code_after_tag('.env', '# Tools', [tool_data['env']], next_line=True) # Add env var
23-
insert_code_after_tag('.env.example', '# Tools', [tool_data['env']], next_line=True) # Add env var
21+
shutil.copy(tool_file_route, f'{path or ""}/src/tools/{tool_name}.py') # Move tool from package to project
22+
add_tool_to_tools_init(tool_data, path) # Export tool from tools dir
23+
add_tool_to_agent_definition(framework, tool_data, path)
24+
insert_code_after_tag(f'{path}/.env', '# Tools', [tool_data['env']], next_line=True) # Add env var
25+
insert_code_after_tag(f'{path}/.env.example', '# Tools', [tool_data['env']], next_line=True) # Add env var
2426

2527
print(f'\033[92m🔨 Tool {tool_name} added to agentstack project successfully\033[0m')
2628

27-
def add_tool_to_tools_init(tool_data: dict):
28-
file_path = 'src/tools/__init__.py'
29+
30+
def add_tool_to_tools_init(tool_data: dict, path: Optional[str] = None):
31+
file_path = f'{path or ""}/src/tools/__init__.py'
2932
tag = '# tool import'
3033
code_to_insert = [
3134
f"from {tool_data['name']} import {', '.join([tool_name for tool_name in tool_data['tools']])}"
3235
]
3336
insert_code_after_tag(file_path, tag, code_to_insert, next_line=True)
3437

3538

36-
def add_tool_to_agent_definition(framework: str, tool_data: dict):
39+
def add_tool_to_agent_definition(framework: str, tool_data: dict, path: Optional[str] = None):
3740
filename = ''
3841
if framework == 'crewai':
3942
filename = 'src/crew.py'
4043

44+
if path:
45+
filename = f'{path}/{filename}'
46+
4147
with fileinput.input(files=filename, inplace=True) as f:
4248
for line in f:
4349
print(line.replace('tools=[', f'tools=[tools.{", tools.".join([tool_name for tool_name in tool_data["tools"]])}, '), end='')

agentstack/utils.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Optional
2+
13
import toml
24
import os
35
import sys
@@ -23,9 +25,12 @@ def verify_agentstack_project():
2325
sys.exit(1)
2426

2527

26-
def get_framework() -> str:
28+
def get_framework(path: Optional[str] = None) -> str:
2729
try:
28-
with open('agentstack.json', 'r') as f:
30+
file_path = 'agentstack.json'
31+
if path is not None:
32+
file_path = path + '/' + file_path
33+
with open(file_path, 'r') as f:
2934
data = json.load(f)
3035
framework = data.get('framework')
3136

@@ -47,7 +52,7 @@ def snake_to_camel(s):
4752
return ''.join(word.title() for word in s.split('_'))
4853

4954

50-
def open_json_file(path):
55+
def open_json_file(path) -> dict:
5156
with open(path, 'r') as f:
5257
data = json.load(f)
5358
return data
@@ -56,3 +61,12 @@ def open_json_file(path):
5661
def clean_input(input_string):
5762
special_char_pattern = re.compile(r'[^a-zA-Z0-9\s_]')
5863
return re.sub(special_char_pattern, '', input_string).lower().replace(' ', '_').replace('-', '_')
64+
65+
66+
def term_color(text: str, color: str) -> str:
67+
if color is 'red':
68+
return "\033[91m{}\033[00m".format(text)
69+
if color is 'green':
70+
return "\033[92m{}\033[00m".format(text)
71+
else:
72+
return text

0 commit comments

Comments
 (0)