Skip to content

Commit

Permalink
Load all tools on plugins (#601)
Browse files Browse the repository at this point in the history
Co-authored-by: Karan Vaidya <kaavee315@gmail.com>
  • Loading branch information
angrybayblade and kaavee315 authored Sep 30, 2024
1 parent d1a873a commit 3aa67f1
Show file tree
Hide file tree
Showing 19 changed files with 163 additions and 455 deletions.
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
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

0 comments on commit 3aa67f1

Please sign in to comment.