538 lines
16 KiB
Bash
538 lines
16 KiB
Bash
#!/bin/bash
|
|
|
|
# Check if provider name is provided
|
|
if [ -z "$1" ]; then
|
|
echo "Please provide a provider name"
|
|
echo "Usage: ./scripts/create-provider.sh <provider-name> [--agentic] [--output-dir <directory>]"
|
|
exit 1
|
|
fi
|
|
|
|
PROVIDER_NAME=$1
|
|
shift
|
|
|
|
# Default output directory
|
|
OUTPUT_DIR="python/providers"
|
|
IS_AGENTIC=false
|
|
|
|
# Parse optional arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--agentic)
|
|
IS_AGENTIC=true
|
|
shift
|
|
;;
|
|
--output-dir)
|
|
OUTPUT_DIR="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
echo "Usage: ./scripts/create-provider.sh <provider-name> [--agentic] [--output-dir <directory>]"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
PROVIDER_PATH="$OUTPUT_DIR/$PROVIDER_NAME"
|
|
# Convert to title case (e.g., myai -> Myai)
|
|
TITLE_CASE_PROVIDER_NAME="$(tr '[:lower:]' '[:upper:]' <<< ${PROVIDER_NAME:0:1})${PROVIDER_NAME:1}"
|
|
PACKAGE_NAME="composio_$PROVIDER_NAME"
|
|
|
|
# Check if provider directory already exists
|
|
if [ -d "$PROVIDER_PATH" ]; then
|
|
echo "❌ Provider '$PROVIDER_NAME' already exists in $PROVIDER_PATH"
|
|
exit 1
|
|
fi
|
|
|
|
# Create directory structure
|
|
mkdir -p "$PROVIDER_PATH/$PACKAGE_NAME"
|
|
|
|
# Create pyproject.toml
|
|
cat > "$PROVIDER_PATH/pyproject.toml" << EOL
|
|
[project]
|
|
name = "$PACKAGE_NAME"
|
|
version = "0.1.0"
|
|
description = "${IS_AGENTIC:+Agentic }Provider for ${TITLE_CASE_PROVIDER_NAME} in the Composio SDK"
|
|
readme = "README.md"
|
|
requires-python = ">=3.10,<4"
|
|
authors = [
|
|
{ name = "Composio", email = "tech@composio.dev" }
|
|
]
|
|
classifiers = [
|
|
"Programming Language :: Python :: 3",
|
|
"License :: OSI Approved :: Apache Software License",
|
|
"Operating System :: OS Independent",
|
|
]
|
|
dependencies = [
|
|
"composio",
|
|
]
|
|
|
|
[project.urls]
|
|
Homepage = "https://github.com/ComposioHQ/composio"
|
|
EOL
|
|
|
|
# Create setup.py for backwards compatibility
|
|
cat > "$PROVIDER_PATH/setup.py" << EOL
|
|
from setuptools import setup, find_packages
|
|
|
|
setup(
|
|
name="$PACKAGE_NAME",
|
|
version="0.1.0",
|
|
packages=find_packages(),
|
|
install_requires=[
|
|
"composio>=0.1.0",
|
|
],
|
|
python_requires=">=3.10,<4",
|
|
author="Your Name",
|
|
author_email="you@example.com",
|
|
description="${IS_AGENTIC:+Agentic }Provider for ${TITLE_CASE_PROVIDER_NAME} in the Composio SDK",
|
|
long_description=open("README.md").read(),
|
|
long_description_content_type="text/markdown",
|
|
url="https://github.com/composio/composio",
|
|
classifiers=[
|
|
"Development Status :: 3 - Alpha",
|
|
"Intended Audience :: Developers",
|
|
"License :: OSI Approved :: Apache Software License",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3.10",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Programming Language :: Python :: 3.12",
|
|
],
|
|
)
|
|
EOL
|
|
|
|
# Create __init__.py
|
|
cat > "$PROVIDER_PATH/$PACKAGE_NAME/__init__.py" << EOL
|
|
"""
|
|
${TITLE_CASE_PROVIDER_NAME} Provider for composio SDK.
|
|
"""
|
|
|
|
from .provider import ${TITLE_CASE_PROVIDER_NAME}Provider
|
|
|
|
__all__ = ["${TITLE_CASE_PROVIDER_NAME}Provider"]
|
|
EOL
|
|
|
|
# Create provider.py
|
|
if [ "$IS_AGENTIC" = true ]; then
|
|
cat > "$PROVIDER_PATH/$PACKAGE_NAME/provider.py" << EOL
|
|
"""
|
|
${TITLE_CASE_PROVIDER_NAME} Provider implementation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import typing as t
|
|
|
|
from composio.core.provider import AgenticProvider
|
|
from composio.core.provider.agentic import AgenticProviderExecuteFn
|
|
from composio.types import Tool
|
|
|
|
|
|
# Define your tool format
|
|
class ${TITLE_CASE_PROVIDER_NAME}Tool:
|
|
"""${TITLE_CASE_PROVIDER_NAME} tool format"""
|
|
def __init__(self, name: str, description: str, execute: t.Callable, schema: dict):
|
|
self.name = name
|
|
self.description = description
|
|
self.execute = execute
|
|
self.schema = schema
|
|
|
|
|
|
class ${TITLE_CASE_PROVIDER_NAME}Provider(
|
|
AgenticProvider[${TITLE_CASE_PROVIDER_NAME}Tool, t.List[${TITLE_CASE_PROVIDER_NAME}Tool]],
|
|
name="${PROVIDER_NAME}"
|
|
):
|
|
"""
|
|
Composio toolset for ${TITLE_CASE_PROVIDER_NAME} framework.
|
|
"""
|
|
|
|
def wrap_tool(
|
|
self,
|
|
tool: Tool,
|
|
execute_tool: AgenticProviderExecuteFn
|
|
) -> ${TITLE_CASE_PROVIDER_NAME}Tool:
|
|
"""Wrap a tool in the ${PROVIDER_NAME} format."""
|
|
def execute_wrapper(**kwargs) -> t.Dict:
|
|
result = execute_tool(tool.slug, kwargs)
|
|
if not result.get("successful", False):
|
|
raise Exception(result.get("error", "Tool execution failed"))
|
|
return result.get("data", {})
|
|
|
|
return ${TITLE_CASE_PROVIDER_NAME}Tool(
|
|
name=tool.slug,
|
|
description=tool.description or "",
|
|
execute=execute_wrapper,
|
|
schema=tool.input_parameters
|
|
)
|
|
|
|
def wrap_tools(
|
|
self,
|
|
tools: t.Sequence[Tool],
|
|
execute_tool: AgenticProviderExecuteFn
|
|
) -> t.List[${TITLE_CASE_PROVIDER_NAME}Tool]:
|
|
"""
|
|
Get composio tools wrapped as a list of ${TITLE_CASE_PROVIDER_NAME} tools.
|
|
"""
|
|
return [self.wrap_tool(tool, execute_tool) for tool in tools]
|
|
EOL
|
|
else
|
|
cat > "$PROVIDER_PATH/$PACKAGE_NAME/provider.py" << EOL
|
|
"""
|
|
${TITLE_CASE_PROVIDER_NAME} Provider implementation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import typing as t
|
|
|
|
from composio.core.provider import NonAgenticProvider
|
|
from composio.types import Tool, Modifiers, ToolExecutionResponse
|
|
|
|
|
|
# Define your tool format
|
|
class ${TITLE_CASE_PROVIDER_NAME}Tool:
|
|
"""${TITLE_CASE_PROVIDER_NAME} tool format"""
|
|
def __init__(self, name: str, description: str, parameters: dict):
|
|
self.name = name
|
|
self.description = description
|
|
self.parameters = parameters
|
|
|
|
|
|
# Define your tool collection format
|
|
${TITLE_CASE_PROVIDER_NAME}ToolCollection: t.TypeAlias = t.List[${TITLE_CASE_PROVIDER_NAME}Tool]
|
|
|
|
|
|
class ${TITLE_CASE_PROVIDER_NAME}Provider(
|
|
NonAgenticProvider[${TITLE_CASE_PROVIDER_NAME}Tool, ${TITLE_CASE_PROVIDER_NAME}ToolCollection],
|
|
name="${PROVIDER_NAME}"
|
|
):
|
|
"""
|
|
Composio toolset for ${TITLE_CASE_PROVIDER_NAME} platform.
|
|
"""
|
|
|
|
def wrap_tool(self, tool: Tool) -> ${TITLE_CASE_PROVIDER_NAME}Tool:
|
|
"""Transform a single tool to platform format"""
|
|
return ${TITLE_CASE_PROVIDER_NAME}Tool(
|
|
name=tool.slug,
|
|
description=tool.description or "",
|
|
parameters={
|
|
"type": "object",
|
|
"properties": tool.input_parameters.get("properties", {}),
|
|
"required": tool.input_parameters.get("required", [])
|
|
}
|
|
)
|
|
|
|
def wrap_tools(self, tools: t.Sequence[Tool]) -> ${TITLE_CASE_PROVIDER_NAME}ToolCollection:
|
|
"""Transform a collection of tools"""
|
|
return [self.wrap_tool(tool) for tool in tools]
|
|
|
|
def execute_tool_call(
|
|
self,
|
|
user_id: str,
|
|
tool_call: dict,
|
|
modifiers: t.Optional[Modifiers] = None
|
|
) -> ToolExecutionResponse:
|
|
"""
|
|
Execute a tool call.
|
|
|
|
:param user_id: User ID to use for executing function calls.
|
|
:param tool_call: Tool call metadata containing 'name' and 'arguments'.
|
|
:param modifiers: Modifiers to use for executing function calls.
|
|
:return: Object containing output data from the tool call.
|
|
"""
|
|
return self.execute_tool(
|
|
slug=tool_call["name"],
|
|
arguments=tool_call["arguments"],
|
|
modifiers=modifiers,
|
|
user_id=user_id
|
|
)
|
|
|
|
def handle_tool_calls(
|
|
self,
|
|
user_id: str,
|
|
response: t.Union[dict, t.Any],
|
|
modifiers: t.Optional[Modifiers] = None
|
|
) -> t.List[ToolExecutionResponse]:
|
|
"""
|
|
Handle tool calls from ${TITLE_CASE_PROVIDER_NAME} response.
|
|
|
|
:param response: Response object from ${TITLE_CASE_PROVIDER_NAME}
|
|
:param user_id: User ID to use for executing function calls.
|
|
:param modifiers: Modifiers to use for executing function calls.
|
|
:return: A list of output objects from the tool calls.
|
|
"""
|
|
# TODO: Implement based on ${TITLE_CASE_PROVIDER_NAME}'s response format
|
|
# Example:
|
|
# outputs = []
|
|
# for tool_call in response.tool_calls:
|
|
# outputs.append(
|
|
# self.execute_tool_call(
|
|
# user_id=user_id,
|
|
# tool_call=tool_call,
|
|
# modifiers=modifiers,
|
|
# )
|
|
# )
|
|
# return outputs
|
|
raise NotImplementedError("Tool call handling not implemented yet")
|
|
EOL
|
|
fi
|
|
|
|
# Create py.typed marker for PEP 561
|
|
touch "$PROVIDER_PATH/$PACKAGE_NAME/py.typed"
|
|
|
|
# Create demo script
|
|
if [ "$IS_AGENTIC" = true ]; then
|
|
cat > "$PROVIDER_PATH/${PROVIDER_NAME}_demo.py" << EOL
|
|
"""
|
|
${TITLE_CASE_PROVIDER_NAME} demo.
|
|
"""
|
|
|
|
import asyncio
|
|
|
|
# from ${PROVIDER_NAME}_framework import Agent, Runner # Import your agent framework
|
|
from composio_${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME}Provider
|
|
|
|
from composio import Composio
|
|
|
|
# Initialize Composio toolset
|
|
composio = Composio(provider=${TITLE_CASE_PROVIDER_NAME}Provider())
|
|
|
|
# Get all the tools
|
|
tools = composio.tools.get(
|
|
user_id="default",
|
|
tools=["GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"],
|
|
)
|
|
|
|
# TODO: Create an agent with the tools
|
|
# agent = Agent(
|
|
# name="GitHub Agent",
|
|
# instructions="You are a helpful assistant that helps users with GitHub tasks.",
|
|
# tools=tools,
|
|
# )
|
|
|
|
|
|
# Run the agent
|
|
async def main():
|
|
# TODO: Implement your agent execution logic
|
|
# result = await Runner.run(
|
|
# starting_agent=agent,
|
|
# input=(
|
|
# "Star the repository composiohq/composio on GitHub. If done "
|
|
# "successfully, respond with 'Action executed successfully'"
|
|
# ),
|
|
# )
|
|
# print(result.final_output)
|
|
|
|
print("Implement your ${TITLE_CASE_PROVIDER_NAME} agent logic here!")
|
|
print(f"Got {len(tools)} tools:")
|
|
for tool in tools:
|
|
print(f" - {tool.name}: {tool.description}")
|
|
|
|
|
|
asyncio.run(main())
|
|
EOL
|
|
else
|
|
cat > "$PROVIDER_PATH/${PROVIDER_NAME}_demo.py" << EOL
|
|
"""
|
|
${TITLE_CASE_PROVIDER_NAME} demo.
|
|
"""
|
|
|
|
from composio_${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME}Provider
|
|
# from ${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME} # Import your AI client
|
|
|
|
from composio import Composio
|
|
|
|
# Initialize tools.
|
|
# ${PROVIDER_NAME}_client = ${TITLE_CASE_PROVIDER_NAME}() # Initialize your AI client
|
|
composio = Composio(provider=${TITLE_CASE_PROVIDER_NAME}Provider())
|
|
|
|
# Define task.
|
|
task = "Star a repo composiohq/composio on GitHub"
|
|
|
|
# Get GitHub tools that are pre-configured
|
|
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])
|
|
|
|
# TODO: Get response from the LLM
|
|
# response = ${PROVIDER_NAME}_client.chat.completions.create(
|
|
# model="your-model",
|
|
# tools=tools,
|
|
# messages=[
|
|
# {"role": "system", "content": "You are a helpful assistant."},
|
|
# {"role": "user", "content": task},
|
|
# ],
|
|
# )
|
|
# print(response)
|
|
|
|
# TODO: Execute the function calls.
|
|
# result = composio.provider.handle_tool_calls(response=response, user_id="default")
|
|
# print(result)
|
|
|
|
print(f"Got {len(tools)} tools for the task: {task}")
|
|
for tool in tools:
|
|
print(f" - {tool.name}: {tool.description}")
|
|
print("\nImplement your ${TITLE_CASE_PROVIDER_NAME} integration here!")
|
|
EOL
|
|
fi
|
|
|
|
# Create README.md
|
|
cat > "$PROVIDER_PATH/README.md" << EOL
|
|
# $PACKAGE_NAME
|
|
|
|
${IS_AGENTIC:+Agentic }Provider for ${TITLE_CASE_PROVIDER_NAME} in the Composio SDK.
|
|
|
|
## Features
|
|
|
|
${IS_AGENTIC:+- **Full Tool Execution**: Execute tools with proper parameter handling
|
|
- **Agent Support**: Create agents with wrapped tools
|
|
- **Type Safety**: Full type annotations for better IDE support}${!IS_AGENTIC:+- **Tool Transformation**: Convert Composio tools to ${TITLE_CASE_PROVIDER_NAME} format
|
|
- **Tool Execution**: Execute tools with proper parameter handling
|
|
- **Type Safety**: Full type annotations for better IDE support}
|
|
|
|
## Installation
|
|
|
|
\`\`\`bash
|
|
pip install $PACKAGE_NAME
|
|
# or
|
|
uv add $PACKAGE_NAME
|
|
\`\`\`
|
|
|
|
## Quick Start
|
|
|
|
\`\`\`python
|
|
from composio import Composio
|
|
from composio_${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME}Provider
|
|
|
|
# Initialize Composio with ${TITLE_CASE_PROVIDER_NAME} provider
|
|
composio = Composio(
|
|
api_key="your-composio-api-key",
|
|
provider=${TITLE_CASE_PROVIDER_NAME}Provider()
|
|
)
|
|
|
|
# Get available tools
|
|
tools = composio.tools.get(
|
|
user_id="default",
|
|
toolkits=["github", "gmail"]
|
|
)
|
|
|
|
# Use tools with ${TITLE_CASE_PROVIDER_NAME}
|
|
# TODO: Add your usage example here
|
|
\`\`\`
|
|
|
|
## Usage Examples
|
|
|
|
### Basic Example
|
|
|
|
\`\`\`python
|
|
from composio import Composio
|
|
from composio_${PROVIDER_NAME} import ${TITLE_CASE_PROVIDER_NAME}Provider
|
|
|
|
# Initialize provider
|
|
provider = ${TITLE_CASE_PROVIDER_NAME}Provider()
|
|
|
|
# Initialize Composio
|
|
composio = Composio(
|
|
api_key="your-api-key",
|
|
provider=provider
|
|
)
|
|
|
|
# Get tools
|
|
tools = composio.tools.get(
|
|
user_id="default",
|
|
toolkits=["github"]
|
|
)
|
|
|
|
${IS_AGENTIC:+# Use the tools with your agent framework
|
|
# TODO: Implement your agent logic here
|
|
for tool in tools:
|
|
print(f"Tool: {tool.name} - {tool.description}")}${!IS_AGENTIC:+# TODO: Get response from your AI platform (example)
|
|
# response = ${PROVIDER_NAME}_client.chat.completions.create(
|
|
# model="your-model",
|
|
# tools=tools,
|
|
# messages=[
|
|
# {"role": "system", "content": "You are a helpful assistant."},
|
|
# {"role": "user", "content": "Star the composiohq/composio repository"},
|
|
# ],
|
|
# )
|
|
#
|
|
# # Execute the function calls
|
|
# result = composio.provider.handle_tool_calls(response=response, user_id="default")
|
|
# print(result)}
|
|
\`\`\`
|
|
|
|
## API Reference
|
|
|
|
### ${TITLE_CASE_PROVIDER_NAME}Provider Class
|
|
|
|
The \`${TITLE_CASE_PROVIDER_NAME}Provider\` class extends \`${IS_AGENTIC:+AgenticProvider}${!IS_AGENTIC:+NonAgenticProvider}\` and provides ${PROVIDER_NAME}-specific functionality.
|
|
|
|
#### Methods
|
|
|
|
##### \`wrap_tool(tool: Tool${IS_AGENTIC:+, execute_tool: AgenticProviderExecuteFn}) -> ${TITLE_CASE_PROVIDER_NAME}Tool\`
|
|
|
|
Wraps a tool in the ${PROVIDER_NAME} format.
|
|
|
|
\`\`\`python
|
|
tool = provider.wrap_tool(composio_tool${IS_AGENTIC:+, execute_tool})
|
|
\`\`\`
|
|
|
|
##### \`wrap_tools(tools: Sequence[Tool]${IS_AGENTIC:+, execute_tool: AgenticProviderExecuteFn}) -> ${IS_AGENTIC:+${TITLE_CASE_PROVIDER_NAME}Toolkit}${!IS_AGENTIC:+${TITLE_CASE_PROVIDER_NAME}ToolCollection}\`
|
|
|
|
Wraps multiple tools in the ${PROVIDER_NAME} format.
|
|
|
|
\`\`\`python
|
|
tools = provider.wrap_tools(composio_tools${IS_AGENTIC:+, execute_tool})
|
|
\`\`\`
|
|
|
|
${!IS_AGENTIC:+##### \`execute_tool_call(user_id: str, tool_call: dict, modifiers: Optional[Modifiers] = None) -> ToolExecutionResponse\`
|
|
|
|
Executes a tool call.
|
|
|
|
\`\`\`python
|
|
result = provider.execute_tool_call(
|
|
user_id="default",
|
|
tool_call={"name": "TOOL_NAME", "arguments": {...}}
|
|
)
|
|
\`\`\`}
|
|
|
|
## Development
|
|
|
|
1. Clone the repository
|
|
2. Install dependencies: \`uv sync\`
|
|
3. Make your changes
|
|
4. Run tests: \`pytest\`
|
|
5. Format code: \`ruff format\`
|
|
6. Lint code: \`ruff check\`
|
|
|
|
## Contributing
|
|
|
|
We welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for more details.
|
|
|
|
## License
|
|
|
|
Apache License 2.0
|
|
|
|
## Support
|
|
|
|
For support, please visit our [Documentation](https://docs.composio.dev) or join our [Discord Community](https://discord.gg/composio).
|
|
EOL
|
|
|
|
# Make the script executable
|
|
chmod +x "$PROVIDER_PATH/${PROVIDER_NAME}_demo.py"
|
|
|
|
echo "✨ Created new ${IS_AGENTIC:+agentic }provider at $PROVIDER_PATH"
|
|
echo "✨ Provider structure:"
|
|
echo " 📁 $PROVIDER_PATH/"
|
|
echo " ├── 📄 README.md"
|
|
echo " ├── 📄 pyproject.toml"
|
|
echo " ├── 📄 setup.py"
|
|
echo " ├── 📄 ${PROVIDER_NAME}_demo.py"
|
|
echo " └── 📁 $PACKAGE_NAME/"
|
|
echo " ├── 📄 __init__.py"
|
|
echo " ├── 📄 provider.py"
|
|
echo " └── 📄 py.typed"
|
|
echo ""
|
|
echo "🚀 Next steps:"
|
|
echo " 1. cd $PROVIDER_PATH"
|
|
echo " 2. Update provider.py with your implementation"
|
|
echo " 3. Install in development mode: uv pip install -e ."
|
|
echo " 4. Test your provider: python ${PROVIDER_NAME}_demo.py" |