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
11 changes: 11 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
14 changes: 13 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The best place to engage in conversation about your contribution is in the Issue

1. Clone the repo
2. `poetry install`
3. `pip install -e .`
3. `pip install -e .[dev,test]`
- This will install the CLI locally and in editable mode so you can use `agentstack <command>` to test your latest changes

## Project Structure
Expand Down Expand Up @@ -46,5 +46,17 @@ pip install ruff
ruff format .
```

### Type Checking
AgentStack uses MyPy for type checking. To check types, run:
```bash
mypy agentstack
```

### Pre-Commit Hooks
Ruff and MyPy can be run as pre-commit hooks. To enable these hooks, run:
```bash
pre-commit install
```

## Tests
HAHAHAHAHAHAHA good one
12 changes: 5 additions & 7 deletions agentstack/cli/agentstack_data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
from datetime import datetime
from typing import Optional, Literal, List
from typing import Optional

from agentstack.utils import clean_input, get_version
from agentstack.logger import log
Expand All @@ -9,8 +9,8 @@
class ProjectMetadata:
def __init__(
self,
project_name: str = None,
project_slug: str = None,
project_name: Optional[str] = None,
project_slug: Optional[str] = None,
description: str = "",
author_name: str = "",
version: str = "",
Expand All @@ -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 Expand Up @@ -82,7 +80,7 @@ class FrameworkData:
def __init__(
self,
# name: Optional[Literal["crewai"]] = None
name: str = None, # TODO: better framework handling, Literal or Enum
name: Optional[str] = None, # TODO: better framework handling, Literal or Enum
):
self.name = name

Expand Down
76 changes: 20 additions & 56 deletions agentstack/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,14 @@ 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)
except Exception as e:
except Exception:
print(
term_color(
f"Failed to install dependencies for {slug_name}. Please try again by running `agentstack update`",
Expand Down Expand Up @@ -157,16 +151,14 @@ def configure_default_model(path: Optional[str] = None):
return # Default model already set

print("Project does not have a default model configured.")
other_msg = f"Other (enter a model name)"
other_msg = "Other (enter a model name)"
model = inquirer.list_input(
message="Which model would you like to use?",
choices=PREFERRED_MODELS + [other_msg],
)

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,30 +270,24 @@ 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)",
),
inquirer.List(
"agent",
message="Which agent should be assigned this task?",
choices=[a['name'] for a in agents],
choices=[a['name'] for a in agents], # type: ignore
),
]
)

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 All @@ -408,8 +374,8 @@ def insert_template(
version="0.0.1",
license="MIT",
year=datetime.now().year,
template=template_data['name'] if template_data else None,
template_version=template_data['template_version'] if template_data else None,
template=template_data['name'] if template_data else 'none',
template_version=template_data['template_version'] if template_data else '0',
)

project_structure = ProjectStructure()
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
10 changes: 3 additions & 7 deletions agentstack/generation/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class ConfigFile(BaseModel):
The default model to use when generating agent configurations.
"""

framework: Optional[str] = DEFAULT_FRAMEWORK
framework: str = DEFAULT_FRAMEWORK
tools: list[str] = []
telemetry_opt_out: Optional[bool] = None
default_model: Optional[str] = None
Expand Down 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
Loading
Loading