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: Implement lockfiles for composio toolset #1196

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/composio/client/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,7 @@ class ActionModel(BaseModel):

name: str
display_name: t.Optional[str] = None
version: str
tushar-composio marked this conversation as resolved.
Show resolved Hide resolved
parameters: ActionParametersModel
response: ActionResponseModel
appName: str
Expand Down
5 changes: 5 additions & 0 deletions python/composio/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,8 @@
"""
Name of the pusher cluster.
"""

LOCKFILE_PATH = Path("./.composio.lock")
"""
Path to the .composio.lock file.
"""
2 changes: 2 additions & 0 deletions python/composio/tools/base/abs.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ def _generate_schema(cls) -> None:
cls._schema = {
"name": cls.name,
"enum": cls.enum,
# TODO: get version from api
"version": "0_1",
tushar-composio marked this conversation as resolved.
Show resolved Hide resolved
tushar-composio marked this conversation as resolved.
Show resolved Hide resolved
"appName": cls.tool,
"appId": generate_app_id(cls.tool),
"tags": cls.tags(),
Expand Down
2 changes: 2 additions & 0 deletions python/composio/tools/local/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def get_action_schemas(

for schema in action_schemas:
schema["name"] = schema["enum"]
# TODO: Put version in local tool schemas
schema["version"] = "0_1"
tushar-composio marked this conversation as resolved.
Show resolved Hide resolved

return action_schemas

Expand Down
44 changes: 44 additions & 0 deletions python/composio/tools/toolset.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from pathlib import Path

import typing_extensions as te
import yaml
from pydantic import BaseModel

tushar-composio marked this conversation as resolved.
Show resolved Hide resolved
from composio import Action, ActionType, App, AppType, TagType
Expand Down Expand Up @@ -51,6 +52,7 @@
LOCAL_CACHE_DIRECTORY,
LOCAL_OUTPUT_FILE_DIRECTORY_NAME,
USER_DATA_FILE_NAME,
LOCKFILE_PATH,

Choose a reason for hiding this comment

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

The LOCKFILE_PATH variable is defined but not imported in the toolset.py file, which may cause a NameError during execution.

)
from composio.exceptions import ApiKeyNotProvidedError, ComposioSDKError
from composio.storage.user import UserData
Expand Down Expand Up @@ -198,6 +200,7 @@ def __init__(
connected_account_ids: t.Optional[t.Dict[AppType, str]] = None,
*,
max_retries: int = 3,
lock: bool = True,
**kwargs: t.Any,
) -> None:
"""
Expand Down Expand Up @@ -330,6 +333,13 @@ def _limit_file_search_response(response: t.Dict) -> t.Dict:
# composio_openai's Toolset.
self._requested_actions: t.List[str] = []

if lock:
self.locking_enabled = True
self._tool_versions = self.load_tool_versions_from_lockfile()
else:
self.locking_enabled = False
self._tool_versions = {}

def _validating_connection_ids(
self,
connected_account_ids: t.Dict[AppType, str],
Expand Down Expand Up @@ -1019,6 +1029,7 @@ def get_action_schemas(
*,
check_connected_accounts: bool = True,
_populate_requested: bool = False,
# TODO: take manual override version as parameter
) -> t.List[ActionModel]:
runtime_actions = t.cast(
t.List[t.Type[LocalAction]],
Expand Down Expand Up @@ -1054,6 +1065,7 @@ def get_action_schemas(
apps=remote_apps,
actions=remote_actions,
tags=tags,
# TODO: use tool version when fetching actions
)
if check_connected_accounts:
for item in remote_items:
Expand Down Expand Up @@ -1088,6 +1100,10 @@ def get_action_schemas(
action_names = [item.name for item in items]
self._requested_actions += action_names

if self.locking_enabled:
self._tool_versions |= {tool.name: tool.version for tool in items}
self.store_tool_versions_to_lockfile()

return items

def _process_schema(self, action_item: ActionModel) -> ActionModel:
Expand Down Expand Up @@ -1660,6 +1676,34 @@ def _validate_auth_config(

return auth_config, False

@staticmethod
def load_tool_versions_from_lockfile() -> t.Dict[str, str]:
"""Load tool versions from lockfile."""
if not LOCKFILE_PATH.exists():
return {}

with open(LOCKFILE_PATH, encoding="utf-8") as file:
tool_versions = yaml.safe_load(file)

# Validate lockfile
if not isinstance(tool_versions, dict):
raise ComposioSDKError(
f"Invalid lockfile format, expected dict, got {type(tool_versions)}"
)

for tool in tool_versions.values():
if not isinstance(tool, str):
raise ComposioSDKError(
f"Invalid lockfile format, expected version to be string, got {tool!r}"
)

return tool_versions

def store_tool_versions_to_lockfile(self) -> None:
"""Store tool versions to lockfile."""
with open(LOCKFILE_PATH, "w", encoding="utf-8") as file:
yaml.safe_dump(self._tool_versions, file)

Comment on lines +1681 to +1708

Choose a reason for hiding this comment

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

Opening files with write mode without proper error handling could fail silently or leave files in an inconsistent state. Add try-except to handle IOError/OSError when reading/writing lockfile.


def _write_file(file_path: t.Union[str, os.PathLike], content: t.Union[str, bytes]):
"""Write content to a file."""
Expand Down
Loading