Skip to content
Merged

v0.2 #62

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
85 changes: 85 additions & 0 deletions 0.2-changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# 0.2 Roadmap

# Concepts
- Support only CrewAI for now
- Make the process of starting an agent project as easy as possible

# How do we get there
- Understand current agent design
- What are the use cases? How can they be categorized?

# CLI

## Template Types

### Chatbot
- Conversational
- Customer Support

### Research Agent
- RAG
- Search
- Answers question

### Creative Agent
- Coding Agent
- Writer Agent
- Image Gen

### Copilot
- Understand a problem space
- Support decision-making
- Predict next steps

## Templates
Are the templates part of cookiecutter, then structure is generated using the code-gen scripts?
Or do we manage a collection of independent cookiecutter templates.

The options:

### Codegen
This strategy involves using the existing and new codegen scripts to take the base project template and systematically
build off of it.

This would require a templating structure that can handle complex agent designs.

Pros:
- More easily extensible
- Helps prevent some templates lagging behind in updates

Cons:
- More complex design. Requires more work on codegen and is a more difficult pattern to understand
- Contributions will be more challenging
- There may be some templates that this design may not account for

### Cookiecutter
With this strategy, every template would be its own Cookiecutter template.

Pros:
- Much simpler to understand
- No bounds on complexity or variation between templates

Cons:
- Tech debt will easily grow
- Every update will require a change to every single template
- Requires robust processes for keeping every template updated

## Decision
We'll move forward with the codegen method unless other information comes to light.


### `agentstack i <project_name>`
- Flags
- `--in-place`
- dont place into a subdirectory
- `--template <template_name>`
- generate the project according to a template
- can also consume a url

### `agentstack tools`
- Stays the same

### `agentstack generate`
- `agent`
- `--template <agent_name>`
-
13 changes: 11 additions & 2 deletions agentstack/cli/agentstack_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from datetime import datetime
from typing import Optional, Literal

from agentstack.utils import clean_input
from agentstack.utils import clean_input, get_version
from agentstack.logger import log


Expand All @@ -14,7 +14,9 @@ def __init__(self,
author_name: str = "",
version: str = "",
license: str = "",
year: int = datetime.now().year
year: int = datetime.now().year,
template: str = "none",
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
Expand All @@ -23,6 +25,10 @@ def __init__(self,
self.version = version
self.license = license
self.year = year
self.agentstack_version = get_version()
self.template = template
self.template_version = template_version

log.debug(f"ProjectMetadata: {self.to_dict()}")

def to_dict(self):
Expand All @@ -34,6 +40,9 @@ def to_dict(self):
'version': self.version,
'license': self.license,
'year': self.year,
'agentstack_version': self.agentstack_version,
'template': self.template,
'template_version': self.template_version,
}

def to_json(self):
Expand Down
102 changes: 69 additions & 33 deletions agentstack/cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import json
import shutil
import sys
import time
from datetime import datetime
from typing import Optional
import requests
import itertools

from art import text2art
Expand All @@ -19,17 +21,64 @@
from ..utils import open_json_file, term_color, is_snake_case


def init_project_builder(slug_name: Optional[str] = None, skip_wizard: bool = False):
def init_project_builder(slug_name: Optional[str] = None, template: Optional[str] = None, use_wizard: bool = False):
if slug_name and not is_snake_case(slug_name):
print(term_color("Project name must be snake case", 'red'))
return

if skip_wizard:
if template is not None and use_wizard:
print(term_color("Template and wizard flags cannot be used together", 'red'))
return

template_data = None
if template is not None:
url_start = "https://"
if template[:len(url_start)] == url_start:
# template is a url
response = requests.get(template)
if response.status_code == 200:
template_data = response.json()
else:
print(term_color(f"Failed to fetch template data from {template}. Status code: {response.status_code}", 'red'))
sys.exit(1)
else:
with importlib.resources.path('agentstack.templates.proj_templates', f'{template}.json') as template_path:
if template_path is None:
print(term_color(f"No such template {template} found", 'red'))
sys.exit(1)
template_data = open_json_file(template_path)

if template_data:
project_details = {
"name": slug_name or "new_agentstack_project",
"version": "0.1.0",
"name": slug_name or template_data['name'],
"version": "0.0.1",
"description": template_data['description'],
"author": "Name <Email>",
"license": "MIT"
}
framework = template_data['framework']
design = {
'agents': template_data['agents'],
'tasks': template_data['tasks']
}

tools = template_data['tools']

elif use_wizard:
welcome_message()
project_details = ask_project_details(slug_name)
welcome_message()
framework = ask_framework()
design = ask_design()
tools = ask_tools()

else:
welcome_message()
project_details = {
"name": slug_name or "agentstack_project",
"version": "0.0.1",
"description": "New agentstack project",
"author": "<NAME>",
"author": "Name <Email>",
"license": "MIT"
}

Expand All @@ -41,21 +90,15 @@ def init_project_builder(slug_name: Optional[str] = None, skip_wizard: bool = Fa
}

tools = []
else:
welcome_message()
project_details = ask_project_details(slug_name)
welcome_message()
framework = ask_framework()
design = ask_design()
tools = ask_tools()

log.debug(
f"project_details: {project_details}"
f"framework: {framework}"
f"design: {design}"
)
insert_template(project_details, framework, design)
add_tools(tools, project_details['name'])
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'])


def welcome_message():
Expand Down Expand Up @@ -98,11 +141,9 @@ def ask_framework() -> str:


def ask_design() -> dict:
# use_wizard = inquirer.confirm(
# message="Would you like to use the CLI wizard to set up agents and tasks?",
# )

use_wizard = False
use_wizard = inquirer.confirm(
message="Would you like to use the CLI wizard to set up agents and tasks?",
)

if not use_wizard:
return {
Expand Down Expand Up @@ -202,11 +243,9 @@ def ask_design() -> dict:


def ask_tools() -> list:
# use_tools = inquirer.confirm(
# message="Do you want to add agent tools now? (you can do this later with `agentstack tools add <tool_name>`)",
# )

use_tools = False
use_tools = inquirer.confirm(
message="Do you want to add agent tools now? (you can do this later with `agentstack tools add <tool_name>`)",
)

if not use_tools:
return []
Expand Down Expand Up @@ -262,14 +301,16 @@ def ask_project_details(slug_name: Optional[str] = None) -> dict:
return questions


def insert_template(project_details: dict, framework_name: str, design: dict):
def insert_template(project_details: dict, framework_name: str, design: dict, template_data: Optional[dict] = None):
framework = FrameworkData(framework_name.lower())
project_metadata = ProjectMetadata(project_name=project_details["name"],
description=project_details["description"],
author_name=project_details["author"],
version=project_details["version"],
version="0.0.1",
license="MIT",
year=datetime.now().year)
year=datetime.now().year,
template=template_data['name'] if template_data else None,
template_version=template_data['template_version'] if template_data else None)

project_structure = ProjectStructure()
project_structure.agents = design["agents"]
Expand Down Expand Up @@ -321,16 +362,11 @@ def insert_template(project_details: dict, framework_name: str, design: dict):
)


def add_tools(tools: list, project_name: str):
for tool in tools:
generation.add_tool(tool, project_name)


def list_tools():
# Display the tools
tools = get_all_tools()
curr_category = None

print("\n\nAvailable AgentStack Tools:")
for category, tools in itertools.groupby(tools, lambda x: x.category):
if curr_category != category:
Expand Down
4 changes: 2 additions & 2 deletions agentstack/generation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .agent_generation import generate_agent
from .task_generation import generate_task
from .agent_generation import generate_agent, get_agent_names
from .task_generation import generate_task, get_task_names
from .tool_generation import add_tool, remove_tool
from .files import ConfigFile, EnvFile, CONFIG_FILENAME
13 changes: 9 additions & 4 deletions agentstack/generation/agent_generation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Optional
from typing import Optional, List

from .gen_utils import insert_code_after_tag
from .gen_utils import insert_code_after_tag, get_crew_components, CrewComponent
from agentstack.utils import verify_agentstack_project, get_framework
import os
from ruamel.yaml import YAML
Expand All @@ -21,7 +21,7 @@ def generate_agent(
if not backstory:
backstory = 'Add your backstory here'
if not llm:
llm = 'Add your llm here with format provider/model'
llm = 'openai/gpt-4o'

verify_agentstack_project()

Expand All @@ -45,7 +45,7 @@ def generate_crew_agent(
role: Optional[str] = 'Add your role here',
goal: Optional[str] = 'Add your goal here',
backstory: Optional[str] = 'Add your backstory here',
llm: Optional[str] = 'Add your llm here with format provider/model'
llm: Optional[str] = 'openai/gpt-4o'
):
config_path = os.path.join('src', 'config', 'agents.yaml')

Expand Down Expand Up @@ -99,3 +99,8 @@ def generate_crew_agent(
]

insert_code_after_tag(file_path, tag, code_to_insert)


def get_agent_names(framework: str = 'crewai', path: str = '') -> List[str]:
"""Get only agent names from the crew file"""
return get_crew_components(framework, CrewComponent.AGENT, path)['agents']
Loading