Skip to content
Closed
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
5 changes: 4 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ repos:
hooks:
- id: ruff
args: [--fix]
exclude: ^(agentstack/templates/|examples/|tests/fixtures/).*
- id: ruff-format
exclude: ^(agentstack/templates/|examples/|tests/fixtures/).*
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
- id: mypy
exclude: ^(agentstack/templates/|examples/|tests/fixtures/).*
1 change: 0 additions & 1 deletion agentstack/agents.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from typing import Optional
import os
from pathlib import Path
import pydantic
Expand Down
10 changes: 6 additions & 4 deletions agentstack/cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import Optional
import os, sys
import os
import sys
import time
from datetime import datetime
from pathlib import Path

import json
import shutil
Expand Down Expand Up @@ -413,8 +413,10 @@ def insert_template(
pass
# 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")
except Exception as e:
print(
f"Failed to initialize git repository ({str(e)}). 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")
Expand Down
12 changes: 6 additions & 6 deletions agentstack/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from agentstack.exceptions import ValidationError
from agentstack import inputs
from agentstack import frameworks
from agentstack.utils import term_color, get_framework
from agentstack.utils import term_color

MAIN_FILENAME: Path = Path("src/main.py")
MAIN_MODULE_NAME = "main"
Expand All @@ -35,34 +35,34 @@ def _format_friendy_error_message(exception: Exception):

match (name, message, tracebacks):
# The user doesn't have an environment variable set for the LLM provider.
case ('AuthenticationError', m, t) if 'litellm.AuthenticationError' in t[-1]:
case ('AuthenticationError', _, t) if 'litellm.AuthenticationError' in t[-1]:
variable_name = [k for k in COMMON_LLM_ENV_VARS if k in message] or ["correct"]
return (
"We were unable to connect to the LLM provider. "
f"Ensure your .env file has the {variable_name[0]} variable set."
)
# This happens when the LLM configured for an agent is invalid.
case ('BadRequestError', m, t) if 'LLM Provider NOT provided' in t[-1]:
case ('BadRequestError', _, t) if 'LLM Provider NOT provided' in t[-1]:
return (
"An invalid LLM was configured for an agent. "
"Ensure the 'llm' attribute of the agent in the agents.yaml file is in the format <provider>/<model>."
)
# The user has not configured the correct agent name in the tasks.yaml file.
case ('KeyError', m, t) if 'self.tasks_config[task_name]["agent"]' in t[-2]:
case ('KeyError', _, t) if 'self.tasks_config[task_name]["agent"]' in t[-2]:
return (
f"The agent {message} is not defined in your agents file. "
"Ensure the 'agent' fields in your tasks.yaml correspond to an entry in the agents.yaml file."
)
# The user does not have an agent defined in agents.yaml file, but it does
# exist in the entrypoint code.
case ('KeyError', m, t) if 'config=self.agents_config[' in t[-2]:
case ('KeyError', _, tracebacks) if 'config=self.agents_config[' in tracebacks[-2]:
return (
f"The agent {message} is not defined in your agents file. "
"Ensure all agents referenced in your code are defined in the agents.yaml file."
)
# The user does not have a task defined in tasks.yaml file, but it does
# exist in the entrypoint code.
case ('KeyError', m, t) if 'config=self.tasks_config[' in t[-2]:
case ('KeyError', _, tracebacks) if 'config=self.tasks_config[' in tracebacks[-2]:
return (
f"The task {message} is not defined in your tasks. "
"Ensure all tasks referenced in your code are defined in the tasks.yaml file."
Expand Down
2 changes: 1 addition & 1 deletion agentstack/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Optional, Union
import os, sys
import os
import json
from pathlib import Path
from pydantic import BaseModel
Expand Down
4 changes: 1 addition & 3 deletions agentstack/frameworks/crewai.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,9 @@ def add_agent_tools(self, agent_name: str, tool: ToolConfig):
method = asttools.find_method(self.get_agent_methods(), agent_name)
if method is None:
raise ValidationError(f"`@agent` method `{agent_name}` does not exist in {ENTRYPOINT}")

existing_node: ast.List = self.get_agent_tools(agent_name)
existing_elts: list[ast.expr] = existing_node.elts

new_tool_nodes: list[ast.expr] = []
# Add new tool nodes to existing elements
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.
Expand Down
1 change: 0 additions & 1 deletion agentstack/generation/agent_generation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import sys
from typing import Optional
from pathlib import Path
from agentstack.exceptions import ValidationError
from agentstack.conf import ConfigFile
from agentstack import frameworks
Expand Down
5 changes: 2 additions & 3 deletions agentstack/generation/files.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from typing import Optional, Union
import os, sys
from pathlib import Path
import os
import sys

if sys.version_info >= (3, 11):
import tomllib
Expand Down
1 change: 0 additions & 1 deletion agentstack/generation/task_generation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import sys
from typing import Optional
from pathlib import Path
from agentstack.exceptions import ValidationError
from agentstack import frameworks
from agentstack.utils import verify_agentstack_project
Expand Down
4 changes: 2 additions & 2 deletions agentstack/generation/tool_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ def add_import_for_tool(self, tool: ToolConfig, framework: str):

try:
last_import = asttools.get_all_imports(self.tree)[-1]
start, end = self.get_node_range(last_import)
_, end = self.get_node_range(last_import)
except IndexError:
start, end = 0, 0 # No imports in the file
end = 0 # No imports in the file

import_statement = tool.get_import_statement(framework)
self.edit_node_range(end, end, f"\n{import_statement}")
Expand Down
1 change: 0 additions & 1 deletion agentstack/inputs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from typing import Optional
import os
from pathlib import Path
from ruamel.yaml import YAML, YAMLError
Expand Down
4 changes: 2 additions & 2 deletions agentstack/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def main():
parents=[global_parser],
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
--input-<key>=VALUE Specify inputs to be passed to the run.
--input-<key>=VALUE Specify inputs to be passed to the run.
These will override the inputs in the project's inputs.yaml file.
Examples: --input-topic=Sports --input-content-type=News
''',
Expand Down Expand Up @@ -138,7 +138,7 @@ def main():
)
export_parser.add_argument('filename', help='The name of the file to export to')

update = subparsers.add_parser('update', aliases=['u'], help='Check for updates', parents=[global_parser])
_ = subparsers.add_parser('update', aliases=['u'], help='Check for updates', parents=[global_parser])

# Parse known args and store unknown args in extras; some commands use them later on
args, extra_args = parser.parse_known_args()
Expand Down
6 changes: 3 additions & 3 deletions agentstack/proj_templates.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Optional, Literal
import os, sys
from typing import Literal
import os
from pathlib import Path
import pydantic
import requests
Expand Down Expand Up @@ -99,7 +99,7 @@ def write_to_file(self, filename: Path):
@classmethod
def from_template_name(cls, name: str) -> 'TemplateConfig':
path = get_package_path() / f'templates/proj_templates/{name}.json'
if not name in get_all_template_names():
if name not in get_all_template_names():
raise ValidationError(f"Template {name} not bundled with agentstack.")
return cls.from_file(path)

Expand Down
1 change: 0 additions & 1 deletion agentstack/tasks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from typing import Optional
import os
from pathlib import Path
import pydantic
Expand Down
2 changes: 1 addition & 1 deletion agentstack/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import psutil
import requests
from agentstack import conf
from agentstack.utils import get_telemetry_opt_out, get_framework, get_version
from agentstack.utils import get_telemetry_opt_out, get_version

TELEMETRY_URL = 'https://api.agentstack.sh/telemetry'

Expand Down
51 changes: 51 additions & 0 deletions agentstack/templates/crewai/tools/pipedream_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from crewai_tools import BaseTool
from typing import Optional, Dict
import os
import requests
import json
from pydantic import Field

class PipedreamActionTool(BaseTool):
name: str = "pipedream_action"
description: str = "Execute Pipedream component actions"
base_url: str = "https://api.pipedream.com/v1"
api_key: Optional[str] = Field(default_factory=lambda: os.getenv("PIPEDREAM_API_KEY"))
headers: Dict[str, str] = Field(default_factory=lambda: {
"Authorization": f"Bearer {os.getenv('PIPEDREAM_API_KEY')}",
"Content-Type": "application/json"
})

def _run(self, component_id: str, input_data: Optional[dict] = None) -> str:
try:
url = f"{self.base_url}/components/{component_id}/event"
response = requests.post(
url,
headers=self.headers,
json=input_data or {}
)
response.raise_for_status()
return json.dumps(response.json(), indent=2)
except Exception as e:
return f"Error executing Pipedream component: {str(e)}"

class PipedreamTriggerTool(BaseTool):
name: str = "pipedream_trigger"
description: str = "List Pipedream component events"
base_url: str = "https://api.pipedream.com/v1"
api_key: Optional[str] = Field(default_factory=lambda: os.getenv("PIPEDREAM_API_KEY"))
headers: Dict[str, str] = Field(default_factory=lambda: {
"Authorization": f"Bearer {os.getenv('PIPEDREAM_API_KEY')}",
"Content-Type": "application/json"
})

def _run(self, component_id: str) -> str:
try:
url = f"{self.base_url}/components/{component_id}/events"
response = requests.get(url, headers=self.headers)
response.raise_for_status()
return json.dumps(response.json(), indent=2)
except Exception as e:
return f"Error listing Pipedream events: {str(e)}"

pipedream_action = PipedreamActionTool()
pipedream_trigger = PipedreamTriggerTool()
11 changes: 11 additions & 0 deletions agentstack/tools/pipedream.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "pipedream",
"url": "https://pipedream.com/docs/connect/components",
"category": "api",
"packages": ["requests", "crewai_tools"],
"env": {
"PIPEDREAM_API_KEY": "..."
},
"tools": ["pipedream_action", "pipedream_trigger"],
"cta": "Create an API key at https://pipedream.com/settings/api-key"
}
7 changes: 4 additions & 3 deletions agentstack/update.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import os, sys
import os
import sys
import time
from pathlib import Path
from packaging.version import parse as parse_version, Version
Expand Down Expand Up @@ -50,7 +51,7 @@ def get_latest_version(package: str) -> Version:
f"{ENDPOINT_URL}/{package}/", headers={"Accept": "application/vnd.pypi.simple.v1+json"}
)
if response.status_code != 200:
raise Exception(f"Failed to fetch package data from pypi.")
raise Exception("Failed to fetch package data from pypi.")
data = response.json()
return parse_version(data['versions'][-1])

Expand Down Expand Up @@ -116,7 +117,7 @@ def check_for_updates(update_requested: bool = False):

try:
latest_version: Version = get_latest_version(AGENTSTACK_PACKAGE)
except Exception as e:
except Exception:
print(term_color("Failed to retrieve package index.", 'red'))
return

Expand Down
6 changes: 3 additions & 3 deletions agentstack/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Optional, Union
import os, sys
import os
import sys
import json
from ruamel.yaml import YAML
import re
Expand All @@ -19,7 +19,7 @@ def get_version(package: str = 'agentstack'):

def verify_agentstack_project():
try:
agentstack_config = conf.ConfigFile()
_ = conf.ConfigFile() # Just verify it exists
except FileNotFoundError:
print(
"\033[31mAgentStack Error: This does not appear to be an AgentStack project."
Expand Down
11 changes: 5 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ dependencies = [
"requests>=2.32",
"appdirs>=1.4.4",
"python-dotenv>=1.0.1",
"crewai==0.83.0",
"crewai-tools==0.14.0",
"selenium>=4.18.1",
]

[project.optional-dependencies]
Expand All @@ -39,17 +42,13 @@ dev = [
]
test = [
"tox",
"pytest",
"parameterized",
]
crewai = [
"crewai==0.83.0",
"crewai-tools==0.14.0",
]


[tool.setuptools.package-data]
agentstack = ["templates/**/*"]


[project.scripts]
agentstack = "agentstack.main:main"

Expand Down
1 change: 0 additions & 1 deletion tests/fixtures/frameworks/crewai/entrypoint_max.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
import tools


@CrewBase
Expand Down
5 changes: 2 additions & 3 deletions tests/fixtures/frameworks/crewai/entrypoint_min.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
import tools
from crewai import Crew, Process
from crewai.project import CrewBase, crew


@CrewBase
Expand Down
12 changes: 5 additions & 7 deletions tests/test_agents_config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import json
import os, sys
import os
import shutil
import unittest
import importlib.resources
from pathlib import Path
from agentstack import conf
from agentstack.agents import AgentConfig, AGENTS_FILENAME
Expand All @@ -22,10 +20,10 @@ def tearDown(self):
def test_empty_file(self):
config = AgentConfig("agent_name")
assert config.name == "agent_name"
assert config.role is ""
assert config.goal is ""
assert config.backstory is ""
assert config.llm is ""
assert config.role == ""
assert config.goal == ""
assert config.backstory == ""
assert config.llm == ""

def test_read_minimal_yaml(self):
shutil.copy(BASE_PATH / "fixtures/agents_min.yaml", self.project_dir / AGENTS_FILENAME)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_cli_init.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import subprocess
import os, sys
import os
import sys
import unittest
from parameterized import parameterized
from pathlib import Path
import shutil

Expand Down
3 changes: 2 additions & 1 deletion tests/test_cli_loads.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import subprocess
import os, sys
import os
import sys
import unittest
from pathlib import Path
import shutil
Expand Down
Loading
Loading