This repository provides a foundational template and guide for developing custom tools that integrate with Open WebUI and other platforms supporting similar tool-calling mechanisms. By creating custom tools, you can empower your AI assistant to interact with external services, perform calculations, manage data, and much more.
This template is based on the structure recognized by Open WebUI, allowing for easy development, configuration via "Valves," and communication back to the user interface using an "EventEmitter."
The goal of this repository is to give developers a clear, working example of an Open WebUI tool and explain the essential components needed to build their own. Use this as a starting point to create tools tailored to your specific needs.
An Open WebUI tool, as structured in this template, consists of several key parts that work together to define its functionality and interaction with the AI model and the platform.
The first element in your Python tool file is a triple-quoted string containing metadata about the tool. This information is crucial for Open WebUI to identify, manage, and install dependencies for your tool.
"""
title: My Awesome Tool # User-friendly name displayed in the UI
author: Your Name # Your name or handle
author_url: [https://yourwebsite.com](https://yourwebsite.com) # Optional: Link to your profile/site
git_url: [https://github.com/yourrepo/yourtool.git](https://github.com/yourrepo/yourtool.git) # Optional: Link to the tool's source
description: Does something cool. # A brief explanation of what the tool does
required_open_webui_version: 0.6.3 # Optional: Minimum Open WebUI version required
requirements: package1, package2 # Comma-separated list of external Python packages (pip install runs for these)
version: 0.1.0 # Version number for your tool
license: MIT # The license under which your tool is distributed
"""
# ... rest of the tool code
All the functionality of your tool must be encapsulated within a class named Tools. Each method within this class that you want the AI model to be able to call must be async and have a detailed docstring and type hints.
class Tools:
def __init__(self):
# Initialize valves and any other setup needed when the tool loads
self.valves = self.Valves() # If using Valves
self.user_valves = self.UserValves() # If using UserValves
# Other setup...
# ... tool methods go here ...
Each method within the Tools class represents a specific action the language model can trigger. These methods take parameters defined by their type hints and docstrings and return a value (typically a string indicating success, failure, or results).
async def perform_cool_action(
self,
input_parameter: str,
another_param: int,
__event_emitter__: Callable[[dict], Any] # Special internal argument
) -> str: # IMPORTANT: Type hint for return value
"""
This is the crucial docstring! The LLM reads this!
Clearly describe what this specific action does.
:param input_parameter: Describe what this parameter is for.
:param another_param: Describe this optional parameter.
:return: Describe what the function returns (e.g., "Success: ...", "Error: ...").
"""
# ... tool logic using parameters and potentially valves/emitter ...
pass
This is the most critical part for LLM interaction.
- Docstrings: Every tool method must have a clear, detailed Sphinx-style docstring ("""Docstring here"""). This is what the language model reads to understand what the tool does, what parameters it needs (:param ...:), and what it returns (:return: ...:). Be descriptive and accurate!
- Type Hints: Use Python type hints for all parameters and the return value (param: str, -> str). Open WebUI uses these to present the tool's signature to the language model, helping it understand the expected data types.
You can define configuration options for your tool using classes named Valves
and UserValves
nested within the Tools
class.
Valves
: For administrator-level configuration (e.g., API keys, paths). These are typically set once by the admin and are not intended for end-user modification during chat.UserValves
: For configuration options that the end-user can adjust via the chat settings panel (e.g., units of measurement, preference toggles).
class Tools:
class Valves(BaseModel):
api_key: str = Field(default="", description="API Key needed for this tool")
# ... other admin settings
class UserValves(BaseModel):
user_preference: bool = Field(default=True, description="User setting")
# ... other user settings
def __init__(self):
self.valves = self.Valves()
self.user_valves = self.UserValves()
# ...
The __event_emitter__: Callable[[dict], Any]
parameter is a special internal argument passed by Open WebUI to your tool method. You can use an EventEmitter
helper class (like the one included in the template) to send status updates back to the Open WebUI interface while your tool is running. This provides valuable feedback to the user during long-running operations.
# (EventEmitter class definition goes here - see example code)
class Tools:
# ...
async def perform_cool_action(
self,
# ... params
__event_emitter__: Callable[[dict], Any] # Special internal argument
) -> str:
"""
# ... docstring
"""
event_emitter = EventEmitter(__event_emitter__) # Initialize the helper
await event_emitter.progress_update("Step 1: Doing something...")
# ... logic ...
await event_emitter.success_update("Step 2: Finished successfully.")
return "Result"
Included in this repository is a simple, functional example tool that demonstrates the template structure by reversing a string.
"""
title: String Inverse
author: Your Name
author_url: [https://website.com](https://website.com)
git_url: [https://github.com/username/string-reverse.git](https://github.com/username/string-reverse.git)
description: This tool calculates the inverse of a string
required_open_webui_version: 0.6.3
requirements: langchain-openai, langgraph, ollama, langchain_ollama # (Note: These specific requirements are likely placeholders from a different context, replace with actual requirements for your tool)
version: 0.1.0
license: MIT
"""
from typing import Any, Callable
from pydantic import BaseModel, Field
# (EventEmitter class definition as provided in the template notes)
class EventEmitter:
def __init__(self, event_emitter: Callable[[dict], Any] = None):
self.event_emitter = event_emitter
async def progress_update(self, description: str):
await self.emit(description)
async def error_update(self, description: str):
await self.emit(description, "error", True)
async def success_update(self, description: str):
await self.emit(description, "success", True)
async def emit(
self,
description: str = "Unknown State",
status: str = "in_progress",
done: bool = False,
):
if self.event_emitter:
await self.event_emitter(
{
"type": "status",
"data": {
"status": status,
"description": description,
"done": done,
},
}
)
class Tools:
class Valves(BaseModel):
api_key: str = Field(
default="42", description="Mock service API key"
)
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
test_valve: int = Field(
default=4, description="A valve controlling a numerical value"
)
pass
class UserValves(BaseModel):
test_user_valve: bool = Field(
default=False, description="A user valve controlling a True/False (on/off) switch"
)
pass
def __init__(self):
self.valves = self.Valves()
self.user_valves = self.UserValves()
async def reverse_string(
self,
string: str,
__event_emitter__: Callable[[dict], Any]) -> str:
"""
Reverses the input string.
:param string: The string to reverse.
:return: The reversed string.
"""
event_emitter = EventEmitter(__event_emitter__)
await event_emitter.progress_update("Reversing string...")
if self.valves.api_key != "42":
error_result = "Error: Wrong API key"
await event_emitter.error_update(error_result)
return error_result
await event_emitter.success_update("Reversing string complete.")
return string[::-1]
This example demonstrates metadata, Valves
/UserValves
, the Tools
class, a method (reverse_string
) with docstrings and type hints, and the use of the EventEmitter
.
Consider these practices when building your tools:
- Clear Docstrings: Invest time in writing accurate and detailed docstrings for every tool method. This is the primary way the LLM understands your tool.
- Specific Functions: Design each tool method to perform a single, well-defined task. This makes it easier for the LLM to decide which tool to use.
- Robust Error Handling: Anticipate potential issues (API errors, invalid inputs, network problems) and handle them gracefully within your tool code. Use the
EventEmitter
(specificallyerror_update
) to report errors back to the user in a clear manner. - Dependency Management: Carefully list all required external Python packages in the
requirements
metadata field. - Security: If your tool interacts with external systems, APIs, files, or executes commands, be extremely cautious. Validate and sanitise all inputs received from the LLM. Limit permissions and access rights where possible.
- Clone this repository:
git clone https://github.com/pahautelman/open-webui-email-calendar-assistant
cd open-webui-email-calendar-assistant
- Study the example: Review the String Inverse tool and the explanations in this README.
- Create your own tool file: Copy the structure of
string_inverse_tool.py
(or create a new file) and start building your desired functionality within the Tools class. - Update Metadata: Modify the top docstring with your tool's details, author info, and list only the specific requirements for your tool.
- Define Valves: Add
Valves
and/orUserValves
if your tool needs configuration. - Implement Tool Methods: Write your async methods with clear docstrings and type hints. Implement your core logic here.
- Install Dependencies: Ensure the libraries listed in your tool's requirements are installed in your Open WebUI environment.
- Place Tool File: Copy your completed Python tool file (your_tool_name.py) into Open WebUI's designated tools directory. For more guidelines on how to complete this step, please refer to this article
For the most complete and up-to-date specifications on Open WebUI tool development, including details on the internal arguments passed to tool functions (like __event_emitter__
and others that might be available), please refer to the official documentation:
- Open WebUI Tools Documentation: https://docs.openwebui.com/features/plugin/tools/development This documentation provides comprehensive details beyond this basic template.