```diff
--- test_files/346-original.txt	2025-03-07 19:06:48
+++ test_files/346-modified.txt	2025-03-07 19:06:48
@@ -2,16 +2,31 @@
 import hashlib
 import json
 import os
+import traceback
+import typing as t
 from abc import ABC, abstractmethod
-from typing import Generic, List, Type, TypeVar, Union
+from typing import Generic, List, Optional, Type, TypeVar, Union
 
 import inflection
 import jsonref
 from pydantic import BaseModel
 
+# from composio.client.collections import _check_file_uploadable
+from composio.client.enums.base import SentinalObject
 from composio.utils.logging import WithLogger
 
 
+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 generate_hashed_appId(input_string):
     # Generate a 32-character hash using MD5
     hash_object = hashlib.md5(input_string.encode())
@@ -26,8 +41,8 @@
 ResponseType = TypeVar("ResponseType", bound=BaseModel)
 
 
-class Action(ABC, WithLogger, Generic[RequestType, ResponseType]):
-    """Action"""
+class Action(ABC, SentinalObject, WithLogger, Generic[RequestType, ResponseType]):
+    """Action abstraction."""
 
     _history_maintains: bool = False
     _display_name: str = ""  # Add an internal variable to hold the display name
@@ -36,7 +51,10 @@
     _tags: List[str] = []  # Placeholder for tags
     _tool_name: str = ""
 
+    # For workspace
     run_on_shell: bool = False
+    requires: Optional[List[str]] = None  # List of python dependencies
+    module: Optional[str] = None  # File where this tool is defined
 
     @property
     def tool_name(self) -> str:
@@ -84,7 +102,7 @@
 
     @abstractmethod
     def execute(
-        self, request_data: RequestType, authorisation_data: dict
+        self, request_data: RequestType, metadata: dict
     ) -> Union[dict, ResponseType]:
         pass
 
@@ -125,30 +143,42 @@
             "description": (
                 self.__class__.__doc__ if self.__class__.__doc__ else self.action_name
             ),
-            "parameters": jsonref.loads(
-                json.dumps(self.request_schema.model_json_schema(by_alias=False))
+            "parameters": json.loads(
+                jsonref.dumps(
+                    jsonref.replace_refs(
+                        self.request_schema.model_json_schema(by_alias=False),
+                        lazy_load=False,
+                    )
+                )
             ),
-            "response": jsonref.loads(
-                json.dumps(self.response_schema.model_json_schema())
+            "response": json.loads(
+                jsonref.dumps(
+                    jsonref.replace_refs(
+                        self.response_schema.model_json_schema(),
+                        lazy_load=False,
+                    )
+                )
             ),
         }
         return action_schema
 
     def execute_action(
-        self, request_data: RequestType, metadata: dict
+        self,
+        request_data: RequestType,
+        metadata: dict,
     ) -> Union[dict, ResponseType]:
-        # req = self._request_schema.model_validate_json(json_data=json.dumps(request_data))
-
-        # print(f"Executing {self.__class__.__name__} on Tool: {self.tool_name} with request data {request_data} and meta data {metadata}")
         try:
-            request_schema = self.request_schema  # type: ignore
-            modified_request_data = {}
-
+            modified_request_data: t.Dict[str, t.Union[str, t.Dict[str, str]]] = {}
             for param, value in request_data.items():  # type: ignore
-                annotations = request_schema.model_fields[param].json_schema_extra
+                if param not in self.request_schema.model_fields:
+                    raise ValueError(
+                        f"Invalid param `{param}` for action `{self.get_tool_merged_action_name().upper()}`"
+                    )
+                annotations = self.request_schema.model_fields[param].json_schema_extra
                 file_readable = annotations is not None and annotations.get(  # type: ignore
                     "file_readable", False
                 )
+                file_uploadable = _check_file_uploadable(param)
                 if file_readable and isinstance(value, str) and os.path.isfile(value):
                     with open(value, "rb") as file:
                         file_content = file.read()
@@ -162,21 +192,43 @@
                             modified_request_data[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_request_data[param] = {
+                        "name": os.path.basename(value),
+                        "content": base64.b64encode(file_content).decode("utf-8"),
+                    }
                 else:
                     modified_request_data[param] = value
 
-            req = request_schema.model_validate_json(
-                json_data=json.dumps(modified_request_data)
+            return self.execute(
+                request_data=self.request_schema.model_validate_json(
+                    json_data=json.dumps(
+                        modified_request_data,
+                    )
+                ),
+                metadata=metadata,
             )
-            return self.execute(req, metadata)  # type: ignore
         except json.JSONDecodeError as e:
-            # logger.error(f"Error executing {action.__name__} on Tool: {tool_name}: {e}\n{traceback.format_exc()}")
             return {
                 "status": "failure",
                 "details": f"Could not parse response with error: {e}. Please contact the tool developer.",
             }
-            # logger.error(f"Error executing {action.__name__} on Tool: {tool_name}: {e}\n{traceback.format_exc()}")
         except Exception as e:
+            self.logger.error(
+                "Error while executing `%s` with parameters `%s`; Error: %s",
+                self.display_name,
+                request_data,
+                traceback.format_exc(),
+            )
             return {
                 "status": "failure",
                 "details": "Error executing action with error: " + str(e),
```
