Skip to content

Commit e41b92a

Browse files
authored
Merge pull request #136 from tcdent/runtime-errors
Some friendly error messages for runtime errors in `agentstack run`
2 parents 37f3a44 + cef10f0 commit e41b92a

File tree

2 files changed

+75
-8
lines changed

2 files changed

+75
-8
lines changed

agentstack/cli/run.py

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Optional
22
import sys
3+
import traceback
34
from pathlib import Path
45
import importlib.util
56
from dotenv import load_dotenv
@@ -14,6 +15,62 @@
1415
MAIN_MODULE_NAME = "main"
1516

1617

18+
def _format_friendy_error_message(exception: Exception):
19+
"""
20+
Projects will throw various errors, especially on first runs, so we catch
21+
them here and print a more helpful message.
22+
23+
In order to prevent us from having to import all possible backend exceptions
24+
we do string matching on the exception type and traceback contents.
25+
"""
26+
# TODO These end up being pretty framework-specific; consider individual implementations.
27+
COMMON_LLM_ENV_VARS = (
28+
'OPENAI_API_KEY',
29+
'ANTHROPIC_API_KEY',
30+
)
31+
32+
name = exception.__class__.__name__
33+
message = str(exception)
34+
tracebacks = traceback.format_exception(type(exception), exception, exception.__traceback__)
35+
36+
match (name, message, tracebacks):
37+
# The user doesn't have an environment variable set for the LLM provider.
38+
case ('AuthenticationError', m, t) if 'litellm.AuthenticationError' in t[-1]:
39+
variable_name = [k for k in COMMON_LLM_ENV_VARS if k in message] or ["correct"]
40+
return (
41+
"We were unable to connect to the LLM provider. "
42+
f"Ensure your .env file has the {variable_name[0]} variable set."
43+
)
44+
# This happens when the LLM configured for an agent is invalid.
45+
case ('BadRequestError', m, t) if 'LLM Provider NOT provided' in t[-1]:
46+
return (
47+
"An invalid LLM was configured for an agent. "
48+
"Ensure the 'llm' attribute of the agent in the agents.yaml file is in the format <provider>/<model>."
49+
)
50+
# The user has not configured the correct agent name in the tasks.yaml file.
51+
case ('KeyError', m, t) if 'self.tasks_config[task_name]["agent"]' in t[-2]:
52+
return (
53+
f"The agent {message} is not defined in your agents file. "
54+
"Ensure the 'agent' fields in your tasks.yaml correspond to an entry in the agents.yaml file."
55+
)
56+
# The user does not have an agent defined in agents.yaml file, but it does
57+
# exist in the entrypoint code.
58+
case ('KeyError', m, t) if 'config=self.agents_config[' in t[-2]:
59+
return (
60+
f"The agent {message} is not defined in your agents file. "
61+
"Ensure all agents referenced in your code are defined in the agents.yaml file."
62+
)
63+
# The user does not have a task defined in tasks.yaml file, but it does
64+
# exist in the entrypoint code.
65+
case ('KeyError', m, t) if 'config=self.tasks_config[' in t[-2]:
66+
return (
67+
f"The task {message} is not defined in your tasks. "
68+
"Ensure all tasks referenced in your code are defined in the tasks.yaml file."
69+
)
70+
case (_, _, _):
71+
return f"{name}: {message}, {tracebacks[-1]}"
72+
73+
1774
def _import_project_module(path: Path):
1875
"""
1976
Import `main` from the project path.
@@ -32,7 +89,7 @@ def _import_project_module(path: Path):
3289
return project_module
3390

3491

35-
def run_project(command: str = 'run', cli_args: Optional[str] = None):
92+
def run_project(command: str = 'run', debug: bool = False, cli_args: Optional[str] = None):
3693
"""Validate that the project is ready to run and then run it."""
3794
if conf.get_framework() not in frameworks.SUPPORTED_FRAMEWORKS:
3895
print(term_color(f"Framework {conf.get_framework()} is not supported by agentstack.", 'red'))
@@ -55,14 +112,18 @@ def run_project(command: str = 'run', cli_args: Optional[str] = None):
55112
load_dotenv(Path.home() / '.env') # load the user's .env file
56113
load_dotenv(conf.PATH / '.env', override=True) # load the project's .env file
57114

58-
# import src/main.py from the project path
115+
# import src/main.py from the project path and run `command` from the project's main.py
59116
try:
117+
print("Running your agent...")
60118
project_main = _import_project_module(conf.PATH)
119+
getattr(project_main, command)()
61120
except ImportError as e:
62121
print(term_color(f"Failed to import project. Does '{MAIN_FILENAME}' exist?:\n{e}", 'red'))
63122
sys.exit(1)
64-
65-
# run `command` from the project's main.py
66-
# TODO try/except this and print detailed information with a --debug flag
67-
print("Running your agent...")
68-
return getattr(project_main, command)()
123+
except Exception as exception:
124+
if debug:
125+
raise exception
126+
print(term_color("\nAn error occurred while running your project:\n", 'red'))
127+
print(_format_friendy_error_message(exception))
128+
print(term_color("\nRun `agentstack run --debug` for a full traceback.", 'blue'))
129+
sys.exit(1)

agentstack/main.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ def main():
2424
help="Path to the project directory, defaults to current working directory",
2525
dest="project_path",
2626
)
27+
global_parser.add_argument(
28+
"--debug",
29+
help="Print more information when an error occurs",
30+
dest="debug",
31+
action="store_true",
32+
)
2733

2834
parser = argparse.ArgumentParser(
2935
parents=[global_parser], description="AgentStack CLI - The easiest way to build an agent application"
@@ -157,7 +163,7 @@ def main():
157163
elif args.command in ["init", "i"]:
158164
init_project_builder(args.slug_name, args.template, args.wizard)
159165
elif args.command in ["run", "r"]:
160-
run_project(command=args.function, cli_args=extra_args)
166+
run_project(command=args.function, debug=args.debug, cli_args=extra_args)
161167
elif args.command in ['generate', 'g']:
162168
if args.generate_command in ['agent', 'a']:
163169
if not args.llm:

0 commit comments

Comments
 (0)