Skip to content

Commit 754b3c5

Browse files
authored
Merge pull request #206 from tcdent/langgraph
LangGraph support, baby!
2 parents 57a8cf0 + 0467a79 commit 754b3c5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+3312
-610
lines changed

agentstack/__init__.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,42 @@
99
from pathlib import Path
1010
from agentstack import conf
1111
from agentstack.utils import get_framework
12+
from agentstack.agents import get_agent
13+
from agentstack.tasks import get_task
1214
from agentstack.inputs import get_inputs
1315
from agentstack import frameworks
1416

1517
___all___ = [
16-
"conf",
17-
"tools",
18-
"get_tags",
19-
"get_framework",
20-
"get_inputs",
18+
"conf",
19+
"agent",
20+
"task",
21+
"tools",
22+
"get_tags",
23+
"get_framework",
24+
"get_agent",
25+
"get_task",
26+
"get_inputs",
2127
]
2228

29+
def agent(func):
30+
"""
31+
The `agent` decorator is used to mark a method that implements an Agent.
32+
"""
33+
def wrap(*args, **kwargs):
34+
"""Does not alter the function's behavior; this is just a marker."""
35+
return func(*args, **kwargs)
36+
return wrap
37+
38+
39+
def task(func):
40+
"""
41+
The `task` decorator is used to mark a method that implements a Task.
42+
"""
43+
def wrap(*args, **kwargs):
44+
"""Does not alter the function's behavior; this is just a marker."""
45+
return func(*args, **kwargs)
46+
return wrap
47+
2348

2449
def get_tags() -> list[str]:
2550
"""

agentstack/_tools/__init__.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ class ToolConfig(pydantic.BaseModel):
3333
@classmethod
3434
def from_tool_name(cls, name: str) -> 'ToolConfig':
3535
path = TOOLS_DIR / name / TOOLS_CONFIG_FILENAME
36-
if not os.path.exists(path): # TODO raise exceptions and handle message/exit in cli
37-
print(term_color(f'No known agentstack tool: {name}', 'red'))
38-
sys.exit(1)
36+
if not os.path.exists(path):
37+
raise ValidationError(f'No known agentstack tool: {name}')
3938
return cls.from_json(path)
4039

4140
@classmethod
@@ -44,11 +43,10 @@ def from_json(cls, path: Path) -> 'ToolConfig':
4443
try:
4544
return cls(**data)
4645
except pydantic.ValidationError as e:
47-
# TODO raise exceptions and handle message/exit in cli
48-
print(term_color(f"Error validating tool config JSON: \n{path}", 'red'))
46+
error_str = "Error validating tool config:\n"
4947
for error in e.errors():
50-
print(f"{' '.join([str(loc) for loc in error['loc']])}: {error['msg']}")
51-
sys.exit(1)
48+
error_str += f"{' '.join([str(loc) for loc in error['loc']])}: {error['msg']}\n"
49+
raise ValidationError(f"Error loading tool from {path}.\n{error_str}")
5250

5351
@property
5452
def type(self) -> type:

agentstack/agents.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
from ruamel.yaml import YAML, YAMLError
66
from ruamel.yaml.scalarstring import FoldedScalarString
77
from agentstack import conf, log
8+
from agentstack import frameworks
89
from agentstack.exceptions import ValidationError
910

1011

1112
AGENTS_FILENAME: Path = Path("src/config/agents.yaml")
13+
AGENTS_PROMPT_TPL: str = "You are {role}. {backstory}\nYour personal goal is: {goal}"
1214

1315
yaml = YAML()
1416
yaml.preserve_quotes = True # Preserve quotes in existing data
@@ -67,6 +69,23 @@ def __init__(self, name: str):
6769
error_str += f"{' '.join([str(loc) for loc in error['loc']])}: {error['msg']}\n"
6870
raise ValidationError(f"Error loading agent {name} from {filename}.\n{error_str}")
6971

72+
@property
73+
def provider(self) -> str:
74+
return frameworks.parse_llm(self.llm)[0]
75+
76+
@property
77+
def model(self) -> str:
78+
return frameworks.parse_llm(self.llm)[1]
79+
80+
@property
81+
def prompt(self) -> str:
82+
"""Concatenate the prompts for role, goal, and backstory."""
83+
return AGENTS_PROMPT_TPL.format(**{
84+
'role': self.role,
85+
'goal': self.goal,
86+
'backstory': self.backstory,
87+
})
88+
7089
def model_dump(self, *args, **kwargs) -> dict:
7190
dump = super().model_dump(*args, **kwargs)
7291
dump.pop('name') # name is the key, so keep it out of the data
@@ -106,3 +125,9 @@ def get_all_agent_names() -> list[str]:
106125

107126
def get_all_agents() -> list[AgentConfig]:
108127
return [AgentConfig(name) for name in get_all_agent_names()]
128+
129+
130+
def get_agent(name: str) -> Optional[AgentConfig]:
131+
"""Get an agent configuration by name."""
132+
return AgentConfig(name)
133+

agentstack/cli/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
from .cli import init_project_builder, configure_default_model, export_template, welcome_message
1+
from .cli import configure_default_model, welcome_message, get_validated_input
22
from .init import init_project
3-
from .tools import list_tools, add_tool
3+
from .wizard import run_wizard
44
from .run import run_project
5+
from .tools import list_tools, add_tool
6+
from .templates import insert_template, export_template
7+

agentstack/cli/agentstack_data.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def __init__(
5858
):
5959
self.agents = []
6060
self.tasks = []
61+
self.graph = []
6162
self.inputs = {}
6263
self.method = method
6364
self.manager_agent = manager_agent
@@ -68,6 +69,9 @@ def add_agent(self, agent):
6869
def add_task(self, task):
6970
self.tasks.append(task)
7071

72+
def add_edge(self, edge):
73+
self.graph.append(edge)
74+
7175
def set_inputs(self, inputs):
7276
self.inputs = inputs
7377

@@ -77,6 +81,7 @@ def to_dict(self):
7781
'manager_agent': self.manager_agent,
7882
'agents': self.agents,
7983
'tasks': self.tasks,
84+
'graph': self.graph,
8085
'inputs': self.inputs,
8186
}
8287

0 commit comments

Comments
 (0)