This document provides a comprehensive guide to Open WebUI Functions, explaining their types, usage, and implementation details.
Functions are essentially plugins for OpenWebUI, extending its capabilities. They allow for:
- Adding support for new AI model providers (e.g., Anthropic, Vertex AI).
- Modifying message processing.
- Introducing custom buttons to the interface.
Functions are built-in and run within the OpenWebUI environment, making them fast, modular, and independent of external dependencies. They are written in pure Python, offering high customizability.
OpenWebUI offers three types of Functions:
- Pipe Function: Creates custom "Agents/Models" or integrations, appearing as standalone models in the interface.
- Filter Function: Modifies inputs and outputs, acting as "hooks" in the workflow.
- Action Function: Adds custom buttons to the chat interface for interactive shortcuts.
A Pipe Function allows you to create custom agents/models or integrations. These appear in the OpenWebUI interface as if they were standalone models.
What does it do?
- Defines complex workflows. For example, sending data to multiple models (Model A and Model B), processing their outputs, and combining the results.
- Can interact with non-AI systems like search APIs, weather data providers, or systems like Home Assistant.
Use case example:
Querying Google Search directly from OpenWebUI:
- Takes the user's message as the search query.
- Sends the query to Google Search's API.
- Processes the response and returns it within the WebUI like a normal model response.
Enabling: When enabled, Pipe Functions appear as selectable models.
Detailed Guide: Pipe Functions (See below)
A Filter Function modifies data before it's sent to the AI model (input) or after it comes back (output).
What does it do?
- Inlet: Adjusts the input sent to the model (e.g., adding instructions, keywords, or formatting).
- Outlet: Modifies the output received from the model (e.g., cleaning up the response, adjusting tone, or formatting).
Use case example:
Ensuring precise formatting for a project:
- Transforms input into the required format.
- Cleans up the model's output before display.
Enabling: Filters can be linked to specific models or enabled globally for all models.
Detailed Guide: Filter Functions (See below)
An Action Function adds custom buttons to the chat interface, appearing beneath individual chat messages.
What does it do?
- Defines interactive shortcuts that trigger specific functionality.
Use case example:
Adding a "Summarize" button:
- Adds a "Summarize" button under every incoming message.
- When clicked, triggers a custom function to process the message and return a summary.
Detailed Guide: Action Functions (See below)
-
Install Functions: Install via the OpenWebUI interface or by importing manually. Find community-created functions on the OpenWebUI Community Site.
โ ๏ธ Caution: Only install Functions from trusted sources due to security risks.
-
Enable Functions: Functions must be explicitly enabled after installation.
- Pipe Functions become available as models.
- Filter and Action Functions need to be assigned to specific models or enabled globally.
-
Assign Filters or Actions to Models:
- Navigate to
Workspace => Modelsand assign the Function. - Alternatively, enable globally via
Workspace => Functions, select the "..." menu, and toggle theGlobalswitch.
- Navigate to
Quick Summary:
- Pipes: Standalone models.
- Filters: Modify inputs/outputs.
- Actions: Add clickable buttons.
- Extend: Add new models or integrate with external tools (APIs, databases, smart devices).
- Optimize: Tweak inputs and outputs.
- Simplify: Add buttons/shortcuts for an intuitive interface.
This section provides a detailed guide on creating Pipe Functions in Open WebUI.
Pipes are analogous to plugins that introduce new data pathways and custom logic within Open WebUI. They allow you to create custom models with specific behaviors.
A basic Pipe structure:
from pydantic import BaseModel, Field
class Pipe:
class Valves(BaseModel):
MODEL_ID: str = Field(default="")
def __init__(self):
self.valves = self.Valves()
def pipe(self, body: dict):
# Logic goes here
print(self.valves, body) # Prints configuration and input
return "Hello, World!"The Pipe Class:
- Defines the custom logic and acts as the blueprint for the plugin.
Valves (Configuration):
- A nested class inheriting from
BaseModel. - Contains configuration options (parameters) that persist across the Pipe's use.
- Example:
MODEL_IDis a configuration option. - Think of
Valvesas knobs controlling the data flow.
The __init__ Method:
- The constructor method.
- Initializes the Pipe's state, primarily setting up
self.valves.
The pipe Function:
- The core function containing the custom logic.
body: A dictionary containing the input data.- Processes the input and returns the result.
Note: The recommended structure is Valves, then __init__, then pipe.
To create multiple models from a single Pipe, define a pipes function or variable within the Pipe class:
from pydantic import BaseModel, Field
class Pipe:
class Valves(BaseModel):
MODEL_ID: str = Field(default="")
def __init__(self):
self.valves = self.Valves()
def pipes(self):
return [
{"id": "model_id_1", "name": "model_1"},
{"id": "model_id_2", "name": "model_2"},
{"id": "model_id_3", "name": "model_3"},
]
def pipe(self, body: dict):
# Logic goes here
print(self.valves, body)
model = body.get("model", "")
return f"{model}: Hello, World!"pipes Function:
- Returns a list of dictionaries, each representing a model with
idandname. - These models appear individually in the OpenWebUI model selector.
Updated pipe Function:
- Processes input based on the selected model.
This example creates a Pipe that proxies requests to the OpenAI API:
from pydantic import BaseModel, Field
import requests
class Pipe:
class Valves(BaseModel):
NAME_PREFIX: str = Field(
default="OPENAI/",
description="Prefix to be added before model names.",
)
OPENAI_API_BASE_URL: str = Field(
default="https://api.openai.com/v1",
description="Base URL for accessing OpenAI API endpoints.",
)
OPENAI_API_KEY: str = Field(
default="",
description="API key for authenticating requests to the OpenAI API.",
)
def __init__(self):
self.valves = self.Valves()
def pipes(self):
if self.valves.OPENAI_API_KEY:
try:
headers = {
"Authorization": f"Bearer {self.valves.OPENAI_API_KEY}",
"Content-Type": "application/json",
}
r = requests.get(
f"{self.valves.OPENAI_API_BASE_URL}/models", headers=headers
)
models = r.json()
return [
{
"id": model["id"],
"name": f'{self.valves.NAME_PREFIX}{model.get("name", model["id"])}',
}
for model in models["data"]
if "gpt" in model["id"]
]
except Exception as e:
return [
{
"id": "error",
"name": "Error fetching models. Check your API Key.",
},
]
else:
return [
{
"id": "error",
"name": "API Key not provided.",
},
]
def pipe(self, body: dict, **user: dict):
print(f"pipe:{__name__}")
headers = {
"Authorization": f"Bearer {self.valves.OPENAI_API_KEY}",
"Content-Type": "application/json",
}
# Extract model id from the model name
model_id = body["model"][body["model"].find(".") + 1 :]
# Update the model id in the body
payload = {**body, "model": model_id}
try:
r = requests.post(
url=f"{self.valves.OPENAI_API_BASE_URL}/chat/completions",
json=payload,
headers=headers,
stream=True,
)
r.raise_for_status()
if body.get("stream", False):
return r.iter_lines()
else:
return r.json()
except Exception as e:
return f"Error: {e}"Detailed Breakdown:
ValvesConfiguration:NAME_PREFIX,OPENAI_API_BASE_URL,OPENAI_API_KEY.pipesFunction: Fetches available OpenAI models, filters for models with "gpt" in their ID, and handles errors.pipeFunction: Handles requests to the selected OpenAI model, extracts the model ID, prepares the payload, makes the API request, and handles streaming.
This proxy Pipe can be modified to support other providers (Anthropic, Perplexity, etc.) by adjusting API endpoints, headers, and logic.
You can leverage internal Open WebUI functions:
from pydantic import BaseModel, Field
from fastapi import Request
from open_webui.models.users import Users
from open_webui.utils.chat import generate_chat_completion
class Pipe:
def __init__(self):
pass
async def pipe(
self,
body: dict,
**user: dict,
**request: Request,
) -> str:
# Use the unified endpoint with the updated signature
user = Users.get_user_by_id(user["id"])
body["model"] = "llama3.2:latest" # Example model
return await generate_chat_completion(request, body, user)Explanation:
- Imports
Usersandgenerate_chat_completion. - The
pipefunction fetches the user object, sets the model, and callsgenerate_chat_completion.
Important Notes:
- Refer to the latest Open WebUI codebase for accurate function signatures.
- Handle exceptions and errors gracefully.
- Q1: Why should I use Pipes in Open WebUI? To add new models with custom logic and processing.
- Q2: What are Valves, and why are they important? Configurable parameters of your Pipe.
- Q3: Can I create a Pipe without Valves? Yes, but
Valvesare good practice for flexibility. - Q4: How do I ensure my Pipe is secure when using API keys? Use
Valvesto input and store API keys securely. - Q5: What is the difference between the
pipeandpipesfunctions?pipehandles logic for a single model;pipesallows representing multiple models. - Q6: How can I handle errors in my Pipe? Use
try-exceptblocks. - Q7: Can I use external libraries in my Pipe? Yes.
- Q8: How do I test my Pipe? Run Open WebUI in a development environment and select your custom model.
- Q9: Are there any best practices for organizing my Pipe's code? Keep
Valvesat the top, initialize in__init__, and placepipeafter__init__. - Q10: Where can I find the latest Open WebUI documentation? Visit the official Open WebUI repository.
This section details Filter Functions in Open WebUI.
Filters modify data flowing to and from models. They act as checkpoints for adjustments.
Key Functions:
- Modify User Inputs (
inletFunction): Tweak input data before it reaches the AI model. - Modify Model Outputs (
outletFunction): Adjust the AI's response before showing it to the user.
Key Concept: Filters are not standalone models but tools to enhance data.
from pydantic import BaseModel
from typing import Optional
class Filter:
# Valves: Configuration options for the filter
class Valves(BaseModel):
pass
def __init__(self):
# Initialize valves (optional configuration for the Filter)
self.valves = self.Valves()
def inlet(self, body: dict) -> dict:
# This is where you manipulate user inputs.
print(f"inlet called: {body}")
return body
def outlet(self, body: dict) -> None:
# This is where you manipulate model outputs.
print(f"outlet called: {body}")
return body๐ฏ Key Components Explained:
-
ValvesClass (Optional Settings): Configuration options for the filter. -
inletFunction (Input Pre-Processing):- Input:
body(the raw input from OpenWebUI). - Task: Modify and return the
body. - Why Use
inlet?- Adding Context: Append information to the user's input.
- Formatting Data: Transform input into a specific format (JSON, Markdown).
- Sanitizing Input: Remove unwanted characters.
- Streamlining User Input: Inject clarifying instructions.
Example 1: Adding System Context:
def inlet(self, body: dict, **user: Optional[dict] = None) -> dict: context_message = { "role": "system", "content": "You are helping the user prepare an Italian meal." } body.setdefault("messages", []).insert(0, context_message) return body
Example 2: Cleaning Input:
def inlet(self, body: dict, **user: Optional[dict] = None) -> dict: last_message = body["messages"][-1]["content"] body["messages"][-1]["content"] = last_message.replace("!!!", "").strip() return body
- Input:
-
outletFunction (Output Post-Processing):- Input:
body(all current messages in the chat). - Task: Modify the
body. - Best Practices: Prefer logging over direct edits in the
outlet.
Example: Strip out sensitive API responses:
def outlet(self, body: dict, **user**: Optional[dict] = None) -> dict: for message in body["messages"]: message["content"] = message["content"].replace("<API_KEY>", "[REDACTED]") return body
- Input:
Example #1: Add Context to Every User Input:
class Filter:
def inlet(self, body: dict, **user: Optional[dict] = None) -> dict:
context_message = {
"role": "system",
"content": "You're a software troubleshooting assistant."
}
body.setdefault("messages", []).insert(0, context_message)
return bodyExample #2: Highlight Outputs for Easy Reading:
class Filter:
def outlet(self, body: dict, **user: Optional[dict] = None) -> dict:
for message in body["messages"]:
if message["role"] == "assistant":
message["content"] = f"**{message['content']}**"
return body- Q: How Are Filters Different From Pipe Functions? Filters modify data; Pipes integrate external APIs or significantly transform backend operations.
- Q: Can I Do Heavy Post-Processing Inside
outlet? You can, but it's not best practice. Use a Pipe Function instead.
Action functions add custom buttons to the message toolbar.
Creates a button in the Message UI (underneath individual chat messages).
Example:
async def action(
self,
body: dict,
**user=None,
__event_emitter__=None,
__event_call__=None,
) -> Optional[dict]:
print(f"action:{__name__}")
response = await __event_call__(
{
"type": "input",
"data": {
"title": "write a message",
"message": "here write a message to append",
"placeholder": "enter your message",
},
}
)
print(response)Pipes can perform actions before returning LLM messages, such as RAG, sending requests to non-OpenAI LLMs, or executing functions in the web UI. Pipes can be hosted as a Function or on a Pipelines server.
Pipe Workflow: (Diagram is mentioned in the original documentation, but cannot be rendered here. It would show the flow of data through a Pipe.)
Pipes defined in WebUI appear as new models with an "External" designation.
Valves are input variables set per pipeline. They are set as a subclass of the Pipeline class and initialized in the __init__ method.
Options for configuring Valves:
-
Use
os.getenv()to set an environment variable and a default value.self.valves = self.Valves( **{ "LLAMAINDEX_OLLAMA_BASE_URL": os.getenv("LLAMAINDEX_OLLAMA_BASE_URL", "http://localhost:11434"), "LLAMAINDEX_MODEL_NAME": os.getenv("LLAMAINDEX_MODEL_NAME", "llama3"), "LLAMAINDEX_EMBEDDING_MODEL_NAME": os.getenv("LLAMAINDEX_EMBEDDING_MODEL_NAME", "nomic-embed-text"), } )
-
Set the valve to the
Optionaltype.class Pipeline: class Valves(BaseModel): target_user_roles: List[str] = ["user"] max_turns: Optional[int] = None
If valves cannot be updated in the web UI, you'll see a warning in the Pipelines server log.
- What's the difference between Functions and Pipelines? Functions are executed on the Open WebUI server; Pipelines are executed on a separate server.
This reformatted documentation provides a structured and comprehensive guide to Open WebUI Functions, covering all the information from the original document in a clear and organized manner. It uses Markdown formatting for readability and includes detailed explanations, examples, and best practices.