Skip to content

Commit e8acff4

Browse files
authored
Merge pull request #259 from tcdent/init-templates
Allow the user to select a built-in template or use an empty one on init
2 parents eed77c7 + 0f0bc6a commit e8acff4

File tree

6 files changed

+50
-21
lines changed

6 files changed

+50
-21
lines changed

agentstack/cli/init.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
import os, sys
22
from typing import Optional
33
from pathlib import Path
4+
import inquirer
5+
from textwrap import shorten
46

57
from agentstack import conf, log
68
from agentstack.exceptions import EnvironmentError
79
from agentstack.utils import is_snake_case
810
from agentstack import packaging
911
from agentstack import frameworks
1012
from agentstack import generation
11-
from agentstack.proj_templates import TemplateConfig
13+
from agentstack.proj_templates import get_all_templates, TemplateConfig
1214

1315
from agentstack.cli import welcome_message
1416
from agentstack.cli.wizard import run_wizard
1517
from agentstack.cli.templates import insert_template
1618

17-
DEFAULT_TEMPLATE_NAME: str = "hello_alex"
18-
1919

2020
def require_uv():
2121
try:
2222
uv_bin = packaging.get_uv_bin()
2323
assert os.path.exists(uv_bin)
2424
except (AssertionError, ImportError):
25-
message = "Error: uv is not installed.\n"
26-
message += "Full installation instructions at: https://docs.astral.sh/uv/getting-started/installation\n"
25+
message = (
26+
"Error: uv is not installed.\n"
27+
"Full installation instructions at: "
28+
"https://docs.astral.sh/uv/getting-started/installation\n"
29+
)
2730
match sys.platform:
2831
case 'linux' | 'darwin':
2932
message += "Hint: run `curl -LsSf https://astral.sh/uv/install.sh | sh`\n"
@@ -32,6 +35,28 @@ def require_uv():
3235
raise EnvironmentError(message)
3336

3437

38+
def select_template(slug_name: str, framework: Optional[str] = None) -> TemplateConfig:
39+
"""Let the user select a template from the ones available."""
40+
templates: list[TemplateConfig] = get_all_templates()
41+
template_names = [shorten(f"⚡️ {t.name} - {t.description}", 80) for t in templates]
42+
43+
empty_msg = "🆕 Empty Project"
44+
template_choice = inquirer.list_input(
45+
message="Do you want to start with a template?",
46+
choices=[empty_msg] + template_names,
47+
)
48+
template_name = template_choice.split("⚡️ ")[1].split(" - ")[0]
49+
50+
if template_name == empty_msg:
51+
return TemplateConfig(
52+
name=slug_name,
53+
description="",
54+
framework=framework or frameworks.DEFAULT_FRAMEWORK,
55+
)
56+
57+
return TemplateConfig.from_template_name(template_name)
58+
59+
3560
def init_project(
3661
slug_name: Optional[str] = None,
3762
template: Optional[str] = None,
@@ -61,18 +86,19 @@ def init_project(
6186

6287
if template and use_wizard:
6388
raise Exception("Template and wizard flags cannot be used together")
64-
89+
90+
welcome_message()
91+
6592
if use_wizard:
6693
log.debug("Initializing new project with wizard.")
6794
template_data = run_wizard(slug_name)
6895
elif template:
6996
log.debug(f"Initializing new project with template: {template}")
7097
template_data = TemplateConfig.from_user_input(template)
7198
else:
72-
log.debug(f"Initializing new project with default template: {DEFAULT_TEMPLATE_NAME}")
73-
template_data = TemplateConfig.from_template_name(DEFAULT_TEMPLATE_NAME)
99+
log.debug("Initializing new project with template selection.")
100+
template_data = select_template(slug_name, framework)
74101

75-
welcome_message()
76102
log.notify("🦾 Creating a new AgentStack project...")
77103
log.info(f"Using project directory: {conf.PATH.absolute()}")
78104

@@ -81,15 +107,15 @@ def init_project(
81107
if not framework in frameworks.SUPPORTED_FRAMEWORKS:
82108
raise Exception(f"Framework '{framework}' is not supported.")
83109
log.info(f"Using framework: {framework}")
84-
110+
85111
# copy the project skeleton, create a virtual environment, and install dependencies
86112
# project template is populated before the venv is created so we have a working directory
87113
insert_template(name=slug_name, template=template_data, framework=framework)
88114
log.info("Creating virtual environment...")
89115
packaging.create_venv()
90116
log.info("Installing dependencies...")
91117
packaging.install_project()
92-
118+
93119
# now we can interact with the project and add Agents, Tasks, and Tools
94120
# we allow dependencies to be installed along with these, so the project must
95121
# be fully initialized first.

agentstack/frameworks/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
LANGGRAPH,
2323
OPENAI_SWARM,
2424
]
25+
DEFAULT_FRAMEWORK = CREWAI
2526

2627

2728
class FrameworkModule(Protocol):

agentstack/proj_templates.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,15 +196,15 @@ class Node(pydantic.BaseModel):
196196

197197
name: str
198198
description: str
199-
template_version: Literal[4]
199+
template_version: Literal[4] = CURRENT_VERSION
200200
framework: str
201-
method: str
201+
method: str = "sequential"
202202
manager_agent: Optional[str] = None
203-
agents: list[Agent]
204-
tasks: list[Task]
205-
tools: list[Tool]
206-
graph: list[list[Node]]
207-
inputs: dict[str, str] = {}
203+
agents: list[Agent] = pydantic.Field(default_factory=list)
204+
tasks: list[Task] = pydantic.Field(default_factory=list)
205+
tools: list[Tool] = pydantic.Field(default_factory=list)
206+
graph: list[list[Node]] = pydantic.Field(default_factory=list)
207+
inputs: dict[str, str] = pydantic.Field(default_factory=dict)
208208

209209
@pydantic.field_validator('graph')
210210
@classmethod

agentstack/templates/proj_templates/hello_alex.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hello_alex",
3-
"description": "This is the start of your AgentStack project.",
3+
"description": "A simple example that opens the README and offers suggestions.",
44
"template_version": 1,
55
"framework": "crewai",
66
"agents": [{

tests/test_cli_init.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import Path
55
import shutil
66
from cli_test_utils import run_cli
7+
from agentstack.proj_templates import get_all_templates
78

89
BASE_PATH = Path(__file__).parent
910

@@ -19,8 +20,9 @@ def setUp(self):
1920
def tearDown(self):
2021
shutil.rmtree(self.project_dir, ignore_errors=True)
2122

22-
def test_init_command(self):
23+
@parameterized.expand([(template.name, ) for template in get_all_templates()])
24+
def test_init_command(self, template_name: str):
2325
"""Test the 'init' command to create a project directory."""
24-
result = run_cli('init', 'test_project')
26+
result = run_cli('init', 'test_project', '--template', template_name)
2527
self.assertEqual(result.returncode, 0)
2628
self.assertTrue((self.project_dir / 'test_project').exists())

0 commit comments

Comments
 (0)