Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update file upload and download mechanism on toolset #1124

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
65 changes: 5 additions & 60 deletions python/composio/client/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
Composio server object collections
"""

import base64
import difflib
import json
import os
import time
import traceback
import typing as t
Expand Down Expand Up @@ -986,28 +984,15 @@ def get( # type: ignore
return super().get(queries=queries)


def _check_file_uploadable(param_field: dict) -> bool:
return (
isinstance(param_field, dict)
and (param_field.get("title") in ["File", "FileType"])
and all(
field_name in param_field.get("properties", {})
for field_name in ["name", "content"]
)
)


def _check_file_downloadable(param_field: dict) -> bool:
return set(param_field.keys()) == {"name", "content"}


class ActionParametersModel(BaseModel):
"""Action parameter data models."""

properties: t.Dict[str, t.Any]
title: str
type: str

accept: t.List[str] = Field(default_factory=lambda: ["*/*"])
file_uploadable: bool = False
required: t.Optional[t.List[str]] = None


Expand All @@ -1018,6 +1003,7 @@ class ActionResponseModel(BaseModel):
title: str
type: str

file_downloadable: bool = False
required: t.Optional[t.List[str]] = None


Expand Down Expand Up @@ -1246,54 +1232,13 @@ def execute(
if action.is_local:
return self.client.local.execute_action(action=action, request_data=params)

actions = self.get(actions=[action])
if len(actions) == 0:
raise ComposioClientError(f"Action {action} not found")

(action_model,) = actions
action_req_schema = action_model.parameters.properties
modified_params: t.Dict[str, t.Union[str, t.Dict[str, str]]] = {}
for param, value in params.items():
request_param_schema = action_req_schema.get(param)
if request_param_schema is None:
# User has sent a parameter that is not used by this action,
# so we can ignore it.
continue

file_readable = request_param_schema.get("file_readable", False)
file_uploadable = _check_file_uploadable(request_param_schema)

if file_readable and isinstance(value, str) and os.path.isfile(value):
with open(value, "rb") as file:
file_content = file.read()
try:
modified_params[param] = file_content.decode("utf-8")
except UnicodeDecodeError:
# If decoding fails, treat as binary and encode in base64
modified_params[param] = base64.b64encode(file_content).decode(
"utf-8"
)
elif file_uploadable and isinstance(value, str):
if not os.path.isfile(value):
raise ValueError(f"Attachment File with path `{value}` not found.")

with open(value, "rb") as file:
file_content = file.read()

modified_params[param] = {
"name": os.path.basename(value),
"content": base64.b64encode(file_content).decode("utf-8"),
}
else:
modified_params[param] = value

if action.no_auth:
return self._raise_if_required(
self.client.long_timeout_http.post(
url=str(self.endpoint / action.slug / "execute"),
json={
"appName": action.app,
"input": modified_params,
"input": params,
"text": text,
"sessionInfo": {
"sessionId": session_id,
Expand All @@ -1315,7 +1260,7 @@ def execute(
"connectedAccountId": connected_account,
"entityId": entity_id,
"appName": action.app,
"input": modified_params,
"input": params,
"text": text,
"authConfig": self._serialize_auth(auth=auth),
},

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug Fix: Verify the necessity of 'modified_params' before reverting to 'params' to prevent potential logical errors.

🔧 Suggested Code Diff:
- "input": params,
+ "input": modified_params,
📝 Committable Code Suggestion

‼️ Ensure you review the code suggestion before committing it to the branch. Make sure it replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
"connectedAccountId": connected_account,
"entityId": entity_id,
"appName": action.app,
"input": modified_params,
"input": params,
"text": text,
"authConfig": self._serialize_auth(auth=auth),
},
{
"connectedAccountId": connected_account,
"entityId": entity_id,
"appName": action.app,
"input": modified_params, # Ensure modified_params is correctly set and validated
"text": text,
"authConfig": self._serialize_auth(auth=auth),
}

Expand Down
Loading
Loading