🚰 管道函数:创建自定义“代理/模型”
欢迎阅读这份关于在 Open WebUI 中创建 管道 (Pipes) 的指南!您可以将管道理解为一种在 Open WebUI 中添加新模型的方式。本文将详细介绍什么是管道、它是如何工作的以及如何创建自己的管道来为您的 Open WebUI 模型添加自定义逻辑和处理。我们将使用清晰的比喻并详细讲解每个细节,确保您能全面理解。
管道简介
将 Open WebUI 想象成一个数据流经管道和阀门的管道系统。在这个比喻中:
- 管道 (Pipes) 就像是插件,它们允许您引入新的数据流路径,从而注入自定义逻辑和处理。
- 阀门 (Valves) 是您管道的可配置部分,控制数据如何流过它。
通过创建一个管道,您实际上是在 Open WebUI 框架内构建了一个具有您所需特定行为的自定义模型。
理解管道结构
让我们从一个最基本的管道版本开始,以理解其结构:
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!"
管道类
- 定义:
Pipe
类是您定义自定义逻辑的地方。 - 目的:充当您插件的蓝图,决定其在 Open WebUI 中的行为方式。
阀门:配置您的管道
- 定义:
Valves
是Pipe
类中的一个嵌套类,继承自BaseModel
。 - 目的:它包含在使用您的管道时持续存在的配置选项(参数)。
- 示例:在上面的代码中,
MODEL_ID
是一个配置选项,默认值为空字符串。
比喻:将阀门想象成现实世界管道系统中控制水流的旋钮。在您的管道中,阀门允许用户调整设置,从而影响数据如何流经和被处理。
__init__
方法
- 定义:
Pipe
类的构造方法。 - 目的:初始化管道的状态并设置任何必要的组件。
- 最佳实践:保持其简单;主要在此处初始化
self.valves
。
def __init__(self):
self.valves = self.Valves()
pipe
函数
- 定义:这是您自定义逻辑的核心函数。
- 参数:
body
:一个包含输入数据的字典。
- 目的:使用您的自定义逻辑处理输入数据并返回结果。
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!"
注意:始终将 Valves
放在 Pipe
类的顶部,然后是 __init__
,最后是 pipe
函数。这种结构确保了清晰度和一致性。
使用管道创建多个模型
如果您想让您的管道在 Open WebUI 中创建多个模型怎么办?您可以在您的 Pipe
类内部定义一个 pipes
函数或变量来实现。这种设置,非正式地称为分流器 (manifold),允许您的管道表示多个模型。
以下是实现方法:
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!"
说明
-
pipes
函数:- 返回一个字典列表。
- 每个字典表示一个模型,具有唯一的
id
和name
键。 - 这些模型将单独显示在 Open WebUI 模型选择器中。
-
更新的
pipe
函数:- 根据选定的模型处理输入。
- 在此示例中,它将模型名称包含在返回的字符串中。
示例:OpenAI 代理管道
让我们深入了解一个实际示例,我们将创建一个代理请求到 OpenAI API 的管道。这个管道将从 OpenAI 获取可用模型,并允许用户通过 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}"
详细分解
阀门配置
NAME_PREFIX
:- 为 Open WebUI 中显示的模型名称添加前缀。
- 默认值:
"OPENAI/"
。
OPENAI_API_BASE_URL
:- 指定 OpenAI API 的基本 URL。
- 默认值:
"https://api.openai.com/v1"
。
OPENAI_API_KEY
:- 您的 OpenAI API 密钥,用于身份验证。
- 默认值:
""
(空字符串;必须提供)。
pipes
函数
-
目的:获取可用的 OpenAI 模型,并在 Open WebUI 中使其可访问。
-
流程:
- 检查 API 密钥:确保提供了 API 密钥。
- 获取模型:向 OpenAI API 发起 GET 请求以检索可用模型。
- 过滤模型:返回
id
中包含"gpt"
的模型。 - 错误处理:如果出现问题,返回错误消息。
-
返回格式:一个字典列表,包含每个模型的
id
和name
。
pipe
函数
-
目的:处理对选定 OpenAI 模型的请求并返回响应。
-
参数:
body
:包含请求数据。__user__
:包含用户信息(在此示例中未使用,但对于身份验证或日志记录可能有用)。
-
流程:
- 准备头部信息:使用 API 密钥和内容类型设置头部信息。
- 提取模型 ID:从选定的模型名称中提取实际的模型 ID。
- 准备载荷:使用正确的模型 ID 更新 body。
- 发起 API 请求:向 OpenAI API 的聊天补全端点发送 POST 请求。
- 处理流式传输:如果
stream
为True
,则返回一个行的可迭代对象。 - 错误处理:捕获异常并返回错误消息。
扩展代理管道
您可以修改此代理管道,通过调整 pipes
和 pipe
函数中的 API 端点、头部信息和逻辑,以支持其他服务提供商,如 Anthropic、Perplexity 等。
使用内部 Open WebUI 函数
有时,您可能希望在您的管道中利用 Open WebUI 的内部函数。您可以直接从 open_webui
包导入这些函数。请记住,尽管不太可能,内部函数可能会出于优化目的而更改,因此请始终查阅最新的文档。
以下是使用内部 Open WebUI 函数的方法:
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"
return await generate_chat_completion(__request__, body, user)
说明
-
导入:
- 从
open_webui.models.users
导入Users
:用于获取用户信息。 - 从
open_webui.utils.chat
导入generate_chat_completion
:用于使用内部逻辑生成聊天补全。
- 从
-
异步
pipe
函数:- 参数:
body
:模型的输入数据。__user__
:包含用户信息的字典。__request__
:来自 FastAPI 的请求对象(generate_chat_completion
需要)。
- 流程:
- 获取用户对象:使用用户 ID 检索用户对象。
- 设置模型:指定要使用的模型。
- 生成补全:调用
generate_chat_completion
处理输入并生成输出。
- 参数:
重要注意事项
- 函数签名:查阅最新的 Open WebUI 代码库或文档,以获取最准确的函数签名和参数。
- 最佳实践:始终优雅地处理异常和错误,以确保流畅的用户体验。
常见问题解答
Q1: 我为什么应该在 Open WebUI 中使用管道?
A: 管道允许您向 Open WebUI 添加具有自定义逻辑和处理的新“模型”。它是一个灵活的插件系统,让您可以集成外部 API、自定义模型行为以及创建创新功能,而无需修改核心代码库。
Q2: 什么是阀门,为什么它们很重要?
A: 阀门是您的管道的可配置参数。它们就像设置或控件,决定您的管道如何运行。通过调整阀门,您无需修改底层代码即可更改管道的行为。
Q3: 我可以创建一个没有阀门的管道吗?
A: 是的,如果您的管道不需要任何持久配置选项,您可以创建一个不定义 Valves 类的简单管道。然而,包含 Valves 是为了灵活性和未来可扩展性的良好实践。
Q4: 使用 API 密钥时,我如何确保我的管道是安全的?
A: 绝不要将 API 密钥等敏感信息硬编码到您的管道中。相反,使用 Valves 来安全地输入和存储 API 密钥。确保您的代码正确处理这些密钥,并避免记录或暴露它们。
Q5: pipe
函数和 pipes
函数有什么区别?
A:
-
pipe
函数:这是处理输入数据并生成输出的主要函数。它处理单个模型的逻辑。 -
pipes
函数:通过返回模型定义列表,允许您的管道表示多个模型。每个模型将单独显示在 Open WebUI 中。
Q6: 我如何在管道中处理错误?
A: 在您的 pipe
和 pipes
函数中使用 try-except 块来捕获异常。返回有意义的错误消息或优雅地处理错误,以确保用户被告知哪里出了问题。
Q7: 我可以在管道中使用外部库吗?
A: 是的,您可以根据需要导入和使用外部库。确保您的环境中正确安装和管理了所有依赖项。
Q8: 我如何测试我的管道?
A: 通过在开发环境中运行 Open WebUI 并从界面中选择您的自定义模型来测试您的管道。验证您的管道在各种输入和配置下是否按预期运行。
Q9: 组织管道代码有哪些最佳实践?
A: 是的,遵循以下指导原则:
- 将
Valves
放在Pipe
类的顶部。 - 在
__init__
方法中初始化变量,主要是self.valves
。 - 将
pipe
函数放在__init__
方法之后。 - 使用清晰且具有描述性的变量名。
- 为代码添加注释以提高清晰度。
Q10: 我在哪里可以找到最新的 Open WebUI 文档?
A: 访问 Open WebUI 官方仓库或文档网站,获取最新信息,包括函数签名、示例以及(如果发生任何更改)迁移指南。
结论
至此,您应该对如何在 Open WebUI 中创建和使用管道有了全面的了解。管道提供了一种强大的方式,可以根据您的特定需求扩展和定制 Open WebUI 的功能。无论您是要集成外部 API、添加新模型还是注入复杂的逻辑,管道都提供了实现的灵活性。
请记住:
- 在您的管道类中使用清晰一致的结构。
- 利用阀门进行配置选项。
- 优雅地处理错误以改善用户体验。
- 查阅最新文档以了解任何更新或更改。
愉快地编程,享受使用管道扩展您的 Open WebUI 的乐趣!