Skip to content
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
112 changes: 111 additions & 1 deletion fern/pages/src/tool-calling/executing-tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,114 @@ response = composio.tools.proxy(
```
</CodeGroup>

If you're interested in extending toolkits and creating custom tools, see [Custom tools](/docs/custom-tools).
If you're interested in extending toolkits and creating custom tools, see [Custom tools](/docs/custom-tools).

## Automatic File Handling

Composio SDK includes automatic file handling for tools that work with files. When enabled (default), the SDK automatically handles file uploads and downloads during tool execution.

### File Upload

When a tool accepts file inputs (marked with `file_uploadable: true`), you can pass local file paths or URLs. Here's an example using Google Drive upload:

<CodeGroup>
```typescript TypeScript maxLines=60 wordWrap
import { Composio } from '@composio/core';
import path from 'path';

const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY
});

// Upload a local file to Google Drive
const result = await composio.tools.execute('GOOGLEDRIVE_UPLOAD_FILE', {
userId: 'default',
arguments: {
file_to_upload: path.join(__dirname, 'document.pdf') // Local file path
}
});

console.log(result.data); // Contains Google Drive file details
```

```python Python maxLines=60 wordWrap
import os

from composio import Composio

composio = Composio()

# Upload a local file to Google Drive
result = composio.tools.execute(
"GOOGLEDRIVE_UPLOAD_FILE",
user_id="default",
arguments={
"file_to_upload": os.path.join(os.getcwd(), "document.pdf") # Local file path
}
)

print(result.data) # Contains Google Drive file details
```
</CodeGroup>

The SDK automatically:
1. Reads the file content
2. Uploads it to secure storage
3. Passes the file metadata to the tool

### File Download

When a tool returns file outputs, the SDK automatically:
1. Downloads the file to a local temporary directory
2. Provides the local file path in the response

<CodeGroup>
```typescript TypeScript maxLines=60 wordWrap
// Download a file from Google Drive
const result = await composio.tools.execute('GOOGLEDRIVE_DOWNLOAD_FILE', {
userId: 'default',
arguments: {
file_id: 'your_file_id'
}
});

// Result includes local file path
console.log(result.data.file.uri); // "/path/to/downloaded/file.pdf"
```

```python Python maxLines=60 wordWrap
# Download a file from Google Drive
result = composio.tools.execute(
"GOOGLEDRIVE_DOWNLOAD_FILE",
user_id="default",
arguments={
"file_id": "your_file_id"
}
)

# Result includes local file path
print(result.data["file"]) # "/path/to/downloaded/file.pdf"
```
</CodeGroup>

### Disabling Auto File Handling

You can disable automatic file handling when initializing the Typescript SDK:

<CodeGroup>
```typescript TypeScript maxLines=60 wordWrap
const composio = new Composio({
apiKey: process.env.COMPOSIO_API_KEY,
autoUploadDownloadFiles: false
});

// Now you need to handle files manually using composio.files API
const fileData = await composio.files.upload({
filePath: path.join(__dirname, 'document.pdf'),
toolSlug: 'GOOGLEDRIVE_UPLOAD_FILE',
toolkitSlug: 'googledrive'
});
```
</CodeGroup>

For more details on file handling, see [Auto Upload and Download Files](/docs/advanced/auto-upload-download).
76 changes: 74 additions & 2 deletions fern/pages/src/tool-calling/fetching-tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ When you fetch tools from a toolkit, the most important tools are returned first

Composio determines the importance of a tool based on the usage and relevance of the tool.
</Tip>

<CodeGroup>
```typescript TypeScript maxLines=60 wordWrap
const tools = await composio.tools.get(
Expand Down Expand Up @@ -62,6 +63,37 @@ tools = composio.tools.get(
```
</CodeGroup>

**Filtering by scopes**

When working with OAuth-based toolkits, you can filter tools based on their required scopes. This is useful when you want to:
- Get tools that match specific permission levels
- Ensure tools align with available user permissions
- Filter tools based on their required OAuth scopes

<Tip title="Single Toolkit Requirement" icon="warning">
Scope filtering can only be used with a single toolkit at a time.
</Tip>

<CodeGroup>
```typescript TypeScript maxLines=60 wordWrap
// Get GitHub tools that require specific scopes
const tools = await composio.tools.get(userId, {
toolkits: ["GITHUB"],
scopes: ["repo"], // Only get tools requiring these scopes
limit: 10
});
```
```python Python maxLines=60 wordWrap
# Get GitHub tools that require specific scopes
tools = composio.tools.get(
user_id,
toolkits=["GITHUB"],
scopes=["repo"], # Only get tools requiring these scopes
limit=10
)
```
</CodeGroup>

## Filtering by tool
You may specify the list of tools to fetch by directly providing the tool names. Browse the list of tools [here](/tools) to view and inspect the tools for each toolkit.

Expand Down Expand Up @@ -118,17 +150,57 @@ You may also filter tools by searching for them. This is a good way to find tool
This step runs a semantic search on the tool names and descriptions and returns the most relevant tools.

<CodeGroup>

```typescript TypeScript maxLines=60 wordWrap
const tools = await composio.tools.get(userId, {
search: "hubspot organize contacts",
});

// Search within a specific toolkit
const tools = await composio.tools.get(userId, {
search: "repository issues",
toolkits: ["GITHUB"], // Optional: limit search to specific toolkit
limit: 5 // Optional: limit number of results
});
```

```python Python maxLines=60 wordWrap
tools = composio.tools.get(
user_id,
search="hubspot organize contacts",
)

# Search within a specific toolkit
tools = composio.tools.get(
user_id,
search="repository issues",
toolkits=["GITHUB"], # Optional: limit search to specific toolkit
limit=5 # Optional: limit number of results
)
```
</CodeGroup>
</CodeGroup>

## Filter Combinations

When fetching tools, you must use one of these filter combinations:

1. **Tools Only**: Fetch specific tools by their slugs
```typescript
{ tools: ["TOOL_1", "TOOL_2"] }
```

2. **Toolkits Only**: Fetch tools from specific toolkits
```typescript
{ toolkits: ["TOOLKIT_1", "TOOLKIT_2"], limit?: number }
```

3. **Single Toolkit with Scopes**: Fetch tools requiring specific OAuth scopes
```typescript
{ toolkits: ["GITHUB"], scopes: ["read:repo"], limit?: number }
```

4. **Search**: Search across all tools or within specific toolkits
```typescript
{ search: "query", toolkits?: string[], limit?: number }
```

These combinations are mutually exclusive - you can't mix `tools` with `search` or use `scopes` with multiple toolkits.
25 changes: 4 additions & 21 deletions python/composio/core/models/connected_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,33 +227,16 @@ def calcom_auth(
),
}

def snowflake(
self, options: connected_account_create_params.ConnectionStateUnionMember9Val
) -> connected_account_create_params.ConnectionState:
"""
Create a new connected account using Snowflake.
"""
return {
"auth_scheme": "SNOWFLAKE",
"val": t.cast(
connected_account_create_params.ConnectionStateUnionMember9Val,
{
**options,
"status": "ACTIVE",
},
),
}

def billcom_auth(
self, options: connected_account_create_params.ConnectionStateUnionMember10Val
self, options: connected_account_create_params.ConnectionStateUnionMember9Val
) -> connected_account_create_params.ConnectionState:
"""
Create a new connected account using Bill.com auth.
"""
return {
"auth_scheme": "BILLCOM_AUTH",
"val": t.cast(
connected_account_create_params.ConnectionStateUnionMember10Val,
connected_account_create_params.ConnectionStateUnionMember9Val,
{
**options,
"status": "ACTIVE",
Expand All @@ -262,15 +245,15 @@ def billcom_auth(
}

def basic_with_jwt(
self, options: connected_account_create_params.ConnectionStateUnionMember11Val
self, options: connected_account_create_params.ConnectionStateUnionMember10Val
) -> connected_account_create_params.ConnectionState:
"""
Create a new connected account using basic auth with JWT.
"""
return {
"auth_scheme": "BASIC_WITH_JWT",
"val": t.cast(
connected_account_create_params.ConnectionStateUnionMember11Val,
connected_account_create_params.ConnectionStateUnionMember10Val,
{
**options,
"status": "ACTIVE",
Expand Down
1 change: 1 addition & 0 deletions python/composio/core/models/custom_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def __parse_info(self) -> Tool:
output_parameters={},
available_versions=[],
version="1.0.0",
scopes=[],
slug=self.slug,
toolkit=tool_list_response.ItemToolkit(
logo="",
Expand Down
15 changes: 15 additions & 0 deletions python/composio/core/models/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ def get_raw_composio_tools(
tools: t.Optional[list[str]] = None,
search: t.Optional[str] = None,
toolkits: t.Optional[list[str]] = None,
scopes: t.Optional[t.List[str]] = None,
limit: t.Optional[int] = None,
) -> list[Tool]:
"""
Get a list of tool schemas based on the provided filters.
Expand All @@ -116,6 +118,8 @@ def get_raw_composio_tools(
",".join(toolkits) if toolkits else self._client.not_given
),
search=search if search else self._client.not_given,
scopes=scopes,
limit=str(limit) if limit is not None else self._client.not_given,
).items
)
return tools_list
Expand All @@ -126,13 +130,17 @@ def _get(
tools: t.Optional[list[str]] = None,
search: t.Optional[str] = None,
toolkits: t.Optional[list[str]] = None,
scopes: t.Optional[t.List[str]] = None,
modifiers: t.Optional[Modifiers] = None,
limit: t.Optional[int] = None,
):
"""Get a list of tools based on the provided filters."""
tools_list = self.get_raw_composio_tools(
tools=tools,
search=search,
toolkits=toolkits,
scopes=scopes,
limit=limit,
)
if modifiers is not None:
tools_list = [
Expand Down Expand Up @@ -197,6 +205,8 @@ def get(
user_id: str,
*,
toolkits: list[str],
scopes: t.Optional[t.List[str]] = None,
limit: t.Optional[int] = None,
modifiers: t.Optional[Modifiers] = None,
):
"""Get tools by toolkit slugs (Only important tools are returned)"""
Expand All @@ -218,6 +228,7 @@ def get(
*,
toolkits: list[str],
search: t.Optional[str] = None,
limit: t.Optional[int] = None,
modifiers: t.Optional[Modifiers] = None,
):
"""Get tool by search term and/or toolkit slugs and search term"""
Expand All @@ -230,7 +241,9 @@ def get(
tools: t.Optional[list[str]] = None,
search: t.Optional[str] = None,
toolkits: t.Optional[list[str]] = None,
scopes: t.Optional[t.List[str]] = None,
modifiers: t.Optional[Modifiers] = None,
limit: t.Optional[int] = None,
):
"""Get a tool or list of tools based on the provided arguments."""
if slug is not None:
Expand All @@ -240,7 +253,9 @@ def get(
tools=tools,
search=search,
toolkits=toolkits,
scopes=scopes,
modifiers=modifiers,
limit=limit,
)

def _wrap_execute_tool(
Expand Down
2 changes: 1 addition & 1 deletion python/composio/core/models/triggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ def create(
)

def _get_connected_account_for_user(self, trigger: str, user_id: str) -> str:
toolkit = self.get_type(slug=trigger).toolkit.name
toolkit = self.get_type(slug=trigger).toolkit.slug
connected_accounts = self._client.connected_accounts.list(
toolkit_slugs=[toolkit]
)
Expand Down
2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ requires-python = ">=3.10,<4"
dependencies = [
"pysher>=1.0.8",
"pydantic>=2.6.4",
"composio-client==1.3.0",
"composio-client==1.4.0",
"typing-extensions>=4.0.0",
"openai",
]
Expand Down
Loading
Loading