Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions agentstack/cli/agentstack_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ def __init__(
template_version: str = "0",
):
self.project_name = clean_input(project_name) if project_name else "myagent"
self.project_slug = (
clean_input(project_slug) if project_slug else self.project_name
)
self.project_slug = clean_input(project_slug) if project_slug else self.project_name
self.description = description
self.author_name = author_name
self.version = version
Expand Down
66 changes: 15 additions & 51 deletions agentstack/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,10 @@ def init_project_builder(

tools = []

log.debug(
f"project_details: {project_details}"
f"framework: {framework}"
f"design: {design}"
)
log.debug(f"project_details: {project_details}" f"framework: {framework}" f"design: {design}")
insert_template(project_details, framework, design, template_data)
for tool_data in tools:
generation.add_tool(
tool_data['name'], agents=tool_data['agents'], path=project_details['name']
)
generation.add_tool(tool_data['name'], agents=tool_data['agents'], path=project_details['name'])

try:
packaging.install(f'{AGENTSTACK_PACKAGE}[{framework}]', path=slug_name)
Expand Down Expand Up @@ -164,9 +158,7 @@ def configure_default_model(path: Optional[str] = None):
)

if model == other_msg: # If the user selects "Other", prompt for a model name
print(
f'A list of available models is available at: "https://docs.litellm.ai/docs/providers"'
)
print(f'A list of available models is available at: "https://docs.litellm.ai/docs/providers"')
model = inquirer.text(message="Enter the model name")

with ConfigFile(path) as agentstack_config:
Expand Down Expand Up @@ -194,9 +186,7 @@ def ask_framework() -> str:
# choices=["CrewAI", "Autogen", "LiteLLM"],
# )

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

return framework

Expand Down Expand Up @@ -231,9 +221,7 @@ def ask_design() -> dict:
while agent_incomplete:
agent = inquirer.prompt(
[
inquirer.Text(
"name", message="What's the name of this agent? (snake_case)"
),
inquirer.Text("name", message="What's the name of this agent? (snake_case)"),
inquirer.Text("role", message="What role does this agent have?"),
inquirer.Text("goal", message="What is the goal of the agent?"),
inquirer.Text("backstory", message="Give your agent a backstory"),
Expand All @@ -254,11 +242,7 @@ def ask_design() -> dict:
print(term_color("Error: Agent name is required - Try again", 'red'))
agent_incomplete = True
elif not is_snake_case(agent['name']):
print(
term_color(
"Error: Agent name must be snake case - Try again", 'red'
)
)
print(term_color("Error: Agent name must be snake case - Try again", 'red'))
else:
agent_incomplete = False

Expand Down Expand Up @@ -286,12 +270,8 @@ def ask_design() -> dict:
while task_incomplete:
task = inquirer.prompt(
[
inquirer.Text(
"name", message="What's the name of this task? (snake_case)"
),
inquirer.Text(
"description", message="Describe the task in more detail"
),
inquirer.Text("name", message="What's the name of this task? (snake_case)"),
inquirer.Text("description", message="Describe the task in more detail"),
inquirer.Text(
"expected_output",
message="What do you expect the result to look like? (ex: A 5 bullet point summary of the email)",
Expand All @@ -307,9 +287,7 @@ def ask_design() -> dict:
if not task['name'] or task['name'] == '':
print(term_color("Error: Task name is required - Try again", 'red'))
elif not is_snake_case(task['name']):
print(
term_color("Error: Task name must be snake case - Try again", 'red')
)
print(term_color("Error: Task name must be snake case - Try again", 'red'))
else:
task_incomplete = False

Expand Down Expand Up @@ -348,14 +326,8 @@ def ask_tools() -> list:
choices=list(tools_data.keys()) + ["~~ Stop adding tools ~~"],
)

tools_in_cat = [
f"{t['name']} - {t['url']}"
for t in tools_data[tool_type]
if t not in tools_to_add
]
tool_selection = inquirer.list_input(
message="Select your tool", choices=tools_in_cat
)
tools_in_cat = [f"{t['name']} - {t['url']}" for t in tools_data[tool_type] if t not in tools_to_add]
tool_selection = inquirer.list_input(message="Select your tool", choices=tools_in_cat)

tools_to_add.append(tool_selection.split(' - ')[0])

Expand All @@ -369,22 +341,16 @@ def ask_tools() -> list:


def ask_project_details(slug_name: Optional[str] = None) -> dict:
name = inquirer.text(
message="What's the name of your project (snake_case)", default=slug_name or ''
)
name = inquirer.text(message="What's the name of your project (snake_case)", default=slug_name or '')

if not is_snake_case(name):
print(term_color("Project name must be snake case", 'red'))
return ask_project_details(slug_name)

questions = inquirer.prompt(
[
inquirer.Text(
"version", message="What's the initial version", default="0.1.0"
),
inquirer.Text(
"description", message="Enter a description for your project"
),
inquirer.Text("version", message="What's the initial version", default="0.1.0"),
inquirer.Text("description", message="Enter a description for your project"),
inquirer.Text("author", message="Who's the author (your name)?"),
]
)
Expand Down Expand Up @@ -452,9 +418,7 @@ def insert_template(
# subprocess.check_output(["git", "init"])
# subprocess.check_output(["git", "add", "."])
except:
print(
"Failed to initialize git repository. Maybe you're already in one? Do this with: git init"
)
print("Failed to initialize git repository. Maybe you're already in one? Do this with: git init")

# TODO: check if poetry is installed and if so, run poetry install in the new directory
# os.system("poetry install")
Expand Down
4 changes: 1 addition & 3 deletions agentstack/generation/agent_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@ def generate_crew_agent(
# Handle None values
role_str = FoldedScalarString(role) if role else FoldedScalarString('')
goals_str = FoldedScalarString(goal) if goal else FoldedScalarString('')
backstory_str = (
FoldedScalarString(backstory) if backstory else FoldedScalarString('')
)
backstory_str = FoldedScalarString(backstory) if backstory else FoldedScalarString('')
model_str = llm if llm else ''

# Add new agent details
Expand Down
8 changes: 2 additions & 6 deletions agentstack/generation/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ class EnvFile:

variables: dict[str, str]

def __init__(
self, path: Union[str, Path, None] = None, filename: str = ENV_FILEMANE
):
def __init__(self, path: Union[str, Path, None] = None, filename: str = ENV_FILEMANE):
self._path = Path(path) if path else Path.cwd()
self._filename = filename
self.read()
Expand Down Expand Up @@ -117,9 +115,7 @@ def parse_line(line):

if os.path.exists(self._path / self._filename):
with open(self._path / self._filename, 'r') as f:
self.variables = dict(
[parse_line(line) for line in f.readlines() if '=' in line]
)
self.variables = dict([parse_line(line) for line in f.readlines() if '=' in line])
else:
self.variables = {}
self._new_variables = {}
Expand Down
12 changes: 3 additions & 9 deletions agentstack/generation/gen_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ def insert_code_after_tag(file_path, tag, code_to_insert, next_line=False):
if tag in line:
# Insert the code block after the tag
indented_code = [
(line[: len(line) - len(line.lstrip())] + code_line + '\n')
for code_line in code_to_insert
(line[: len(line) - len(line.lstrip())] + code_line + '\n') for code_line in code_to_insert
]
lines[index + 1 : index + 1] = indented_code
break
Expand All @@ -40,8 +39,7 @@ def insert_after_tasks(file_path, code_to_insert):
last_task_start = None
for node in ast.walk(module):
if isinstance(node, ast.FunctionDef) and any(
isinstance(deco, ast.Name) and deco.id == 'task'
for deco in node.decorator_list
isinstance(deco, ast.Name) and deco.id == 'task' for deco in node.decorator_list
):
last_task_end = node.end_lineno
last_task_start = node.lineno
Expand Down Expand Up @@ -140,10 +138,6 @@ def get_crew_components(

# If specific types were requested, only return those
if component_type:
return {
k: v
for k, v in components.items()
if CrewComponent(k[:-1]) in component_type
}
return {k: v for k, v in components.items() if CrewComponent(k[:-1]) in component_type}

return components
10 changes: 2 additions & 8 deletions agentstack/generation/task_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,8 @@ def generate_crew_task(
data = {}

# Handle None values
description_str = (
FoldedScalarString(description) if description else FoldedScalarString('')
)
expected_output_str = (
FoldedScalarString(expected_output)
if expected_output
else FoldedScalarString('')
)
description_str = FoldedScalarString(description) if description else FoldedScalarString('')
expected_output_str = FoldedScalarString(expected_output) if expected_output else FoldedScalarString('')
agent_str = FoldedScalarString(agent) if agent else FoldedScalarString('')

# Add new agent details
Expand Down
54 changes: 13 additions & 41 deletions agentstack/generation/tool_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,7 @@ def get_all_tools() -> list[ToolConfig]:
return [ToolConfig.from_json(path) for path in get_all_tool_paths()]


def add_tool(
tool_name: str, path: Optional[str] = None, agents: Optional[List[str]] = []
):
def add_tool(tool_name: str, path: Optional[str] = None, agents: Optional[List[str]] = []):
if path:
path = path.endswith('/') and path or path + '/'
else:
Expand All @@ -116,9 +114,7 @@ def add_tool(

if tool_data.packages:
packaging.install(' '.join(tool_data.packages))
shutil.copy(
tool_file_path, f'{path}src/tools/{tool_name}_tool.py'
) # Move tool from package to project
shutil.copy(tool_file_path, f'{path}src/tools/{tool_name}_tool.py') # Move tool from package to project
add_tool_to_tools_init(tool_data, path) # Export tool from tools dir
add_tool_to_agent_definition(
framework=framework, tool_data=tool_data, path=path, agents=agents
Expand All @@ -138,11 +134,7 @@ def add_tool(
with agentstack_config as config:
config.tools.append(tool_name)

print(
term_color(
f'🔨 Tool {tool_name} added to agentstack project successfully', 'green'
)
)
print(term_color(f'🔨 Tool {tool_name} added to agentstack project successfully', 'green'))
if tool_data.cta:
print(term_color(f'🪩 {tool_data.cta}', 'blue'))

Expand Down Expand Up @@ -224,9 +216,7 @@ def add_tool_to_agent_definition(
)


def remove_tool_from_agent_definition(
framework: str, tool_data: ToolConfig, path: str = ''
):
def remove_tool_from_agent_definition(framework: str, tool_data: ToolConfig, path: str = ''):
modify_agent_tools(
framework=framework,
tool_data=tool_data,
Expand All @@ -239,24 +229,18 @@ def remove_tool_from_agent_definition(

def _create_tool_attribute(tool_name: str, base_name: str = 'tools') -> ast.Attribute:
"""Create an AST node for a tool attribute"""
return ast.Attribute(
value=ast.Name(id=base_name, ctx=ast.Load()), attr=tool_name, ctx=ast.Load()
)
return ast.Attribute(value=ast.Name(id=base_name, ctx=ast.Load()), attr=tool_name, ctx=ast.Load())


def _create_starred_tool(tool_name: str, base_name: str = 'tools') -> ast.Starred:
"""Create an AST node for a starred tool expression"""
return ast.Starred(
value=ast.Attribute(
value=ast.Name(id=base_name, ctx=ast.Load()), attr=tool_name, ctx=ast.Load()
),
value=ast.Attribute(value=ast.Name(id=base_name, ctx=ast.Load()), attr=tool_name, ctx=ast.Load()),
ctx=ast.Load(),
)


def _create_tool_attributes(
tool_names: List[str], base_name: str = 'tools'
) -> List[ast.Attribute]:
def _create_tool_attributes(tool_names: List[str], base_name: str = 'tools') -> List[ast.Attribute]:
"""Create AST nodes for multiple tool attributes"""
return [_create_tool_attribute(name, base_name) for name in tool_names]

Expand All @@ -266,16 +250,12 @@ def _create_tool_nodes(
) -> List[Union[ast.Attribute, ast.Starred]]:
"""Create AST nodes for multiple tool attributes"""
return [
_create_starred_tool(name, base_name)
if is_bundled
else _create_tool_attribute(name, base_name)
_create_starred_tool(name, base_name) if is_bundled else _create_tool_attribute(name, base_name)
for name in tool_names
]


def _is_tool_node_match(
node: ast.AST, tool_name: str, base_name: str = 'tools'
) -> bool:
def _is_tool_node_match(node: ast.AST, tool_name: str, base_name: str = 'tools') -> bool:
"""
Check if an AST node matches a tool reference, regardless of whether it's starred

Expand Down Expand Up @@ -318,19 +298,15 @@ def _process_tools_list(
if operation == 'add':
new_tools = current_tools.copy()
# Add new tools with bundling if specified
new_tools.extend(
_create_tool_nodes(tool_data.tools, tool_data.tools_bundled, base_name)
)
new_tools.extend(_create_tool_nodes(tool_data.tools, tool_data.tools_bundled, base_name))
return new_tools

elif operation == 'remove':
# Filter out tools that match any in the removal list
return [
tool
for tool in current_tools
if not any(
_is_tool_node_match(tool, name, base_name) for name in tool_data.tools
)
if not any(_is_tool_node_match(tool, name, base_name) for name in tool_data.tools)
]

raise ValueError(f"Unsupported operation: {operation}")
Expand Down Expand Up @@ -359,9 +335,7 @@ def _modify_agent_tools(
return node

# Check if this is an agent-decorated function
if not any(
isinstance(d, ast.Name) and d.id == 'agent' for d in node.decorator_list
):
if not any(isinstance(d, ast.Name) and d.id == 'agent' for d in node.decorator_list):
return node

# Find the Return statement and modify tools
Expand All @@ -373,9 +347,7 @@ def _modify_agent_tools(
if kw.arg == 'tools':
if isinstance(kw.value, ast.List):
# Process the tools list
new_tools = _process_tools_list(
kw.value.elts, tool_data, operation, base_name
)
new_tools = _process_tools_list(kw.value.elts, tool_data, operation, base_name)

# Replace with new list
kw.value = ast.List(elts=new_tools, ctx=ast.Load())
Expand Down
Loading
Loading