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: swarm plugin added #1218

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
127 changes: 127 additions & 0 deletions python/plugins/swarm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
## 🚀🔗 Leveraging Swarm with Composio

Integrate Swarm agents with Composio to enable direct interaction with external applications, enhancing their capabilities through strongly-typed, validated tools.

### Objective

- **Automate GitHub operations** using type-safe instructions via Swarm's Tool system.
- Demonstrate how to use Composio's tools with Swarm's strict type checking and validation.

### Installation and Setup

Requires Python 3.10+

Install the necessary packages and connect your GitHub account to enable agent interactions with GitHub:

```bash
# Install Composio Swarm package
pip install composio-swarm

# Connect your GitHub account
composio add github

# View available applications you can connect with
composio apps
```

### Usage Steps

#### 1. Import Required Packages

Set up your environment by importing the necessary components from Composio & Pydantic-AI:

```python
import os
from dotenv import load_dotenv
from swarm import Agent
from swarm.repl import run_demo_loop
from composio_swarm import ComposioToolSet, Action

load_dotenv()
```

#### 2. Initialize Tools with Composio

Configure and fetch GitHub tools provided by Composio:

```python
# Initialize toolset
composio_toolset = ComposioToolSet()

# Configure max retries for specific tools
max_retries = {
Action.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER: 5, # More retries for starring
Action.GITHUB_CREATE_REPOSITORY: 2 # Fewer retries for creation
}

# Get GitHub tools with retry configuration
tools = composio_toolset.get_tools(
actions=[Action.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER],
max_retries=max_retries,
default_max_retries=3 # Default retries for tools not specified in max_retries
)
```

The `max_retries` parameter lets you configure retry attempts per tool, with a default fallback for unspecified tools.

#### 3. Set Up the Swarm Agent

Create and configure a Swarm agent with the Composio tools:

```python
# Create an agent with the tools
agent = Agent(
name="GitHub Star Agent",
instructions="You are an agent that stars a repository on GitHub.",
functions=tools,
)
```

#### 4. Execute Tasks

Run your agent:

```python
run_demo_loop(agent, stream=True)
```

### Advanced Usage

The integration supports more complex scenarios:

```python
# Using multiple tools
tools = composio_toolset.get_tools(
actions=[
Action.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER,
Action.GITHUB_CREATE_REPOSITORY
],
max_retries={
Action.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER: 5,
Action.GITHUB_CREATE_REPOSITORY: 2
}
)

# Filtering tools by tags
tools = composio_toolset.get_tools(
tags=["github", "repository"],
default_max_retries=3
)

# Using app-specific tools
tools = composio_toolset.get_tools(
apps=[App.GITHUB],
max_retries={
Action.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER: 5
}
)
```

### Best Practices

1. Always use proper type hints in your code
2. Handle validation errors appropriately
3. Use the latest version of both Swarm and Composio
4. Leverage async operations for better performance
5. Keep your API keys secure using environment variables
6. Configure retries based on the specific needs of each tool
14 changes: 14 additions & 0 deletions python/plugins/swarm/composio_swarm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from composio_swarm.toolset import ComposioToolSet

from composio import Action, App, Tag, Trigger, WorkspaceType, action


__all__ = (
"Action",
"App",
"Tag",
"Trigger",
"WorkspaceType",
"action",
"ComposioToolSet",
)
179 changes: 179 additions & 0 deletions python/plugins/swarm/composio_swarm/toolset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import types
import typing as t
import warnings
from inspect import Signature

import typing_extensions as te

from composio import ActionType, AppType, TagType
from composio.tools import ComposioToolSet as BaseComposioToolSet
from composio.tools.toolset import ProcessorsType
from composio.utils import help_msg
from composio.utils.shared import get_signature_format_from_schema_params


class StructuredTool:
def __init__(self, name: str, description: str, params: t.Dict, func: t.Callable):
self.name = name
self.description = description
self.params = params
self.func = func
Comment on lines +15 to +20

Choose a reason for hiding this comment

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

The StructuredTool class is defined but its attributes are not properly typed with type hints, which could lead to runtime type errors. Add type annotations for class attributes.

📝 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
class StructuredTool:
def __init__(self, name: str, description: str, params: t.Dict, func: t.Callable):
self.name = name
self.description = description
self.params = params
self.func = func
class StructuredTool:
name: str
description: str
params: t.Dict
func: t.Callable
def __init__(self, name: str, description: str, params: t.Dict, func: t.Callable):
self.name = name
self.description = description
self.params = params
self.func = func



class ComposioToolSet(
BaseComposioToolSet,
runtime="langchain",
Copy link
Collaborator

Choose a reason for hiding this comment

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

The class is inheriting from BaseComposioToolSet with runtime="langchain" but this should be runtime="swarm" for consistency.

description_char_limit=1024,
action_name_char_limit=64,
):
"""
Composio toolset for Langchain framework.

Example:
```python
Copy link
Collaborator

Choose a reason for hiding this comment

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

The docstring example shows Langchain usage but this is a Swarm plugin. Please update the example to show Swarm-specific usage instead.

import os
import dotenv

from composio_langchain import App, ComposioToolSet
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_openai import ChatOpenAI

from langchain import hub


# Load environment variables from .env
dotenv.load_dotenv()


# Pull relevant agent model.
prompt = hub.pull("hwchase17/openai-functions-agent")

# Initialize tools.
openai_client = ChatOpenAI(api_key=os.environ["OPENAI_API_KEY"])
composio_toolset = ComposioToolSet()

# Get All the tools
tools = composio_toolset.get_tools(apps=[App.GITHUB])

# Define task
task = "Star a repo composiohq/docs on GitHub"

# Define agent
agent = create_openai_functions_agent(openai_client, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# Execute using agent_executor
agent_executor.invoke({"input": task})
```
"""

def _wrap_action(
self,
action: str,
description: str,
schema_params: t.Dict,
entity_id: t.Optional[str] = None,
):
Copy link
Collaborator

Choose a reason for hiding this comment

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

The _wrap_action method is missing a return type hint. Consider adding -> t.Callable to make the type signature complete.

def function(**kwargs: t.Any) -> t.Dict:
"""Wrapper function for composio action."""
self.logger.debug(f"Executing action: {action} with params: {kwargs}")
return self.execute_action(
action=action,
params=kwargs,
entity_id=entity_id or self.entity_id,
_check_requested_actions=True,
)

action_func = types.FunctionType(
function.__code__,
globals=globals(),
name=action,
closure=function.__closure__,
)
action_func.__signature__ = Signature( # type: ignore
parameters=get_signature_format_from_schema_params(
schema_params=schema_params
)
)

action_func.__doc__ = description

return action_func

def _wrap_tool(
self,
schema: t.Dict[str, t.Any],
entity_id: t.Optional[str] = None,
) -> StructuredTool:
"""Wraps composio tool as Langchain StructuredTool object."""
action = schema["name"]
description = schema["description"]
schema_params = schema["parameters"]
action_func = self._wrap_action(
action=action,
description=description,
schema_params=schema_params,
entity_id=entity_id,
)
tool = action_func
return tool
Comment on lines +103 to +119

Choose a reason for hiding this comment

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

The _wrap_tool method returns action_func directly without using the StructuredTool class, despite declaring StructuredTool return type. This type mismatch could cause runtime errors.

📝 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
def _wrap_tool(
self,
schema: t.Dict[str, t.Any],
entity_id: t.Optional[str] = None,
) -> StructuredTool:
"""Wraps composio tool as Langchain StructuredTool object."""
action = schema["name"]
description = schema["description"]
schema_params = schema["parameters"]
action_func = self._wrap_action(
action=action,
description=description,
schema_params=schema_params,
entity_id=entity_id,
)
tool = action_func
return tool
def _wrap_tool(
self,
schema: t.Dict[str, t.Any],
entity_id: t.Optional[str] = None,
) -> StructuredTool:
"""Wraps composio tool as Langchain StructuredTool object."""
action = schema["name"]
description = schema["description"]
schema_params = schema["parameters"]
action_func = self._wrap_action(
action=action,
description=description,
schema_params=schema_params,
entity_id=entity_id,
)
tool = StructuredTool(
name=action,
description=description,
func=action_func,
)
return tool


@te.deprecated("Use `ComposioToolSet.get_tools` instead.\n", category=None)
def get_actions(
self,
actions: t.Sequence[ActionType],
entity_id: t.Optional[str] = None,
) -> t.Sequence[StructuredTool]:
"""
Get composio tools wrapped as Langchain StructuredTool objects.

:param actions: List of actions to wrap
:param entity_id: Entity ID to use for executing function calls.

:return: Composio tools wrapped as `StructuredTool` objects
"""
warnings.warn(
"Use `ComposioToolSet.get_tools` instead.\n" + help_msg(),
DeprecationWarning,
stacklevel=2,
)
return self.get_tools(actions=actions, entity_id=entity_id)

def get_tools(
self,
actions: t.Optional[t.Sequence[ActionType]] = None,
apps: t.Optional[t.Sequence[AppType]] = None,
tags: t.Optional[t.List[TagType]] = None,
entity_id: t.Optional[str] = None,
*,
processors: t.Optional[ProcessorsType] = None,
check_connected_accounts: bool = True,
) -> t.Sequence[StructuredTool]:
"""
Get composio tools wrapped as Langchain StructuredTool objects.

:param actions: List of actions to wrap
:param apps: List of apps to wrap
:param tags: Filter the apps by given tags
:param entity_id: Entity ID for the function wrapper

:return: Composio tools wrapped as `StructuredTool` objects
"""
self.validate_tools(apps=apps, actions=actions, tags=tags)
if processors is not None:
self._merge_processors(processors)
return [
self._wrap_tool(
schema=tool.model_dump(
exclude_none=True,
),
entity_id=entity_id or self.entity_id,
)
for tool in self.get_action_schemas(
actions=actions,
apps=apps,
tags=tags,
check_connected_accounts=check_connected_accounts,
_populate_requested=True,
)
]
30 changes: 30 additions & 0 deletions python/plugins/swarm/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
Setup configuration for Composio Pydantic AI plugin
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

The docstring incorrectly mentions 'Pydantic AI plugin' but this is a Swarm plugin. Please update the docstring to reflect the correct plugin type.


from pathlib import Path

from setuptools import setup


setup(
name="composio_swarm",
version="0.6.12",
author="Prathit",
author_email="[email protected]",
description="Use Composio to get array of tools for Swarm",
long_description=(Path(__file__).parent / "README.md").read_text(encoding="utf-8"),
long_description_content_type="text/markdown",
url="https://github.com/ComposioHQ/composio",
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
],
python_requires=">=3.10,<4",
install_requires=[
"composio_core>=0.6.11,<0.7.0",
"swarm @ git+https://github.com/openai/swarm.git",
Comment on lines +26 to +27

Choose a reason for hiding this comment

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

The swarm package dependency is specified via git URL without pinning a specific version/commit, which could lead to breaking changes if upstream changes occur. Should pin to specific commit hash.

📝 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
"composio_core>=0.6.11,<0.7.0",
"swarm @ git+https://github.com/openai/swarm.git",
"composio_core>=0.6.11,<0.7.0",
"swarm @ git+https://github.com/openai/swarm.git@a1b2c3d4e5f6"

Copy link
Collaborator

Choose a reason for hiding this comment

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

Using a git URL for the swarm dependency is not recommended as it can be unstable. Consider using a specific version or at least pinning to a specific commit hash for better stability.

],
include_package_data=True,
)
29 changes: 29 additions & 0 deletions python/plugins/swarm/swarm_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from composio_swarm import Action, ComposioToolSet
from dotenv import load_dotenv
from swarm import Agent
from swarm.repl import run_demo_loop


load_dotenv()


def main() -> None:
composio_toolset = ComposioToolSet()

# Get All the tools
tools = composio_toolset.get_tools(
actions=[Action.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER]
)

# Create a new agent
agent = Agent(
name="GitHub Star Agent",
instructions="You are an agent that stars a repository on GitHub.",
functions=tools,
)

run_demo_loop(agent, stream=True)


if __name__ == "__main__":
main()
Loading