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

Load all tools on plugins #601

Merged
merged 18 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 6 additions & 3 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
run:
working-directory: ${{ inputs.working-directory }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.10"]
Expand Down Expand Up @@ -82,6 +83,7 @@ jobs:
LISTENNOTES_API_KEY: ${{secrets.LISTENNOTES_API_KEY}}
COMPOSIO_API_KEY: ${{ inputs.api_key || secrets.COMPOSIO_API_KEY_RELEASE }}
COMPOSIO_BASE_URL: ${{ inputs.base_url || secrets.COMPOSIO_BASE_URL || 'https://backend.composio.dev/api' }}
# TODO(@kaavee): Add Anthropic API key
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

run: |
sudo apt-get update --fix-missing
sudo apt-get autoremove
Expand All @@ -91,10 +93,11 @@ jobs:
python -m pip install plugins/${{matrix.package}}
python -m pip install '.[all]'
python -m pip install 'numpy<2' python-dotenv

composio apps update
python -m pip install unstructured

CI=false COMPOSIO_BASE_URL=${{ env.COMPOSIO_BASE_URL }} PLUGIN_TO_TEST=${{matrix.package}} COMPOSIO_LOGGING_LEVEL='debug' pytest -vv tests/test_example.py
COMPOSIO_BASE_URL=${{ env.COMPOSIO_BASE_URL }} COMPOSIO_API_KEY=${{ env.COMPOSIO_API_KEY }} COMPOSIO_LOGGING_LEVEL='debug' composio apps update
COMPOSIO_BASE_URL=${{ env.COMPOSIO_BASE_URL }} COMPOSIO_API_KEY=${{ env.COMPOSIO_API_KEY }} COMPOSIO_LOGGING_LEVEL='debug' PLUGIN_TO_TEST=${{matrix.package}} CI=false pytest -vv tests/test_example.py
COMPOSIO_BASE_URL=${{ env.COMPOSIO_BASE_URL }} COMPOSIO_API_KEY=${{ env.COMPOSIO_API_KEY }} COMPOSIO_LOGGING_LEVEL='debug' PLUGIN_TO_TEST=${{matrix.package}} CI=false pytest -vv tests/test_load_tools.py
- name: Slack Notification on Failure
if: ${{ failure() && github.ref == 'refs/heads/master' && !contains(github.event.head_commit.message, 'release') && !contains(github.event.head_commit.message, 'Release') && !inputs.dont_notify }}
uses: rtCamp/action-slack-notify@v2
Expand Down
2 changes: 2 additions & 0 deletions python/composio/client/enums/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ def __str__(self) -> str:
"""String representation."""
return t.cast(str, self._slug)

__repr__ = __str__

def __eq__(self, other: object) -> bool:
"""Check equivalence of two objects."""
if not isinstance(other, (str, _AnnotatedEnum)):
Expand Down
6 changes: 6 additions & 0 deletions python/composio/tools/base/abs.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ def schema(self) -> t.Dict:
"description"
] += f" Note: choose value only from following options - {prop['enum']}"

if "anyOf" in prop:
typedef, *_ = [
td for td in prop["anyOf"] if td.get("type", "null") != "null"
]
prop["type"] = typedef["type"]

request["properties"] = properties
return request

Expand Down
6 changes: 3 additions & 3 deletions python/composio/tools/toolset.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def __init__(
needs to be JSON serialisable dictionary. For example

```python
toolset = ComposioToolset(
toolset = ComposioToolSet(
...,
metadata={
App.IMAGEANALYSER: {
Expand Down Expand Up @@ -196,7 +196,7 @@ def _limit_file_search_response(response: t.Dict) -> t.Dict:
response["results"] = response["results"][:100]
return response

toolset = ComposioToolset(
toolset = ComposioToolSet(
...,
processors={
"pre": {
Expand Down Expand Up @@ -724,7 +724,7 @@ def _process_schema(self, action_item: ActionModel) -> ActionModel:
required_params = action_item.parameters.required or []
for param_name, param_details in action_item.parameters.properties.items():
if param_details.get("title") == "FileType" and all(
fprop in param_details.get("properties")
fprop in param_details.get("properties", {})
for fprop in ("name", "content")
):
action_item.parameters.properties[param_name].pop("properties")
Expand Down
63 changes: 45 additions & 18 deletions python/composio/utils/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@
"boolean": False,
"object": {},
"array": [],
"null": None,
}

reserved_names = ["validate"]


def json_schema_to_pydantic_type(
json_schema: t.Dict[str, t.Any],
Expand Down Expand Up @@ -91,7 +94,7 @@ def json_schema_to_pydantic_field(
name: str,
json_schema: t.Dict[str, t.Any],
required: t.List[str],
) -> t.Tuple[t.Type, FieldInfo]:
) -> t.Tuple[str, t.Type, FieldInfo]:
"""
Converts a JSON schema property to a Pydantic field definition.

Expand All @@ -109,7 +112,16 @@ def json_schema_to_pydantic_field(

examples = json_schema.get("examples", [])
default = json_schema.get("default")

# Check if the field name is a reserved Pydantic name
if name in reserved_names:
name = f"{name}_"
alias = name
else:
alias = None

return (
name,
t.cast(
t.Type,
json_schema_to_pydantic_type(
Expand All @@ -124,6 +136,7 @@ def json_schema_to_pydantic_field(
if (name in required or json_schema.get("required", False))
else default
),
alias=alias,
),
)

Expand All @@ -144,10 +157,12 @@ def json_schema_to_fields_dict(json_schema: t.Dict[str, t.Any]) -> t.Dict[str, t
```

"""
field_definitions = {
name: json_schema_to_pydantic_field(name, prop, json_schema.get("required", []))
for name, prop in json_schema.get("properties", {}).items()
}
field_definitions = {}
for name, prop in json_schema.get("properties", {}).items():
updated_name, pydantic_type, pydantic_field = json_schema_to_pydantic_field(
name, prop, json_schema.get("required", [])
)
field_definitions[updated_name] = (pydantic_type, pydantic_field)
return field_definitions # type: ignore


Expand All @@ -159,10 +174,12 @@ def json_schema_to_model(json_schema: t.Dict[str, t.Any]) -> t.Type[BaseModel]:
:return: Pydantic `BaseModel` type
"""
model_name = json_schema.get("title")
field_definitions = {
name: json_schema_to_pydantic_field(name, prop, json_schema.get("required", []))
for name, prop in json_schema.get("properties", {}).items()
}
field_definitions = {}
for name, prop in json_schema.get("properties", {}).items():
updated_name, pydantic_type, pydantic_field = json_schema_to_pydantic_field(
name, prop, json_schema.get("required", [])
)
field_definitions[updated_name] = (pydantic_type, pydantic_field)
return create_model(model_name, **field_definitions) # type: ignore


Expand Down Expand Up @@ -209,21 +226,28 @@ def pydantic_model_from_param_schema(param_schema: t.Dict) -> t.Type:
else:
signature_prop_type = pydantic_model_from_param_schema(prop_info)

field_kwargs = {
"description": prop_info.get(
"description", prop_info.get("desc", prop_title)
),
}

# Add alias if the field name is a reserved Pydantic name
if prop_name in reserved_names:
field_kwargs["alias"] = prop_name
field_kwargs["title"] = f"{prop_name}_"
else:
field_kwargs["title"] = prop_title

if prop_name in required_props or prop_info.get("required", False):
required_fields[prop_name] = (
signature_prop_type,
Field(
...,
title=prop_title,
description=prop_info.get(
"description", prop_info.get("desc", prop_title)
),
),
Field(..., **field_kwargs),
)
else:
optional_fields[prop_name] = (
signature_prop_type,
Field(title=prop_title, default=prop_default),
Field(default=prop_default, **field_kwargs),
)

if not required_fields and not optional_fields:
Expand Down Expand Up @@ -290,7 +314,10 @@ def demo_function(
param_default = param_schema.get("default", FALLBACK_VALUES[param_type])
else:
signature_param_type = pydantic_model_from_param_schema(param_schema)
param_default = param_schema.get("default", FALLBACK_VALUES[param_type])
if param_type is None or param_type == "null":
param_default = None
else:
param_default = param_schema.get("default", FALLBACK_VALUES[param_type])

param_annotation = signature_param_type
is_required = param_name in required_params or param_schema.get(
Expand Down
6 changes: 3 additions & 3 deletions python/plugins/autogen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Setup your environment by importing necessary packages and configuring the Autog

```python
from autogen import AssistantAgent, UserProxyAgent
from composio_autogen import ComposioToolset, App, Action
from composio_autogen import ComposioToolSet, App, Action
import os

# Configuration for the language model
Expand All @@ -52,10 +52,10 @@ user_proxy = UserProxyAgent(
Initialize and register the necessary tools for interacting with GitHub.

```python
from composio_autogen import ComposioToolset, App, Action
from composio_autogen import ComposioToolSet, App, Action

# Initialize Composio Toolset
composio_tools = ComposioToolset()
composio_tools = ComposioToolSet()

# Register tools with appropriate executors
composio_tools.register_tools(tools=[App.GITHUB], caller=chatbot, executor=user_proxy)
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/camel/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
"Operating System :: OS Independent",
],
python_requires=">=3.9,<4",
install_requires=["composio_core==0.5.28", "camel-ai>=0.1.5.7"],
install_requires=["composio_core==0.5.26", "camel-ai>=0.1.5.7", "pillow"],
include_package_data=True,
)
4 changes: 2 additions & 2 deletions python/plugins/claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ client = anthropic.Anthropic()

This step involves fetching and integrating GitHub tools provided by Composio, enabling enhanced functionality for LangChain operations.
```python
from composio_claude import App, ComposioToolset
from composio_claude import App, ComposioToolSet

toolset = ComposioToolset()
toolset = ComposioToolSet()
actions = toolset.get_tools(tools=App.GITHUB)
```

Expand Down
Loading
Loading