diff --git a/agentstack/cli/agentstack_data.py b/agentstack/cli/agentstack_data.py index 25dd88a3..2eda10aa 100644 --- a/agentstack/cli/agentstack_data.py +++ b/agentstack/cli/agentstack_data.py @@ -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 diff --git a/agentstack/cli/cli.py b/agentstack/cli/cli.py index dc447ce7..56dd2549 100644 --- a/agentstack/cli/cli.py +++ b/agentstack/cli/cli.py @@ -115,16 +115,10 @@ 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) @@ -164,9 +158,7 @@ def configure_default_model(path: Optional[str] = None): ) 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: @@ -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 @@ -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"), @@ -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 @@ -286,12 +270,8 @@ 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)", @@ -307,9 +287,7 @@ def ask_design() -> dict: 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 @@ -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]) @@ -369,9 +341,7 @@ 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')) @@ -379,12 +349,8 @@ def ask_project_details(slug_name: Optional[str] = None) -> dict: 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)?"), ] ) @@ -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") diff --git a/agentstack/generation/agent_generation.py b/agentstack/generation/agent_generation.py index be053cce..8eb865f4 100644 --- a/agentstack/generation/agent_generation.py +++ b/agentstack/generation/agent_generation.py @@ -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 diff --git a/agentstack/generation/files.py b/agentstack/generation/files.py index 7aff5448..04d37b33 100644 --- a/agentstack/generation/files.py +++ b/agentstack/generation/files.py @@ -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() @@ -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 = {} diff --git a/agentstack/generation/gen_utils.py b/agentstack/generation/gen_utils.py index 80ebcf8f..d4a0fbab 100644 --- a/agentstack/generation/gen_utils.py +++ b/agentstack/generation/gen_utils.py @@ -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 @@ -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 @@ -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 diff --git a/agentstack/generation/task_generation.py b/agentstack/generation/task_generation.py index c8a3da35..99df034e 100644 --- a/agentstack/generation/task_generation.py +++ b/agentstack/generation/task_generation.py @@ -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 diff --git a/agentstack/generation/tool_generation.py b/agentstack/generation/tool_generation.py index 20930683..86635d90 100644 --- a/agentstack/generation/tool_generation.py +++ b/agentstack/generation/tool_generation.py @@ -96,9 +96,7 @@ def get_all_tools() -> list[ToolConfig]: return [ToolConfig.from_json(path) for path in get_all_tool_paths()] -def add_tool( - tool_name: str, path: Optional[str] = None, agents: Optional[List[str]] = [] -): +def add_tool(tool_name: str, path: Optional[str] = None, agents: Optional[List[str]] = []): if path: path = path.endswith('/') and path or path + '/' else: @@ -116,9 +114,7 @@ def add_tool( if tool_data.packages: packaging.install(' '.join(tool_data.packages)) - shutil.copy( - tool_file_path, f'{path}src/tools/{tool_name}_tool.py' - ) # Move tool from package to project + shutil.copy(tool_file_path, f'{path}src/tools/{tool_name}_tool.py') # Move tool from package to project add_tool_to_tools_init(tool_data, path) # Export tool from tools dir add_tool_to_agent_definition( framework=framework, tool_data=tool_data, path=path, agents=agents @@ -138,11 +134,7 @@ def add_tool( with agentstack_config as config: config.tools.append(tool_name) - print( - term_color( - f'🔨 Tool {tool_name} added to agentstack project successfully', 'green' - ) - ) + print(term_color(f'🔨 Tool {tool_name} added to agentstack project successfully', 'green')) if tool_data.cta: print(term_color(f'🪩 {tool_data.cta}', 'blue')) @@ -224,9 +216,7 @@ def add_tool_to_agent_definition( ) -def remove_tool_from_agent_definition( - framework: str, tool_data: ToolConfig, path: str = '' -): +def remove_tool_from_agent_definition(framework: str, tool_data: ToolConfig, path: str = ''): modify_agent_tools( framework=framework, tool_data=tool_data, @@ -239,24 +229,18 @@ def remove_tool_from_agent_definition( def _create_tool_attribute(tool_name: str, base_name: str = 'tools') -> ast.Attribute: """Create an AST node for a tool attribute""" - return ast.Attribute( - value=ast.Name(id=base_name, ctx=ast.Load()), attr=tool_name, ctx=ast.Load() - ) + return ast.Attribute(value=ast.Name(id=base_name, ctx=ast.Load()), attr=tool_name, ctx=ast.Load()) def _create_starred_tool(tool_name: str, base_name: str = 'tools') -> ast.Starred: """Create an AST node for a starred tool expression""" return ast.Starred( - value=ast.Attribute( - value=ast.Name(id=base_name, ctx=ast.Load()), attr=tool_name, ctx=ast.Load() - ), + value=ast.Attribute(value=ast.Name(id=base_name, ctx=ast.Load()), attr=tool_name, ctx=ast.Load()), ctx=ast.Load(), ) -def _create_tool_attributes( - tool_names: List[str], base_name: str = 'tools' -) -> List[ast.Attribute]: +def _create_tool_attributes(tool_names: List[str], base_name: str = 'tools') -> List[ast.Attribute]: """Create AST nodes for multiple tool attributes""" return [_create_tool_attribute(name, base_name) for name in tool_names] @@ -266,16 +250,12 @@ def _create_tool_nodes( ) -> List[Union[ast.Attribute, ast.Starred]]: """Create AST nodes for multiple tool attributes""" return [ - _create_starred_tool(name, base_name) - if is_bundled - else _create_tool_attribute(name, base_name) + _create_starred_tool(name, base_name) if is_bundled else _create_tool_attribute(name, base_name) for name in tool_names ] -def _is_tool_node_match( - node: ast.AST, tool_name: str, base_name: str = 'tools' -) -> bool: +def _is_tool_node_match(node: ast.AST, tool_name: str, base_name: str = 'tools') -> bool: """ Check if an AST node matches a tool reference, regardless of whether it's starred @@ -318,9 +298,7 @@ def _process_tools_list( if operation == 'add': new_tools = current_tools.copy() # Add new tools with bundling if specified - new_tools.extend( - _create_tool_nodes(tool_data.tools, tool_data.tools_bundled, base_name) - ) + new_tools.extend(_create_tool_nodes(tool_data.tools, tool_data.tools_bundled, base_name)) return new_tools elif operation == 'remove': @@ -328,9 +306,7 @@ def _process_tools_list( return [ tool for tool in current_tools - if not any( - _is_tool_node_match(tool, name, base_name) for name in tool_data.tools - ) + if not any(_is_tool_node_match(tool, name, base_name) for name in tool_data.tools) ] raise ValueError(f"Unsupported operation: {operation}") @@ -359,9 +335,7 @@ def _modify_agent_tools( return node # Check if this is an agent-decorated function - if not any( - isinstance(d, ast.Name) and d.id == 'agent' for d in node.decorator_list - ): + if not any(isinstance(d, ast.Name) and d.id == 'agent' for d in node.decorator_list): return node # Find the Return statement and modify tools @@ -373,9 +347,7 @@ def _modify_agent_tools( if kw.arg == 'tools': if isinstance(kw.value, ast.List): # Process the tools list - new_tools = _process_tools_list( - kw.value.elts, tool_data, operation, base_name - ) + new_tools = _process_tools_list(kw.value.elts, tool_data, operation, base_name) # Replace with new list kw.value = ast.List(elts=new_tools, ctx=ast.Load()) diff --git a/agentstack/main.py b/agentstack/main.py index 2c32bd20..9cc1333f 100644 --- a/agentstack/main.py +++ b/agentstack/main.py @@ -31,24 +31,16 @@ def main(): subparsers.add_parser("templates", help="View Agentstack templates") # 'init' command - init_parser = subparsers.add_parser( - "init", aliases=["i"], help="Initialize a directory for the project" - ) - init_parser.add_argument( - "slug_name", nargs="?", help="The directory name to place the project in" - ) - init_parser.add_argument( - "--wizard", "-w", action="store_true", help="Use the setup wizard" - ) + init_parser = subparsers.add_parser("init", aliases=["i"], help="Initialize a directory for the project") + init_parser.add_argument("slug_name", nargs="?", help="The directory name to place the project in") + init_parser.add_argument("--wizard", "-w", action="store_true", help="Use the setup wizard") init_parser.add_argument("--template", "-t", help="Agent template to use") # 'run' command _ = subparsers.add_parser("run", aliases=["r"], help="Run your agent") # 'generate' command - generate_parser = subparsers.add_parser( - "generate", aliases=["g"], help="Generate agents or tasks" - ) + generate_parser = subparsers.add_parser("generate", aliases=["g"], help="Generate agents or tasks") # Subparsers under 'generate' generate_subparsers = generate_parser.add_subparsers( @@ -56,9 +48,7 @@ def main(): ) # 'agent' command under 'generate' - agent_parser = generate_subparsers.add_parser( - "agent", aliases=["a"], help="Generate an agent" - ) + agent_parser = generate_subparsers.add_parser("agent", aliases=["a"], help="Generate an agent") agent_parser.add_argument("name", help="Name of the agent") agent_parser.add_argument("--role", "-r", help="Role of the agent") agent_parser.add_argument("--goal", "-g", help="Goal of the agent") @@ -66,31 +56,23 @@ def main(): agent_parser.add_argument("--llm", "-l", help="Language model to use") # 'task' command under 'generate' - task_parser = generate_subparsers.add_parser( - "task", aliases=["t"], help="Generate a task" - ) + task_parser = generate_subparsers.add_parser("task", aliases=["t"], help="Generate a task") task_parser.add_argument("name", help="Name of the task") task_parser.add_argument("--description", "-d", help="Description of the task") - task_parser.add_argument( - "--expected_output", "-e", help="Expected output of the task" - ) + task_parser.add_argument("--expected_output", "-e", help="Expected output of the task") task_parser.add_argument("--agent", "-a", help="Agent associated with the task") # 'tools' command tools_parser = subparsers.add_parser("tools", aliases=["t"], help="Manage tools") # Subparsers under 'tools' - tools_subparsers = tools_parser.add_subparsers( - dest="tools_command", help="Tools commands" - ) + tools_subparsers = tools_parser.add_subparsers(dest="tools_command", help="Tools commands") # 'list' command under 'tools' _ = tools_subparsers.add_parser("list", aliases=["l"], help="List tools") # 'add' command under 'tools' - tools_add_parser = tools_subparsers.add_parser( - "add", aliases=["a"], help="Add a new tool" - ) + tools_add_parser = tools_subparsers.add_parser("add", aliases=["a"], help="Add a new tool") tools_add_parser.add_argument("name", help="Name of the tool to add") tools_add_parser.add_argument( "--agents", "-a", help="Name of agents to add this tool to, comma separated" @@ -98,9 +80,7 @@ def main(): tools_add_parser.add_argument("--agent", help="Name of agent to add this tool to") # 'remove' command under 'tools' - tools_remove_parser = tools_subparsers.add_parser( - "remove", aliases=["r"], help="Remove a tool" - ) + tools_remove_parser = tools_subparsers.add_parser("remove", aliases=["r"], help="Remove a tool") tools_remove_parser.add_argument("name", help="Name of the tool to remove") update = subparsers.add_parser('update', aliases=['u'], help='Check for updates') @@ -133,13 +113,9 @@ def main(): if args.generate_command in ["agent", "a"]: if not args.llm: configure_default_model() - generation.generate_agent( - args.name, args.role, args.goal, args.backstory, args.llm - ) + generation.generate_agent(args.name, args.role, args.goal, args.backstory, args.llm) elif args.generate_command in ["task", "t"]: - generation.generate_task( - args.name, args.description, args.expected_output, args.agent - ) + generation.generate_task(args.name, args.description, args.expected_output, args.agent) else: generate_parser.print_help() elif args.command in ["tools", "t"]: diff --git a/agentstack/utils.py b/agentstack/utils.py index c6e7b274..ce02c9bb 100644 --- a/agentstack/utils.py +++ b/agentstack/utils.py @@ -50,9 +50,7 @@ def get_framework(path: Optional[str] = None) -> str: return framework except FileNotFoundError: - print( - "\033[31mFile agentstack.json does not exist. Are you in the right directory?\033[0m" - ) + print("\033[31mFile agentstack.json does not exist. Are you in the right directory?\033[0m") sys.exit(1) @@ -63,9 +61,7 @@ def get_telemetry_opt_out(path: Optional[str] = None) -> str: agentstack_config = ConfigFile(path) return bool(agentstack_config.telemetry_opt_out) except FileNotFoundError: - print( - "\033[31mFile agentstack.json does not exist. Are you in the right directory?\033[0m" - ) + print("\033[31mFile agentstack.json does not exist. Are you in the right directory?\033[0m") sys.exit(1) @@ -86,12 +82,7 @@ def open_json_file(path) -> dict: def clean_input(input_string): special_char_pattern = re.compile(r'[^a-zA-Z0-9\s_]') - return ( - re.sub(special_char_pattern, '', input_string) - .lower() - .replace(' ', '_') - .replace('-', '_') - ) + return re.sub(special_char_pattern, '', input_string).lower().replace(' ', '_').replace('-', '_') def term_color(text: str, color: str) -> str: diff --git a/pyproject.toml b/pyproject.toml index d97570b0..9994ee93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ exclude = [ "examples", "__init__.py" ] +line-length = 110 [tool.ruff.format] quote-style = "preserve" \ No newline at end of file diff --git a/tests/test_cli_loads.py b/tests/test_cli_loads.py index ce166cb1..16648640 100644 --- a/tests/test_cli_loads.py +++ b/tests/test_cli_loads.py @@ -14,9 +14,7 @@ class TestAgentStackCLI(unittest.TestCase): def run_cli(self, *args): """Helper method to run the CLI with arguments.""" - result = subprocess.run( - [*self.CLI_ENTRY, *args], capture_output=True, text=True - ) + result = subprocess.run([*self.CLI_ENTRY, *args], capture_output=True, text=True) return result def test_version(self): diff --git a/tests/test_generation_files.py b/tests/test_generation_files.py index 435bc745..e6ea52fe 100644 --- a/tests/test_generation_files.py +++ b/tests/test_generation_files.py @@ -92,9 +92,7 @@ def test_write_env(self): env.append_if_new("ENV_VAR100", "value2") # Should be added tmp_data = open(BASE_PATH / "tmp/.env").read() - assert ( - tmp_data == """\nENV_VAR1=value1\nENV_VAR2=value2\nENV_VAR100=value2""" - ) + assert tmp_data == """\nENV_VAR1=value1\nENV_VAR2=value2\nENV_VAR100=value2""" except Exception as e: raise e finally: