Tools have to be defined as methods within a class called `Tools`, with optional subclasses called `Valves` and `UserValves`, for example:
```python
class Tools:
def __init__(self):
"""Initialize the Tool."""
self.valves = self.Valves()
class Valves(BaseModel):
api_key: str = Field("", description="Your API key here")
def reverse_string(self, string: str) -> str:
"""
Reverses the input string.
:param string: The string to reverse
"""
# example usage of valves
if self.valves.api_key != "42":
return "Wrong API key"
return string[::-1]
```
### Type Hints
Each tool must have type hints for arguments. The types may also be nested, such as `queries_and_docs: list[tuple[str, int]]`. Those type hints are used to generate the JSON schema that is sent to the model. Tools without type hints will work with a lot less consistency.
### Valves and UserValves - (optional, but HIGHLY encouraged)
Valves and UserValves are used for specifying customizable settings of the Tool, you can read more on the dedicated [Valves & UserValves page](/features/plugin/development/valves).
### Optional Arguments
Below is a list of optional arguments your tools can depend on:
- `__event_emitter__`: Emit events (see following section)
- `__event_call__`: Same as event emitter but can be used for user interactions
- `__user__`: A dictionary with user information. It also contains the `UserValves` object in `__user__["valves"]`.
- `__metadata__`: Dictionary with chat metadata
- `__messages__`: List of previous messages
- `__files__`: Attached files
- `__model__`: A dictionary with model information
- `__oauth_token__`: A dictionary containing the user's valid, automatically refreshed OAuth token payload. This is the **new, recommended, and secure** way to access user tokens for making authenticated API calls. The dictionary typically contains `access_token`, `id_token`, and other provider-specific data.
For more information about `__oauth_token__` and how to configure this token to be sent to tools, check out the OAuth section in the [environment variable docs page](https://docs.openwebui.com/getting-started/env-configuration/) and the [SSO documentation](https://docs.openwebui.com/features/auth/).
Just add them as argument to any method of your Tool class just like `__user__` in the example above.
#### Using the OAuth Token in a Tool
When building tools that need to interact with external APIs on the user's behalf, you can now directly access their OAuth token. This removes the need for fragile cookie scraping and ensures the token is always valid.
**Example:** A tool that calls an external API using the user's access token.
response.raise_for_status() # Raise an exception for bad status codes
return f"API Response: {response.json()}"
except httpx.HTTPStatusError as e:
return f"Error: Failed to fetch data from API. Status: {e.response.status_code}"
except Exception as e:
return f"An unexpected error occurred: {e}"
```
### Event Emitters
Event Emitters are used to add additional information to the chat interface. Similarly to Filter Outlets, Event Emitters are capable of appending content to the chat. Unlike Filter Outlets, they are not capable of stripping information. Additionally, emitters can be activated at any stage during the Tool.
**⚠️ CRITICAL: Function Calling Mode Compatibility**
Event Emitter behavior is **significantly different** depending on your function calling mode. The function calling mode is controlled by the `function_calling` parameter:
- **Default Mode**: Uses traditional function calling approach with wider model compatibility
Before using event emitters, you must understand these critical limitations:
- **Default Mode** (`function_calling = "default"`): Full event emitter support with all event types working as expected
- **Native Mode** (`function_calling = "native"`): **Limited event emitter support** - many event types don't work properly due to native function calling bypassing Open WebUI's custom tool processing pipeline
**When to Use Each Mode:**
- **Use Default Mode** when you need full event emitter functionality, complex tool interactions, or real-time UI updates
- **Use Native Mode** when you need reduced latency and basic tool calling without complex UI interactions
#### Function Calling Mode Configuration
You can configure the function calling mode in two places:
1. **Model Settings**: Go to Model page → Advanced Params → Function Calling (set to "Default" or "Native")
2. **Per-request basis**: Set `params.function_calling = "native"` or `"default"` in your request
If the model seems to be unable to call the tool, make sure it is enabled (either via the Model page or via the `+` sign next to the chat input field).
#### Complete Event Type Compatibility Matrix
Here's the comprehensive breakdown of how each event type behaves across function calling modes:
| Event Type | Default Mode Functionality | Native Mode Functionality | Status |
| `status` | ✅ Full support - Updates status history during tool execution | ✅ **Identical** - Tracks function execution status | **COMPATIBLE** |
| `message` | ✅ Full support - Appends incremental content during streaming | ❌ **BROKEN** - Gets overwritten by native completion snapshots | **INCOMPATIBLE** |
| `chat:completion` | ✅ Full support - Handles streaming responses and completion data | ⚠️ **LIMITED** - Carries function results but may overwrite tool updates | **PARTIALLY COMPATIBLE** |
| `chat:message:delta` | ✅ Full support - Streams delta content during execution | ❌ **BROKEN** - Content gets replaced by native function snapshots | **INCOMPATIBLE** |
| `chat:message` | ✅ Full support - Replaces entire message content cleanly | ❌ **BROKEN** - Gets overwritten by subsequent native completions | **INCOMPATIBLE** |
| `replace` | ✅ Full support - Replaces content with precise control | ❌ **BROKEN** - Replaced content gets overwritten immediately | **INCOMPATIBLE** |
| `chat:message:files` / `files` | ✅ Full support - Handles file attachments in messages | ✅ **Identical** - Processes files from function outputs | **COMPATIBLE** |
| `chat:message:error` | ✅ Full support - Displays error notifications | ✅ **Identical** - Shows function call errors | **COMPATIBLE** |
| `chat:message:follow_ups` | ✅ Full support - Shows follow-up suggestions | ✅ **Identical** - Displays function-generated follow-ups | **COMPATIBLE** |
| `chat:title` | ✅ Full support - Updates chat title dynamically | ✅ **Identical** - Updates title based on function interactions | **COMPATIBLE** |
| `chat:tags` | ✅ Full support - Modifies chat tags | ✅ **Identical** - Manages tags from function outputs | **COMPATIBLE** |
| `chat:tasks:cancel` | ✅ Full support - Cancels ongoing tasks | ✅ **Identical** - Cancels native function executions | **COMPATIBLE** |
| `citation` / `source` | ✅ Full support - Handles citations with full metadata | ✅ **Identical** - Processes function-generated citations | **COMPATIBLE** |
| `notification` | ✅ Full support - Shows toast notifications | ✅ **Identical** - Displays function execution notifications | **COMPATIBLE** |
| `confirmation` | ✅ Full support - Requests user confirmations | ✅ **Identical** - Confirms function executions | **COMPATIBLE** |
| `execute` | ✅ Full support - Executes code dynamically | ✅ **Identical** - Runs function-generated code | **COMPATIBLE** |
| `input` | ✅ Full support - Requests user input with full UI | ✅ **Identical** - Collects input for functions | **COMPATIBLE** |
#### Why Native Mode Breaks Certain Event Types
In **Native Mode**, the server constructs content blocks from streaming model output and repeatedly emits `"chat:completion"` events with full serialized content snapshots. The client treats these snapshots as authoritative and completely replaces message content, effectively overwriting any prior tool-emitted updates like `message`, `chat:message`, or `replace` events.
**Technical Details:**
- `middleware.py` adds tools directly to form data for native model handling
- Streaming handler emits repeated content snapshots via `chat:completion` events
return f"Event testing completed in {mode_info} mode. Check for missing or flickering content."
```
There are several specific event types with different behaviors:
#### Status Events ✅ FULLY COMPATIBLE
**Status events work identically in both Default and Native function calling modes.** This is the most reliable event type for providing real-time feedback during tool execution.
Status events add live status updates to a message while it's performing steps. These can be emitted at any stage during tool execution. Status messages appear right above the message content and are essential for tools that delay the LLM response or process large amounts of information.
**Basic Status Event Structure:**
```python
await __event_emitter__({
"type": "status",
"data": {
"description": "Message that shows up in the chat",
"done": False, # False = still processing, True = completed
return f"Successfully processed {total_items} items in {total_batches} batches"
```
</details>
#### Message Events ⚠️ DEFAULT MODE ONLY
:::warning
**🚨 CRITICAL WARNING: Message events are INCOMPATIBLE with Native function calling mode!**
:::
Message events (`message`, `chat:message`, `chat:message:delta`, `replace`) allow you to append or modify message content at any stage during tool execution. This enables embedding images, rendering web pages, streaming content updates, and creating rich interactive experiences.
**However, these event types have major compatibility issues:**
- ✅ **Default Mode**: Full functionality - content persists and displays properly
- ❌ **Native Mode**: BROKEN - content gets overwritten by completion snapshots and disappears
**Why Message Events Break in Native Mode:**
Native function calling emits repeated `chat:completion` events with full content snapshots that completely replace message content, causing any tool-emitted message updates to flicker and disappear.
return "Content has been streamed above with full Default mode capabilities."
```
</details>
#### Citations ✅ FULLY COMPATIBLE
**Citation events work identically in both Default and Native function calling modes.** This event type provides source references and citations in the chat interface, allowing users to click and view source materials.
Citations are essential for tools that retrieve information from external sources, databases, or documents. They provide transparency and allow users to verify information sources.
**Citation Event Structure:**
```python
await __event_emitter__({
"type": "citation",
"data": {
"document": [content], # Array of content strings
"metadata": [ # Array of metadata objects
{
"date_accessed": datetime.now().isoformat(),
"source": title,
"author": "Author Name", # Optional
"publication_date": "2024-01-01", # Optional
"url": "https://source-url.com" # Optional
}
],
"source": {"name": title, "url": url} # Primary source info
}
})
```
**Important Citation Setup:**
When implementing custom citations, you **must** disable automatic citations in your `Tools` class:
If you set `self.citation = True` (or don't set it to `False`), automatic citations will replace any custom citations you send. Always disable automatic citations when using custom citation events.
"data": {"message": "Are you sure you want to continue?"}
})
```
**Input Request Events**
```python
await __event_emitter__({
"type": "input",
"data": {"prompt": "Please enter additional information:"}
})
```
**Code Execution Events**
```python
await __event_emitter__({
"type": "execute",
"data": {"code": "print('Hello from tool-generated code!')"}
})
```
#### Comprehensive Function Calling Mode Guide
Choosing the right function calling mode is crucial for your tool's functionality. This guide helps you make an informed decision based on your specific requirements.
**Mode Comparison Overview:**
| Aspect | Default Mode | Native Mode |
|--------|-------------|-------------|
| **Latency** | Higher - processes through Open WebUI pipeline | Lower - direct model handling |
| **Event Support** | ✅ Full - all event types work perfectly | ⚠️ Limited - many event types broken |
| **Complexity** | Handles complex tool interactions well | Best for simple tool calls |
| **Compatibility** | Works with all models | Requires models with native tool calling |
Both External and Built-In Tools now support rich UI element embedding, allowing tools to return HTML content and interactive iframes that display directly within chat conversations. This feature enables tools to provide sophisticated visual interfaces, interactive widgets, charts, dashboards, and other rich web content.
When a tool returns an `HTMLResponse` with the appropriate headers, the content will be embedded as an interactive iframe in the chat interface rather than displayed as plain text.
#### Basic Usage
To embed HTML content, your tool should return an `HTMLResponse` with the `Content-Disposition: inline` header:
The embedded content automatically inherits responsive design and integrates seamlessly with the chat interface, providing a native-feeling experience for users interacting with your tools.
#### CORS and Direct Tools
Direct external tools are tools that run directly from the browser. In this case, the tool is called by JavaScript in the user's browser.
Because we depend on the Content-Disposition header, when using CORS on a remote tool server, the Open WebUI cannot read that header due to Access-Control-Expose-Headers, which prevents certain headers from being read from the fetch result.
To prevent this, you must set Access-Control-Expose-Headers to Content-Disposition. Check the example below of a tool using Node.js:
More info about the header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Expose-Headers
## External packages
In the Tools definition metadata you can specify custom packages. When you click `Save` the line will be parsed and `pip install` will be run on all requirements at once.
**🚨 CRITICAL WARNING: Potential for Package Version Conflicts**
When multiple tools define different versions of the same package (e.g., Tool A requires `pandas==1.5.0` and Tool B requires `pandas==2.0.0`), Open WebUI installs them in a non-deterministic order. This can lead to unpredictable behavior and break one or more of your tools.
**The only robust solution to this problem is to use an OpenAPI tool server.**
We strongly recommend using an [OpenAPI tool server](/features/plugin/tools/openapi-servers/) to avoid these dependency conflicts.
Keep in mind that as pip is used in the same process as Open WebUI, the UI will be completely unresponsive during the installation.
No measures are taken to handle package conflicts with Open WebUI's requirements. That means that specifying requirements can break Open WebUI if you're not careful. You might be able to work around this by specifying `open-webui` itself as a requirement.
<details>
<summary>Example</summary>
```python
"""
title: myToolName
author: myName
funding_url: [any link here will be shown behind a `Heart` button for users to show their support to you]
version: 1.0.0
# the version is displayed in the UI to help users keep track of updates.