Separate tools & helper funcs in separate files (#192)
Separates utility and helper functions, as well as constant values (e.g. base URLs), in dedicated files, apart from tools files.
This commit is contained in:
parent
cd1fb648bd
commit
cd837a363d
17 changed files with 179 additions and 164 deletions
|
|
@ -8,7 +8,6 @@ from arcade.sdk.auth import Google
|
||||||
from arcade.sdk.errors import RetryableToolError
|
from arcade.sdk.errors import RetryableToolError
|
||||||
from google.oauth2.credentials import Credentials
|
from google.oauth2.credentials import Credentials
|
||||||
from googleapiclient.discovery import build
|
from googleapiclient.discovery import build
|
||||||
from googleapiclient.errors import HttpError
|
|
||||||
|
|
||||||
from arcade_google.tools.utils import (
|
from arcade_google.tools.utils import (
|
||||||
DateRange,
|
DateRange,
|
||||||
|
|
@ -19,6 +18,7 @@ from arcade_google.tools.utils import (
|
||||||
get_sent_email_url,
|
get_sent_email_url,
|
||||||
parse_draft_email,
|
parse_draft_email,
|
||||||
parse_email,
|
parse_email,
|
||||||
|
process_email_messages,
|
||||||
remove_none_values,
|
remove_none_values,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -356,22 +356,10 @@ async def list_emails_by_header(
|
||||||
if not messages:
|
if not messages:
|
||||||
return {"emails": []}
|
return {"emails": []}
|
||||||
|
|
||||||
emails = process_messages(service, messages)
|
emails = process_email_messages(service, messages)
|
||||||
return {"emails": emails}
|
return {"emails": emails}
|
||||||
|
|
||||||
|
|
||||||
def process_messages(service: Any, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
||||||
emails = []
|
|
||||||
for msg in messages:
|
|
||||||
try:
|
|
||||||
email_data = service.users().messages().get(userId="me", id=msg["id"]).execute()
|
|
||||||
email_details = parse_email(email_data)
|
|
||||||
emails += [email_details] if email_details else []
|
|
||||||
except HttpError as e:
|
|
||||||
print(f"Error reading email {msg['id']}: {e}")
|
|
||||||
return emails
|
|
||||||
|
|
||||||
|
|
||||||
@tool(
|
@tool(
|
||||||
requires_auth=Google(
|
requires_auth=Google(
|
||||||
scopes=["https://www.googleapis.com/auth/gmail.readonly"],
|
scopes=["https://www.googleapis.com/auth/gmail.readonly"],
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from zoneinfo import ZoneInfo
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from google.oauth2.credentials import Credentials
|
from google.oauth2.credentials import Credentials
|
||||||
from googleapiclient.discovery import Resource, build
|
from googleapiclient.discovery import Resource, build
|
||||||
|
from googleapiclient.errors import HttpError
|
||||||
|
|
||||||
from arcade_google.tools.models import Day, TimeSlot
|
from arcade_google.tools.models import Day, TimeSlot
|
||||||
|
|
||||||
|
|
@ -72,6 +73,18 @@ class DateRange(Enum):
|
||||||
return result + comparison_date.strftime("%Y/%m/%d")
|
return result + comparison_date.strftime("%Y/%m/%d")
|
||||||
|
|
||||||
|
|
||||||
|
def process_email_messages(service: Any, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||||
|
emails = []
|
||||||
|
for msg in messages:
|
||||||
|
try:
|
||||||
|
email_data = service.users().messages().get(userId="me", id=msg["id"]).execute()
|
||||||
|
email_details = parse_email(email_data)
|
||||||
|
emails += [email_details] if email_details else []
|
||||||
|
except HttpError as e:
|
||||||
|
print(f"Error reading email {msg['id']}: {e}")
|
||||||
|
return emails
|
||||||
|
|
||||||
|
|
||||||
def parse_email(email_data: dict[str, Any]) -> dict[str, Any]:
|
def parse_email(email_data: dict[str, Any]) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Parse email data and extract relevant information.
|
Parse email data and extract relevant information.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "arcade_google"
|
name = "arcade_google"
|
||||||
version = "0.1.9"
|
version = "0.1.10"
|
||||||
description = "Arcade tools for the entire google suite"
|
description = "Arcade tools for the entire google suite"
|
||||||
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
||||||
|
|
||||||
|
|
|
||||||
1
toolkits/linkedin/arcade_linkedin/tools/constants.py
Normal file
1
toolkits/linkedin/arcade_linkedin/tools/constants.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
LINKEDIN_BASE_URL = "https://api.linkedin.com/v2"
|
||||||
|
|
@ -1,74 +1,10 @@
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
import httpx
|
|
||||||
from arcade.sdk import ToolContext, tool
|
from arcade.sdk import ToolContext, tool
|
||||||
from arcade.sdk.auth import LinkedIn
|
from arcade.sdk.auth import LinkedIn
|
||||||
from arcade.sdk.errors import ToolExecutionError
|
from arcade.sdk.errors import ToolExecutionError
|
||||||
|
|
||||||
LINKEDIN_BASE_URL = "https://api.linkedin.com/v2"
|
from arcade_linkedin.tools.utils import _handle_linkedin_api_error, _send_linkedin_request
|
||||||
|
|
||||||
|
|
||||||
async def _send_linkedin_request(
|
|
||||||
context: ToolContext,
|
|
||||||
method: str,
|
|
||||||
endpoint: str,
|
|
||||||
params: dict | None = None,
|
|
||||||
json_data: dict | None = None,
|
|
||||||
) -> httpx.Response:
|
|
||||||
"""
|
|
||||||
Send an asynchronous request to the LinkedIn API.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
context: The tool context containing the authorization token.
|
|
||||||
method: The HTTP method (GET, POST, PUT, DELETE, etc.).
|
|
||||||
endpoint: The API endpoint path (e.g., "/ugcPosts").
|
|
||||||
params: Query parameters to include in the request.
|
|
||||||
json_data: JSON data to include in the request body.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The response object from the API request.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ToolExecutionError: If the request fails for any reason.
|
|
||||||
"""
|
|
||||||
url = f"{LINKEDIN_BASE_URL}{endpoint}"
|
|
||||||
token = (
|
|
||||||
context.authorization.token if context.authorization and context.authorization.token else ""
|
|
||||||
)
|
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
|
||||||
try:
|
|
||||||
response = await client.request(
|
|
||||||
method, url, headers=headers, params=params, json=json_data
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
raise ToolExecutionError(f"Failed to send request to LinkedIn API: {e}")
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def _handle_linkedin_api_error(response: httpx.Response) -> None:
|
|
||||||
"""
|
|
||||||
Handle errors from the LinkedIn API by mapping common status codes to ToolExecutionErrors.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
response: The response object from the API request.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ToolExecutionError: If the response contains an error status code.
|
|
||||||
"""
|
|
||||||
status_code_map = {
|
|
||||||
401: ToolExecutionError("Unauthorized: Invalid or expired token"),
|
|
||||||
403: ToolExecutionError("Forbidden: User does not have Spotify Premium"),
|
|
||||||
429: ToolExecutionError("Too Many Requests: Rate limit exceeded"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.status_code in status_code_map:
|
|
||||||
raise status_code_map[response.status_code]
|
|
||||||
elif response.status_code >= 400:
|
|
||||||
raise ToolExecutionError(f"Error: {response.status_code} - {response.text}")
|
|
||||||
|
|
||||||
|
|
||||||
@tool(
|
@tool(
|
||||||
|
|
@ -90,6 +26,7 @@ async def create_text_post(
|
||||||
# LinkedIn calls the user ID "sub" in their user_info data payload. See:
|
# LinkedIn calls the user ID "sub" in their user_info data payload. See:
|
||||||
# https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2#api-request-to-retreive-member-details
|
# https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2#api-request-to-retreive-member-details
|
||||||
user_id = context.authorization.user_info.get("sub") if context.authorization else None
|
user_id = context.authorization.user_info.get("sub") if context.authorization else None
|
||||||
|
|
||||||
if not user_id:
|
if not user_id:
|
||||||
raise ToolExecutionError(
|
raise ToolExecutionError(
|
||||||
"User ID not found.",
|
"User ID not found.",
|
||||||
|
|
@ -110,9 +47,11 @@ async def create_text_post(
|
||||||
}
|
}
|
||||||
|
|
||||||
response = await _send_linkedin_request(context, "POST", endpoint, json_data=payload)
|
response = await _send_linkedin_request(context, "POST", endpoint, json_data=payload)
|
||||||
|
|
||||||
if response.status_code >= 200 and response.status_code < 300:
|
if response.status_code >= 200 and response.status_code < 300:
|
||||||
share_id = response.json().get("id")
|
share_id = response.json().get("id")
|
||||||
return f"https://www.linkedin.com/feed/update/{share_id}/"
|
return f"https://www.linkedin.com/feed/update/{share_id}/"
|
||||||
|
|
||||||
_handle_linkedin_api_error(response)
|
_handle_linkedin_api_error(response)
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
|
||||||
68
toolkits/linkedin/arcade_linkedin/tools/utils.py
Normal file
68
toolkits/linkedin/arcade_linkedin/tools/utils.py
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import httpx
|
||||||
|
from arcade.sdk import ToolContext
|
||||||
|
from arcade.sdk.errors import ToolExecutionError
|
||||||
|
|
||||||
|
from arcade_linkedin.tools.constants import LINKEDIN_BASE_URL
|
||||||
|
|
||||||
|
|
||||||
|
async def _send_linkedin_request(
|
||||||
|
context: ToolContext,
|
||||||
|
method: str,
|
||||||
|
endpoint: str,
|
||||||
|
params: dict | None = None,
|
||||||
|
json_data: dict | None = None,
|
||||||
|
) -> httpx.Response:
|
||||||
|
"""
|
||||||
|
Send an asynchronous request to the LinkedIn API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
context: The tool context containing the authorization token.
|
||||||
|
method: The HTTP method (GET, POST, PUT, DELETE, etc.).
|
||||||
|
endpoint: The API endpoint path (e.g., "/ugcPosts").
|
||||||
|
params: Query parameters to include in the request.
|
||||||
|
json_data: JSON data to include in the request body.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The response object from the API request.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ToolExecutionError: If the request fails for any reason.
|
||||||
|
"""
|
||||||
|
url = f"{LINKEDIN_BASE_URL}{endpoint}"
|
||||||
|
token = (
|
||||||
|
context.authorization.token if context.authorization and context.authorization.token else ""
|
||||||
|
)
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
try:
|
||||||
|
response = await client.request(
|
||||||
|
method, url, headers=headers, params=params, json=json_data
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
except httpx.RequestError as e:
|
||||||
|
raise ToolExecutionError(f"Failed to send request to LinkedIn API: {e}")
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_linkedin_api_error(response: httpx.Response) -> None:
|
||||||
|
"""
|
||||||
|
Handle errors from the LinkedIn API by mapping common status codes to ToolExecutionErrors.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
response: The response object from the API request.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ToolExecutionError: If the response contains an error status code.
|
||||||
|
"""
|
||||||
|
status_code_map = {
|
||||||
|
401: ToolExecutionError("Unauthorized: Invalid or expired token"),
|
||||||
|
403: ToolExecutionError("Forbidden: User does not have Spotify Premium"),
|
||||||
|
429: ToolExecutionError("Too Many Requests: Rate limit exceeded"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.status_code in status_code_map:
|
||||||
|
raise status_code_map[response.status_code]
|
||||||
|
elif response.status_code >= 400:
|
||||||
|
raise ToolExecutionError(f"Error: {response.status_code} - {response.text}")
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "arcade_linkedin"
|
name = "arcade_linkedin"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
description = "Arcade tools for LinkedIn"
|
description = "Arcade tools for LinkedIn"
|
||||||
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import json
|
import json
|
||||||
import os
|
from typing import Annotated
|
||||||
from typing import Annotated, Any, Optional
|
|
||||||
|
|
||||||
import serpapi
|
import serpapi
|
||||||
from arcade.sdk import tool
|
from arcade.sdk import tool
|
||||||
|
|
||||||
|
from arcade_search.tools.utils import get_secret
|
||||||
|
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
async def search_google(
|
async def search_google(
|
||||||
|
|
@ -25,12 +26,3 @@ async def search_google(
|
||||||
organic_results = results.get("organic_results", [])
|
organic_results = results.get("organic_results", [])
|
||||||
|
|
||||||
return json.dumps(organic_results[:n_results])
|
return json.dumps(organic_results[:n_results])
|
||||||
|
|
||||||
|
|
||||||
def get_secret(name: str, default: Optional[Any] = None) -> Any:
|
|
||||||
secret = os.getenv(name)
|
|
||||||
if secret is None:
|
|
||||||
if default is not None:
|
|
||||||
return default
|
|
||||||
raise ValueError(f"Secret {name} is not set.")
|
|
||||||
return secret
|
|
||||||
|
|
|
||||||
11
toolkits/search/arcade_search/tools/utils.py
Normal file
11
toolkits/search/arcade_search/tools/utils.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import os
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
|
||||||
|
def get_secret(name: str, default: Optional[Any] = None) -> Any:
|
||||||
|
secret = os.getenv(name)
|
||||||
|
if secret is None:
|
||||||
|
if default is not None:
|
||||||
|
return default
|
||||||
|
raise ValueError(f"Secret {name} is not set.")
|
||||||
|
return secret
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "arcade_search"
|
name = "arcade_search"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
description = "Tools for searching the web"
|
description = "Tools for searching the web"
|
||||||
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
||||||
|
|
||||||
|
|
|
||||||
1
toolkits/x/arcade_x/tools/constants.py
Normal file
1
toolkits/x/arcade_x/tools/constants.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
TWEETS_URL = "https://api.x.com/2/tweets"
|
||||||
|
|
@ -5,6 +5,7 @@ from arcade.sdk import ToolContext, tool
|
||||||
from arcade.sdk.auth import X
|
from arcade.sdk.auth import X
|
||||||
from arcade.sdk.errors import RetryableToolError
|
from arcade.sdk.errors import RetryableToolError
|
||||||
|
|
||||||
|
from arcade_x.tools.constants import TWEETS_URL
|
||||||
from arcade_x.tools.utils import (
|
from arcade_x.tools.utils import (
|
||||||
expand_attached_media,
|
expand_attached_media,
|
||||||
expand_long_tweet,
|
expand_long_tweet,
|
||||||
|
|
@ -15,9 +16,6 @@ from arcade_x.tools.utils import (
|
||||||
remove_none_values,
|
remove_none_values,
|
||||||
)
|
)
|
||||||
|
|
||||||
TWEETS_URL = "https://api.x.com/2/tweets"
|
|
||||||
|
|
||||||
|
|
||||||
# Manage Tweets Tools. See developer docs for additional available parameters:
|
# Manage Tweets Tools. See developer docs for additional available parameters:
|
||||||
# https://developer.x.com/en/docs/x-api/tweets/manage-tweets/api-reference
|
# https://developer.x.com/en/docs/x-api/tweets/manage-tweets/api-reference
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "arcade_x"
|
name = "arcade_x"
|
||||||
version = "0.1.9"
|
version = "0.1.10"
|
||||||
description = "LLM tools for interacting with X (Twitter)"
|
description = "LLM tools for interacting with X (Twitter)"
|
||||||
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
||||||
|
|
||||||
|
|
|
||||||
1
toolkits/zoom/arcade_zoom/tools/constants.py
Normal file
1
toolkits/zoom/arcade_zoom/tools/constants.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ZOOM_BASE_URL = "https://api.zoom.us/v2"
|
||||||
|
|
@ -1,74 +1,9 @@
|
||||||
from typing import Annotated, Optional
|
from typing import Annotated, Optional
|
||||||
|
|
||||||
import httpx
|
|
||||||
from arcade.sdk import ToolContext, tool
|
from arcade.sdk import ToolContext, tool
|
||||||
from arcade.sdk.auth import Zoom
|
from arcade.sdk.auth import Zoom
|
||||||
from arcade.sdk.errors import ToolExecutionError
|
|
||||||
|
|
||||||
ZOOM_BASE_URL = "https://api.zoom.us/v2"
|
from arcade_zoom.tools.utils import _handle_zoom_api_error, _send_zoom_request
|
||||||
|
|
||||||
|
|
||||||
async def _send_zoom_request(
|
|
||||||
context: ToolContext,
|
|
||||||
method: str,
|
|
||||||
endpoint: str,
|
|
||||||
params: dict | None = None,
|
|
||||||
json_data: dict | None = None,
|
|
||||||
) -> httpx.Response:
|
|
||||||
"""
|
|
||||||
Send an asynchronous request to the Zoom API.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
context: The tool context containing the authorization token.
|
|
||||||
method: The HTTP method (GET, POST, PUT, DELETE, etc.).
|
|
||||||
endpoint: The API endpoint path (e.g., "/users/me/upcoming_meetings").
|
|
||||||
params: Query parameters to include in the request.
|
|
||||||
json_data: JSON data to include in the request body.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The response object from the API request.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ToolExecutionError: If the request fails for any reason.
|
|
||||||
"""
|
|
||||||
url = f"{ZOOM_BASE_URL}{endpoint}"
|
|
||||||
token = (
|
|
||||||
context.authorization.token if context.authorization and context.authorization.token else ""
|
|
||||||
)
|
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
|
||||||
try:
|
|
||||||
response = await client.request(
|
|
||||||
method, url, headers=headers, params=params, json=json_data
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
raise ToolExecutionError(f"Failed to send request to Zoom API: {e}")
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def _handle_zoom_api_error(response: httpx.Response) -> None:
|
|
||||||
"""
|
|
||||||
Handle errors from the Zoom API by mapping common status codes to ToolExecutionErrors.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
response: The response object from the API request.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ToolExecutionError: If the response contains an error status code.
|
|
||||||
"""
|
|
||||||
status_code_map = {
|
|
||||||
401: ToolExecutionError("Unauthorized: Invalid or expired token"),
|
|
||||||
403: ToolExecutionError("Forbidden: Access denied"),
|
|
||||||
429: ToolExecutionError("Too Many Requests: Rate limit exceeded"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.status_code in status_code_map:
|
|
||||||
raise status_code_map[response.status_code]
|
|
||||||
elif response.status_code >= 400:
|
|
||||||
raise ToolExecutionError(f"Error: {response.status_code} - {response.text}")
|
|
||||||
|
|
||||||
|
|
||||||
@tool(
|
@tool(
|
||||||
|
|
|
||||||
68
toolkits/zoom/arcade_zoom/tools/utils.py
Normal file
68
toolkits/zoom/arcade_zoom/tools/utils.py
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import httpx
|
||||||
|
from arcade.sdk import ToolContext
|
||||||
|
from arcade.sdk.errors import ToolExecutionError
|
||||||
|
|
||||||
|
from arcade_zoom.tools.constants import ZOOM_BASE_URL
|
||||||
|
|
||||||
|
|
||||||
|
async def _send_zoom_request(
|
||||||
|
context: ToolContext,
|
||||||
|
method: str,
|
||||||
|
endpoint: str,
|
||||||
|
params: dict | None = None,
|
||||||
|
json_data: dict | None = None,
|
||||||
|
) -> httpx.Response:
|
||||||
|
"""
|
||||||
|
Send an asynchronous request to the Zoom API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
context: The tool context containing the authorization token.
|
||||||
|
method: The HTTP method (GET, POST, PUT, DELETE, etc.).
|
||||||
|
endpoint: The API endpoint path (e.g., "/users/me/upcoming_meetings").
|
||||||
|
params: Query parameters to include in the request.
|
||||||
|
json_data: JSON data to include in the request body.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The response object from the API request.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ToolExecutionError: If the request fails for any reason.
|
||||||
|
"""
|
||||||
|
url = f"{ZOOM_BASE_URL}{endpoint}"
|
||||||
|
token = (
|
||||||
|
context.authorization.token if context.authorization and context.authorization.token else ""
|
||||||
|
)
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
try:
|
||||||
|
response = await client.request(
|
||||||
|
method, url, headers=headers, params=params, json=json_data
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
except httpx.RequestError as e:
|
||||||
|
raise ToolExecutionError(f"Failed to send request to Zoom API: {e}")
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_zoom_api_error(response: httpx.Response) -> None:
|
||||||
|
"""
|
||||||
|
Handle errors from the Zoom API by mapping common status codes to ToolExecutionErrors.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
response: The response object from the API request.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ToolExecutionError: If the response contains an error status code.
|
||||||
|
"""
|
||||||
|
status_code_map = {
|
||||||
|
401: ToolExecutionError("Unauthorized: Invalid or expired token"),
|
||||||
|
403: ToolExecutionError("Forbidden: Access denied"),
|
||||||
|
429: ToolExecutionError("Too Many Requests: Rate limit exceeded"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.status_code in status_code_map:
|
||||||
|
raise status_code_map[response.status_code]
|
||||||
|
elif response.status_code >= 400:
|
||||||
|
raise ToolExecutionError(f"Error: {response.status_code} - {response.text}")
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "arcade_zoom"
|
name = "arcade_zoom"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
description = "Arcade tools for Zoom"
|
description = "Arcade tools for Zoom"
|
||||||
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
authors = ["Arcade AI <dev@arcade-ai.com>"]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue