Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ A list of all tools can be found [on our docs](https://docs.agentstack.sh/tools/
Adding tools is as simple as

```bash
agentstack tools add <tool_name>
agentstack tools add
```

## Running Your Agent
Expand Down
3 changes: 2 additions & 1 deletion agentstack/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .cli import init_project_builder, list_tools, configure_default_model, export_template
from .cli import init_project_builder, configure_default_model, export_template
from .tools import list_tools, add_tool
from .run import run_project
22 changes: 0 additions & 22 deletions agentstack/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

import json
import shutil
import itertools

from art import text2art
import inquirer
from cookiecutter.main import cookiecutter
Expand All @@ -22,7 +20,6 @@
from agentstack import conf
from agentstack.conf import ConfigFile
from agentstack.utils import get_package_path
from agentstack.tools import get_all_tools
from agentstack.generation.files import ProjectFile
from agentstack import frameworks
from agentstack import generation
Expand Down Expand Up @@ -440,25 +437,6 @@ def insert_template(
)


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:
print(f"\n{category}:")
curr_category = category
for tool in tools:
print(" - ", end='')
print(term_color(f"{tool.name}", 'blue'), end='')
print(f": {tool.url if tool.url else 'AgentStack default tool'}")

print("\n\n✨ Add a tool with: agentstack tools add <tool_name>")
print(" https://docs.agentstack.sh/tools/core")


def export_template(output_filename: str):
"""
Export the current project as a template.
Expand Down
69 changes: 69 additions & 0 deletions agentstack/cli/tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from typing import Optional
import itertools
import inquirer
from agentstack.utils import term_color
from agentstack import generation
from agentstack.tools import get_all_tools
from agentstack.agents import get_all_agents


def list_tools():
"""
List all available tools by category.
"""
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:
print(f"\n{category}:")
curr_category = category
for tool in tools:
print(" - ", end='')
print(term_color(f"{tool.name}", 'blue'), end='')
print(f": {tool.url if tool.url else 'AgentStack default tool'}")

print("\n\n✨ Add a tool with: agentstack tools add <tool_name>")
print(" https://docs.agentstack.sh/tools/core")


def add_tool(tool_name: Optional[str], agents=Optional[list[str]]):
"""
Add a tool to the user's project.
If no tool name is provided:
- prompt the user to select a tool
- prompt the user to select one or more agents
If a tool name is provided:
- add the tool to the user's project
- add the tool to the specified agents or all agents if none are specified
"""
if not tool_name:
# ask the user for the tool name
tools_list = [
inquirer.List(
"tool_name",
message="Select a tool to add to your project",
choices=[tool.name for tool in get_all_tools()],
)
]
try:
tool_name = inquirer.prompt(tools_list)['tool_name']
except TypeError:
return # user cancelled the prompt

# ask the user for the agents to add the tool to
agents_list = [
inquirer.Checkbox(
"agents",
message="Select which agents to make the tool available to",
choices=[agent.name for agent in get_all_agents()],
)
]
try:
agents = inquirer.prompt(agents_list)['agents']
except TypeError:
return # user cancelled the prompt

assert tool_name # appease type checker
generation.add_tool(tool_name, agents=agents)
30 changes: 20 additions & 10 deletions agentstack/frameworks/crewai.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,17 +152,27 @@ def add_agent_tools(self, agent_name: str, tool: ToolConfig):
if method is None:
raise ValidationError(f"`@agent` method `{agent_name}` does not exist in {ENTRYPOINT}")

new_tool_nodes: set[ast.expr] = set()
for tool_name in tool.tools:
# This prefixes the tool name with the 'tools' module
node: ast.expr = asttools.create_attribute('tools', tool_name)
if tool.tools_bundled: # Splat the variable if it's bundled
node = ast.Starred(value=node, ctx=ast.Load())
new_tool_nodes.add(node)

existing_node: ast.List = self.get_agent_tools(agent_name)
elts: set[ast.expr] = set(existing_node.elts) | new_tool_nodes
new_node = ast.List(elts=list(elts), ctx=ast.Load())
existing_elts: list[ast.expr] = existing_node.elts

new_tool_nodes: list[ast.expr] = []
for tool_name in tool.tools:
# TODO there is definitely a better way to do this. We can't use
# a `set` becasue the ast nodes are unique objects.
_found = False
for elt in existing_elts:
if str(asttools.get_node_value(elt)) == tool_name:
_found = True
break # skip if the tool is already in the list

if not _found:
# This prefixes the tool name with the 'tools' module
node: ast.expr = asttools.create_attribute('tools', tool_name)
if tool.tools_bundled: # Splat the variable if it's bundled
node = ast.Starred(value=node, ctx=ast.Load())
existing_elts.append(node)

new_node = ast.List(elts=existing_elts, ctx=ast.Load())
start, end = self.get_node_range(existing_node)
self.edit_node_range(start, end, new_node)

Expand Down
54 changes: 27 additions & 27 deletions agentstack/generation/tool_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,46 +80,46 @@ def remove_import_for_tool(self, tool: ToolConfig, framework: str):

def add_tool(tool_name: str, agents: Optional[list[str]] = []):
agentstack_config = ConfigFile()
tool = ToolConfig.from_tool_name(tool_name)

if tool_name in agentstack_config.tools:
print(term_color(f'Tool {tool_name} is already installed', 'red'))
sys.exit(1)
print(term_color(f'Tool {tool_name} is already installed', 'blue'))
else: # handle install
tool_file_path = tool.get_impl_file_path(agentstack_config.framework)

tool = ToolConfig.from_tool_name(tool_name)
tool_file_path = tool.get_impl_file_path(agentstack_config.framework)
if tool.packages:
packaging.install(' '.join(tool.packages))

if tool.packages:
packaging.install(' '.join(tool.packages))
# Move tool from package to project
shutil.copy(tool_file_path, conf.PATH / f'src/tools/{tool.module_name}.py')

# Move tool from package to project
shutil.copy(tool_file_path, conf.PATH / f'src/tools/{tool.module_name}.py')
try: # Edit the user's project tool init file to include the tool
with ToolsInitFile(conf.PATH / TOOLS_INIT_FILENAME) as tools_init:
tools_init.add_import_for_tool(tool, agentstack_config.framework)
except ValidationError as e:
print(term_color(f"Error adding tool:\n{e}", 'red'))

try: # Edit the user's project tool init file to include the tool
with ToolsInitFile(conf.PATH / TOOLS_INIT_FILENAME) as tools_init:
tools_init.add_import_for_tool(tool, agentstack_config.framework)
except ValidationError as e:
print(term_color(f"Error adding tool:\n{e}", 'red'))
if tool.env: # add environment variables which don't exist
with EnvFile() as env:
for var, value in tool.env.items():
env.append_if_new(var, value)
with EnvFile(".env.example") as env:
for var, value in tool.env.items():
env.append_if_new(var, value)

if tool.post_install:
os.system(tool.post_install)

with agentstack_config as config:
config.tools.append(tool.name)

# Edit the framework entrypoint file to include the tool in the agent definition
if not agents: # If no agents are specified, add the tool to all agents
agents = frameworks.get_agent_names()
for agent_name in agents:
print(f'Adding tool {tool.name} to agent {agent_name}')
frameworks.add_tool(tool, agent_name)

if tool.env: # add environment variables which don't exist
with EnvFile() as env:
for var, value in tool.env.items():
env.append_if_new(var, value)
with EnvFile(".env.example") as env:
for var, value in tool.env.items():
env.append_if_new(var, value)

if tool.post_install:
os.system(tool.post_install)

with agentstack_config as config:
config.tools.append(tool.name)

print(term_color(f'🔨 Tool {tool.name} added to agentstack project successfully', 'green'))
if tool.cta:
print(term_color(f'🪩 {tool.cta}', 'blue'))
Expand Down
5 changes: 3 additions & 2 deletions agentstack/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from agentstack import conf
from agentstack.cli import (
init_project_builder,
add_tool,
list_tools,
configure_default_model,
run_project,
Expand Down Expand Up @@ -120,7 +121,7 @@ def main():
tools_add_parser = tools_subparsers.add_parser(
"add", aliases=["a"], help="Add a new tool", parents=[global_parser]
)
tools_add_parser.add_argument("name", help="Name of the tool to add")
tools_add_parser.add_argument("name", help="Name of the tool to add", nargs="?")
tools_add_parser.add_argument(
"--agents", "-a", help="Name of agents to add this tool to, comma separated"
)
Expand Down Expand Up @@ -179,7 +180,7 @@ def main():
elif args.tools_command in ["add", "a"]:
agents = [args.agent] if args.agent else None
agents = args.agents.split(",") if args.agents else agents
generation.add_tool(args.name, agents=agents)
add_tool(args.name, agents)
elif args.tools_command in ["remove", "r"]:
generation.remove_tool(args.name)
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This will automatically create a new agent in the `agents.yaml` config as well a

Similarly, tasks can be created with `agentstack g t <tool_name>`

Add tools with `agentstack tools add <tool_name>` and view tools available with `agentstack tools list`
Add tools with `agentstack tools add` and view tools available with `agentstack tools list`

## How to use your Crew
In this directory, run `poetry install`
Expand Down
7 changes: 6 additions & 1 deletion docs/tools/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ description: 'Giving your agents tools should be easy'

Once you find the right tool for your use-case, install it with simply
```bash
agentstack tools add <tool_name>
agentstack tools add
```

You can also specify a tool, and one or more agents to install it to:
```bash
agentstack tools add <tool_name> --agents=<agent_name1>,<agent_name2>
```
Loading