feat: add ZapierActionTool and ZapierActionsAdapter for integrating with Zapier actions (#311)

* feat: add ZapierActionTool and ZapierActionsAdapter for integrating with Zapier actions

- Introduced ZapierActionTool to execute Zapier actions with dynamic parameter handling.
- Added ZapierActionsAdapter to fetch available Zapier actions and convert them into BaseTool instances.
- Updated __init__.py files to include new tools and ensure proper imports.
- Created README.md for ZapierActionTools with installation instructions and usage examples.

* fix: restore ZapierActionTool import and enhance logging in Zapier adapter

- Reintroduced the import of ZapierActionTool in __init__.py for proper accessibility.
- Added logging for error handling in ZapierActionsAdapter to improve debugging.
- Updated ZapierActionTools factory function to include logging for missing API key.
This commit is contained in:
Lorenze Jay
2025-05-27 09:54:35 -07:00
committed by GitHub
parent 0dbcbde119
commit 5d5377cfb9
5 changed files with 250 additions and 1 deletions

View File

@@ -69,4 +69,6 @@ from .tools import (
XMLSearchTool,
YoutubeChannelSearchTool,
YoutubeVideoSearchTool,
)
ZapierActionTools,
)
from .adapters.zapier_adapter import ZapierActionTool

View File

@@ -0,0 +1,122 @@
import os
import logging
from typing import List
import requests
from crewai.tools import BaseTool
from pydantic import Field, create_model
ACTIONS_URL = "https://actions.zapier.com/api/v2/ai-actions"
logger = logging.getLogger(__name__)
class ZapierActionTool(BaseTool):
"""
A tool that wraps a Zapier action
"""
name: str = Field(description="Tool name")
description: str = Field(description="Tool description")
action_id: str = Field(description="Zapier action ID")
api_key: str = Field(description="Zapier API key")
def _run(self, **kwargs) -> str:
"""Execute the Zapier action"""
headers = {"x-api-key": self.api_key, "Content-Type": "application/json"}
instructions = kwargs.pop(
"instructions", "Execute this action with the provided parameters"
)
if not kwargs:
action_params = {"instructions": instructions, "params": {}}
else:
formatted_params = {}
for key, value in kwargs.items():
formatted_params[key] = {
"value": value,
"mode": "guess",
}
action_params = {"instructions": instructions, "params": formatted_params}
execute_url = f"{ACTIONS_URL}/{self.action_id}/execute/"
response = requests.request(
"POST", execute_url, headers=headers, json=action_params
)
response.raise_for_status()
return response.json()
class ZapierActionsAdapter:
"""
Adapter for Zapier Actions
"""
api_key: str
def __init__(self, api_key: str = None):
self.api_key = api_key or os.getenv("ZAPIER_API_KEY")
if not self.api_key:
logger.error("Zapier Actions API key is required")
raise ValueError("Zapier Actions API key is required")
def get_zapier_actions(self):
headers = {
"x-api-key": self.api_key,
}
response = requests.request("GET", ACTIONS_URL, headers=headers)
response.raise_for_status()
response_json = response.json()
return response_json
def tools(self) -> List[BaseTool]:
"""Convert Zapier actions to BaseTool instances"""
actions_response = self.get_zapier_actions()
tools = []
for action in actions_response.get("results", []):
tool_name = (
action["meta"]["action_label"]
.replace(" ", "_")
.replace(":", "")
.lower()
)
params = action.get("params", {})
args_fields = {}
args_fields["instructions"] = (
str,
Field(description="Instructions for how to execute this action"),
)
for param_name, param_info in params.items():
field_type = (
str # Default to string, could be enhanced based on param_info
)
field_description = (
param_info.get("description", "")
if isinstance(param_info, dict)
else ""
)
args_fields[param_name] = (
field_type,
Field(description=field_description),
)
args_schema = create_model(f"{tool_name.title()}Schema", **args_fields)
tool = ZapierActionTool(
name=tool_name,
description=action["description"],
action_id=action["id"],
api_key=self.api_key,
args_schema=args_schema,
)
tools.append(tool)
return tools

View File

@@ -77,3 +77,4 @@ from .youtube_channel_search_tool.youtube_channel_search_tool import (
YoutubeChannelSearchTool,
)
from .youtube_video_search_tool.youtube_video_search_tool import YoutubeVideoSearchTool
from .zapier_action_tool.zapier_action_tool import ZapierActionTools

View File

@@ -0,0 +1,91 @@
# Zapier Action Tools
## Description
This tool enables CrewAI agents to interact with Zapier actions, allowing them to automate workflows and integrate with hundreds of applications through Zapier's platform. The tool dynamically creates BaseTool instances for each available Zapier action, making it easy to incorporate automation into your AI workflows.
## Installation
Install the crewai_tools package by executing the following command in your terminal:
```shell
uv pip install 'crewai[tools]'
```
## Example
To utilize the ZapierActionTools for different use cases, follow these examples:
```python
from crewai_tools import ZapierActionTools
from crewai import Agent
# Get all available Zapier actions you are connected to.
tools = ZapierActionTools(
zapier_api_key="your-zapier-api-key"
)
# Or specify only certain actions you want to use
tools = ZapierActionTools(
zapier_api_key="your-zapier-api-key",
action_list=["gmail_find_email", "slack_send_message", "google_sheets_create_row"]
)
# Adding the tools to an agent
zapier_agent = Agent(
name="zapier_agent",
role="You are a helpful assistant that can automate tasks using Zapier integrations.",
llm="gpt-4o-mini",
tools=tools,
goal="Automate workflows and integrate with various applications",
backstory="You are a Zapier automation expert that helps users connect and automate their favorite apps.",
verbose=True,
)
# Example usage
result = zapier_agent.kickoff(
"Find emails from john@example.com in Gmail"
)
```
## Arguments
- `zapier_api_key` : Your Zapier API key for authentication. Can also be set via `ZAPIER_API_KEY` environment variable. (Required)
- `action_list` : A list of specific Zapier action names to include. If not provided, all available actions will be returned. (Optional)
## Environment Variables
You can set your Zapier API key as an environment variable instead of passing it directly:
```bash
export ZAPIER_API_KEY="your-zapier-api-key"
```
Then use the tool without explicitly passing the API key:
```python
from crewai_tools import ZapierActionTools
# API key will be automatically loaded from environment
tools = ZapierActionTools(
action_list=["gmail_find_email", "slack_send_message"]
)
```
## Getting Your Zapier API Key
1. Log in to your Zapier account
2. Go to https://zapier.com/app/developer/
3. Create a new app or use an existing one
4. Navigate to the "Authentication" section
5. Copy your API key
## Available Actions
The tool will dynamically discover all available Zapier actions associated with your API key. Common actions include:
- Gmail operations (find emails, send emails)
- Slack messaging
- Google Sheets operations
- Calendar events
- And hundreds more depending on your Zapier integrations

View File

@@ -0,0 +1,33 @@
import os
import logging
from typing import List, Optional
from crewai.tools import BaseTool
from crewai_tools.adapters.zapier_adapter import ZapierActionsAdapter
logger = logging.getLogger(__name__)
def ZapierActionTools(
zapier_api_key: Optional[str] = None, action_list: Optional[List[str]] = None
) -> List[BaseTool]:
"""Factory function that returns Zapier action tools.
Args:
zapier_api_key: The API key for Zapier.
action_list: Optional list of specific tool names to include.
Returns:
A list of Zapier action tools.
"""
if zapier_api_key is None:
zapier_api_key = os.getenv("ZAPIER_API_KEY")
if zapier_api_key is None:
logger.error("ZAPIER_API_KEY is not set")
raise ValueError("ZAPIER_API_KEY is not set")
adapter = ZapierActionsAdapter(zapier_api_key)
all_tools = adapter.tools()
if action_list is None:
return all_tools
return [tool for tool in all_tools if tool.name in action_list]