🚰 Pipe Function: Create Custom "Agents/Models"
Welcome to this guide on creating Pipes in Open WebUI! Think of Pipes as a way to adding a new model to Open WebUI. In this document, we'll break down what a Pipe is, how it works, and how you can create your own to add custom logic and processing to your Open WebUI models. We'll use clear metaphors and go through every detail to ensure you have a comprehensive understanding.
Introduction to Pipes
Imagine Open WebUI as a plumbing system where data flows through pipes and valves. In this analogy:
- Pipes are like plugins that let you introduce new pathways for data to flow, allowing you to inject custom logic and processing.
- Valves are the configurable parts of your pipe that control how data flows through it.
By creating a Pipe, you're essentially crafting a custom model with the specific behavior you want, all within the Open WebUI framework.
Understanding the Pipe Structure
Let's start with a basic, barebones version of a Pipe to understand its 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) # This will print the configuration options and the input body
return "Hello, World!"
The Pipe Class
- Definition: The
Pipe
class is where you define your custom logic. - Purpose: Acts as the blueprint for your plugin, determining how it behaves within Open WebUI.
Valves: Configuring Your Pipe
- Definition:
Valves
is a nested class withinPipe
, inheriting fromBaseModel
. - Purpose: It contains the configuration options (parameters) that persist across the use of your Pipe.
- Example: In the above code,
MODEL_ID
is a configuration option with a default empty string.
Metaphor: Think of Valves as the knobs on a real-world pipe system that control the flow of water. In your Pipe, Valves allow users to adjust settings that influence how the data flows and is processed.
The __init__
Method
- Definition: The constructor method for the
Pipe
class. - Purpose: Initializes the Pipe's state and sets up any necessary components.
- Best Practice: Keep it simple; primarily initialize
self.valves
here.
def __init__(self):
self.valves = self.Valves()
The pipe
Function
- Definition: The core function where your custom logic resides.
- Parameters:
body
: A dictionary containing the input data.
- Purpose: Processes the input data using your custom logic and returns the result.
def pipe(self, body: dict):
# Logic goes here
print(self.valves, body) # This will print the configuration options and the input body
return "Hello, World!"
Note: Always place Valves
at the top of your Pipe
class, followed by __init__
, and then the pipe
function. This structure ensures clarity and consistency.
Creating Multiple Models with Pipes
What if you want your Pipe to create multiple models within Open WebUI? You can achieve this by defining a pipes
function or variable inside your Pipe
class. This setup, informally called a manifold, allows your Pipe to represent multiple models.
Here's how you can do it:
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) # Prints the configuration options and the input body
model = body.get("model", "")
return f"{model}: Hello, World!"
Explanation
-
pipes
Function:- Returns a list of dictionaries.
- Each dictionary represents a model with unique
id
andname
keys. - These models will show up individually in the Open WebUI model selector.
-
Updated
pipe
Function:- Processes input based on the selected model.
- In this example, it includes the model name in the returned string.
Example: OpenAI Proxy Pipe
Let's dive into a practical example where we'll create a Pipe that proxies requests to the OpenAI API. This Pipe will fetch available models from OpenAI and allow users to interact with them through Open WebUI.
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. Please 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
Valves Configuration
NAME_PREFIX
:- Adds a prefix to the model names displayed in Open WebUI.
- Default:
"OPENAI/"
.
OPENAI_API_BASE_URL
:- Specifies the base URL for the OpenAI API.
- Default:
"https://api.openai.com/v1"
.
OPENAI_API_KEY
:- Your OpenAI API key for authentication.
- Default:
""
(empty string; must be provided).
The pipes
Function
-
Purpose: Fetches available OpenAI models and makes them accessible in Open WebUI.
-
Process:
- Check for API Key: Ensures that an API key is provided.
- Fetch Models: Makes a GET request to the OpenAI API to retrieve available models.
- Filter Models: Returns models that have
"gpt"
in theirid
. - Error Handling: If there's an issue, returns an error message.
-
Return Format: A list of dictionaries with
id
andname
for each model.
The pipe
Function
-
Purpose: Handles the request to the selected OpenAI model and returns the response.
-
Parameters:
body
: Contains the request data.__user__
: Contains user information (not used in this example but can be useful for authentication or logging).
-
Process:
- Prepare Headers: Sets up the headers with the API key and content type.
- Extract Model ID: Extracts the actual model ID from the selected model name.
- Prepare Payload: Updates the body with the correct model ID.
- Make API Request: Sends a POST request to the OpenAI API's chat completions endpoint.
- Handle Streaming: If
stream
isTrue
, returns an iterable of lines. - Error Handling: Catches exceptions and returns an error message.
Extending the Proxy Pipe
You can modify this proxy Pipe to support additional service providers like Anthropic, Perplexity, and more by adjusting the API endpoints, headers, and logic within the pipes
and pipe
functions.