Skip to content
This repository was archived by the owner on Nov 10, 2025. It is now read-only.

Commit 3da2f0a

Browse files
committed
feat: add EnterpriseActionKitToolAdapter and EnterpriseActionTool for enterprise action execution
- Introduced EnterpriseActionTool to execute specific enterprise actions with dynamic parameter validation. - Added EnterpriseActionKitToolAdapter to manage and create tool instances for available enterprise actions. - Implemented methods for fetching action schemas from the API and creating corresponding tools. - Enhanced error handling and provided detailed descriptions for tool parameters. - Included a main execution block for testing the adapter with a sample agent and task setup.
1 parent a81cd9a commit 3da2f0a

File tree

1 file changed

+259
-0
lines changed

1 file changed

+259
-0
lines changed
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
import requests
2+
from pydantic import Field, create_model
3+
from typing import List, Any, Dict, Optional
4+
import json
5+
from crewai.tools import BaseTool
6+
from crewai import Agent, Task, Crew
7+
8+
9+
ENTERPRISE_ACTION_KIT_PROJECT_ID = "dd525517-df22-49d2-a69e-6a0eed211166"
10+
11+
12+
class EnterpriseActionTool(BaseTool):
13+
"""A tool that executes a specific enterprise action."""
14+
15+
enterprise_action_token: str = Field(
16+
default="", description="The enterprise action token"
17+
)
18+
action_name: str = Field(default="", description="The name of the action")
19+
action_schema: Dict[str, Any] = Field(
20+
default={}, description="The schema of the action"
21+
)
22+
23+
def __init__(
24+
self,
25+
name: str,
26+
description: str,
27+
enterprise_action_token: str,
28+
action_name: str,
29+
action_schema: Dict[str, Any],
30+
):
31+
schema_props = (
32+
action_schema.get("function", {})
33+
.get("parameters", {})
34+
.get("properties", {})
35+
)
36+
required = (
37+
action_schema.get("function", {}).get("parameters", {}).get("required", [])
38+
)
39+
40+
# Define field definitions for the model
41+
field_definitions = {}
42+
for param_name, param_details in schema_props.items():
43+
param_type = str # Default to string type
44+
param_desc = param_details.get("description", "")
45+
is_required = param_name in required
46+
47+
# Basic type mapping (can be extended)
48+
if param_details.get("type") == "integer":
49+
param_type = int
50+
elif param_details.get("type") == "number":
51+
param_type = float
52+
elif param_details.get("type") == "boolean":
53+
param_type = bool
54+
55+
# Create field with appropriate type and config
56+
field_definitions[param_name] = (
57+
param_type if is_required else Optional[param_type],
58+
Field(description=param_desc),
59+
)
60+
61+
# Create the model
62+
if field_definitions:
63+
args_schema = create_model(
64+
f"{name.capitalize()}Schema", **field_definitions
65+
)
66+
else:
67+
# Fallback for empty schema
68+
args_schema = create_model(
69+
f"{name.capitalize()}Schema",
70+
input_text=(str, Field(description="Input for the action")),
71+
)
72+
73+
super().__init__(name=name, description=description, args_schema=args_schema)
74+
self.enterprise_action_token = enterprise_action_token
75+
self.action_name = action_name
76+
self.action_schema = action_schema
77+
78+
def _run(self, **kwargs) -> str:
79+
"""Execute the specific enterprise action with validated parameters."""
80+
try:
81+
params = {k: v for k, v in kwargs.items() if v is not None}
82+
83+
api_url = f"https://worker-actionkit.tools.crewai.com/projects/{ENTERPRISE_ACTION_KIT_PROJECT_ID}/actions"
84+
headers = {
85+
"Authorization": f"Bearer {self.enterprise_action_token}",
86+
"Content-Type": "application/json",
87+
}
88+
payload = {"action": self.action_name, "parameters": params}
89+
90+
response = requests.post(
91+
url=api_url, headers=headers, json=payload, timeout=60
92+
)
93+
94+
data = response.json()
95+
if not response.ok:
96+
error_message = data.get("error", {}).get("message", json.dumps(data))
97+
return f"API request failed: {error_message}"
98+
99+
return json.dumps(data, indent=2)
100+
101+
except Exception as e:
102+
return f"Error executing action {self.action_name}: {str(e)}"
103+
104+
105+
class EnterpriseActionKitToolAdapter:
106+
"""Adapter that creates BaseTool instances for enterprise actions."""
107+
108+
def __init__(self, enterprise_action_token: str):
109+
"""Initialize the adapter with an enterprise action token."""
110+
if not enterprise_action_token:
111+
raise ValueError("enterprise_action_token is required")
112+
113+
self.enterprise_action_token = enterprise_action_token
114+
self._actions_schema = {}
115+
self._tools = None
116+
117+
def tools(self) -> List[BaseTool]:
118+
"""Get the list of tools created from enterprise actions.
119+
120+
Returns:
121+
List of BaseTool instances, one for each enterprise action.
122+
"""
123+
if self._tools is None:
124+
self._fetch_actions()
125+
self._create_tools()
126+
return self._tools
127+
128+
def _fetch_actions(self):
129+
"""Fetch available actions from the API."""
130+
try:
131+
actions_url = f"https://worker-actionkit.tools.crewai.com/projects/{ENTERPRISE_ACTION_KIT_PROJECT_ID}/actions"
132+
headers = {"Authorization": f"Bearer {self.enterprise_action_token}"}
133+
params = {"format": "json_schema"}
134+
135+
response = requests.get(
136+
actions_url, headers=headers, params=params, timeout=30
137+
)
138+
response.raise_for_status()
139+
140+
raw_data = response.json()
141+
if "actions" not in raw_data:
142+
print(f"Unexpected API response structure: {raw_data}")
143+
return
144+
145+
# Parse the actions schema
146+
parsed_schema = {}
147+
action_categories = raw_data["actions"]
148+
149+
for category, action_list in action_categories.items():
150+
if isinstance(action_list, list):
151+
for action in action_list:
152+
func_details = action.get("function")
153+
if func_details and "name" in func_details:
154+
action_name = func_details["name"]
155+
parsed_schema[action_name] = action
156+
157+
self._actions_schema = parsed_schema
158+
159+
except Exception as e:
160+
print(f"Error fetching actions: {e}")
161+
import traceback
162+
163+
traceback.print_exc()
164+
165+
def _create_tools(self):
166+
"""Create BaseTool instances for each action."""
167+
tools = []
168+
169+
for action_name, action_schema in self._actions_schema.items():
170+
function_details = action_schema.get("function", {})
171+
description = function_details.get("description", f"Execute {action_name}")
172+
173+
# Get parameter info for a better description
174+
parameters = function_details.get("parameters", {}).get("properties", {})
175+
param_info = []
176+
for param_name, param_details in parameters.items():
177+
param_desc = param_details.get("description", "")
178+
required = param_name in function_details.get("parameters", {}).get(
179+
"required", []
180+
)
181+
param_info.append(
182+
f"- {param_name}: {param_desc} {'(required)' if required else '(optional)'}"
183+
)
184+
185+
full_description = f"{description}\n\nParameters:\n" + "\n".join(param_info)
186+
187+
tool = EnterpriseActionTool(
188+
name=action_name.lower().replace(" ", "_"),
189+
description=full_description,
190+
action_name=action_name,
191+
action_schema=action_schema,
192+
enterprise_action_token=self.enterprise_action_token,
193+
)
194+
195+
tools.append(tool)
196+
197+
self._tools = tools
198+
199+
# Adding context manager support for convenience, but direct usage is also supported
200+
def __enter__(self):
201+
return self.tools()
202+
203+
def __exit__(self, exc_type, exc_val, exc_tb):
204+
pass
205+
206+
207+
if __name__ == "__main__":
208+
# IMPORTANT: Replace 'YOUR_TOKEN_HERE' with your actual valid token
209+
# You can also load it from an environment variable for better security
210+
import os
211+
212+
token = os.environ.get(
213+
"ENTERPRISE_TOOL_TOKEN",
214+
) # Replace YOUR_TOKEN_HERE if not using env var
215+
216+
if token == "YOUR_TOKEN_HERE" or not token:
217+
print("Error: ENTERPRISE_TOOL_TOKEN is not set.")
218+
print(
219+
"Please replace 'YOUR_TOKEN_HERE' in the code or set the ENTERPRISE_TOOL_TOKEN environment variable."
220+
)
221+
else:
222+
try:
223+
print("Initializing EnterpriseActionKitTool...")
224+
adapter = EnterpriseActionKitToolAdapter(enterprise_action_token=token)
225+
available_tools = adapter.tools()
226+
227+
agent = Agent(
228+
model="gpt-4o",
229+
tools=available_tools,
230+
role="You are are expert at google sheets",
231+
goal="Get the sheet with the data x",
232+
backstory="You are a expert at google sheets",
233+
verbose=True,
234+
)
235+
236+
task = Task(
237+
description="return data from the sheet with the id: {spreadsheetId}, with the limit: {limit}",
238+
expected_output="The data from the sheet with the id: {spreadsheetId} with the limit: {limit}",
239+
agent=agent,
240+
)
241+
crew = Crew(
242+
agents=[agent],
243+
tasks=[task],
244+
verbose=True,
245+
)
246+
result = crew.kickoff(
247+
inputs={
248+
"spreadsheetId": "1DHDIWGdhUXqXeYOO8yA44poiY222qHPQEUu28olipKs",
249+
"limit": 2,
250+
}
251+
)
252+
253+
except ValueError as e:
254+
print(f"\nConfiguration Error: {e}")
255+
except Exception as e:
256+
print(f"\nAn unexpected error occurred during execution: {e}")
257+
import traceback
258+
259+
traceback.print_exc()

0 commit comments

Comments
 (0)