107 lines
4.5 KiB
Python
107 lines
4.5 KiB
Python
import json
|
|
from typing import Any
|
|
|
|
import aiohttp
|
|
from composio import ComposioToolSet as BaseComposioToolSet
|
|
from composio.exceptions import (
|
|
ApiKeyNotProvidedError,
|
|
ComposioSDKError,
|
|
ConnectedAccountNotFoundError,
|
|
EnumMetadataNotFound,
|
|
EnumStringNotFound,
|
|
)
|
|
|
|
|
|
class AsyncComposioToolSet(BaseComposioToolSet, runtime="letta", description_char_limit=1024):
|
|
"""
|
|
Async version of ComposioToolSet client for interacting with Composio API
|
|
Used to asynchronously hit the execute action endpoint
|
|
|
|
https://docs.composio.dev/api-reference/api-reference/v3/tools/post-api-v-3-tools-execute-action
|
|
"""
|
|
|
|
def __init__(self, api_key: str, entity_id: str, lock: bool = True):
|
|
"""
|
|
Initialize the AsyncComposioToolSet client
|
|
|
|
Args:
|
|
api_key (str): Your Composio API key
|
|
entity_id (str): Your Composio entity ID
|
|
lock (bool): Whether to use locking (default: True)
|
|
"""
|
|
super().__init__(api_key=api_key, entity_id=entity_id, lock=lock)
|
|
|
|
self.headers = {
|
|
"Content-Type": "application/json",
|
|
"X-API-Key": self._api_key,
|
|
}
|
|
|
|
async def execute_action(
|
|
self,
|
|
action: str,
|
|
params: dict[str, Any] = {},
|
|
) -> dict[str, Any]:
|
|
"""
|
|
Execute an action asynchronously using the Composio API
|
|
|
|
Args:
|
|
action (str): The name of the action to execute
|
|
params (dict[str, Any], optional): Parameters for the action
|
|
|
|
Returns:
|
|
dict[str, Any]: The API response
|
|
|
|
Raises:
|
|
ApiKeyNotProvidedError: if the API key is not provided
|
|
ComposioSDKError: if a general Composio SDK error occurs
|
|
ConnectedAccountNotFoundError: if the connected account is not found
|
|
EnumMetadataNotFound: if enum metadata is not found
|
|
EnumStringNotFound: if enum string is not found
|
|
aiohttp.ClientError: if a network-related error occurs
|
|
ValueError: if an error with the parameters or response occurs
|
|
"""
|
|
API_VERSION = "v3"
|
|
endpoint = f"{self._base_url}/{API_VERSION}/tools/execute/{action}"
|
|
|
|
json_payload = {
|
|
"entity_id": self.entity_id,
|
|
"arguments": params or {},
|
|
}
|
|
|
|
try:
|
|
async with aiohttp.ClientSession() as session:
|
|
async with session.post(endpoint, headers=self.headers, json=json_payload) as response:
|
|
print(response, response.status, response.reason, response.content)
|
|
if response.status == 200:
|
|
return await response.json()
|
|
else:
|
|
error_text = await response.text()
|
|
try:
|
|
error_json = json.loads(error_text)
|
|
error_message = error_json.get("message", error_text)
|
|
error_code = error_json.get("code")
|
|
|
|
# Handle specific error codes from Composio API
|
|
if error_code == 10401 or "API_KEY_NOT_FOUND" in error_message:
|
|
raise ApiKeyNotProvidedError()
|
|
if "connected account not found" in error_message.lower():
|
|
raise ConnectedAccountNotFoundError(f"Connected account not found: {error_message}")
|
|
if "enum metadata not found" in error_message.lower():
|
|
raise EnumMetadataNotFound(f"Enum metadata not found: {error_message}")
|
|
if "enum string not found" in error_message.lower():
|
|
raise EnumStringNotFound(f"Enum string not found: {error_message}")
|
|
except json.JSONDecodeError:
|
|
error_message = error_text
|
|
|
|
# If no specific error was identified, raise a general error
|
|
raise ValueError(f"API request failed with status {response.status}: {error_message}")
|
|
except aiohttp.ClientError as e:
|
|
# Wrap network errors in ComposioSDKError
|
|
raise ComposioSDKError(f"Network error when calling Composio API: {str(e)}")
|
|
except ValueError:
|
|
# Re-raise ValueError (which could be our custom error message or a JSON parsing error)
|
|
raise
|
|
except Exception as e:
|
|
# Catch any other exceptions and wrap them in ComposioSDKError
|
|
raise ComposioSDKError(f"Unexpected error when calling Composio API: {str(e)}")
|