跳到主要内容

🚰 管道函数:创建自定义“代理/模型”

欢迎来到本指南,我们将介绍如何在 Open WebUI 中创建管道(Pipes)。可以将管道视为一种向 Open WebUI 添加新模型的方式。本文档将详细解释什么是管道、它是如何工作的,以及如何创建自己的管道以向 Open WebUI 模型添加自定义逻辑和处理过程。我们将使用清晰的比喻并逐步讲解每一个细节,确保你能够全面理解。

管道简介

想象一下,Open WebUI 就像一个管道系统,数据通过管道和阀门流动。在这个类比中:

  • 管道就像插件,允许你引入新的数据流动路径,从而可以注入自定义逻辑和处理过程。
  • 阀门是管道中的可配置部分,控制数据如何流经管道。

通过创建一个管道,你实际上是在构建一个具有特定行为的自定义模型,所有这些都在 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):
# 逻辑代码写在这里
print(self.valves, body) # 这将打印配置选项和输入体
return "Hello, World!"

管道类

  • 定义: Pipe 类是你定义自定义逻辑的地方。
  • 用途: 作为插件的蓝图,决定了它在 Open WebUI 中的行为。

阀门:配置你的管道

  • 定义: ValvesPipe 类中的一个嵌套类,继承自 BaseModel
  • 用途: 包含跨管道使用的配置选项(参数)。
  • 示例: 在上面的代码中,MODEL_ID 是一个默认为空字符串的配置选项。

比喻: 可以把阀门想象成现实世界中的管道系统的旋钮,控制水流的方向。在你的管道中,阀门允许用户调整设置,影响数据的流动和处理方式。

__init__ 方法

  • 定义: Pipe 类的构造方法。
  • 用途: 初始化管道的状态并设置必要的组件。
  • 最佳实践: 保持简洁;主要在这里初始化 self.valves
def __init__(self):
self.valves = self.Valves()

pipe 函数

  • 定义: 核心函数,包含你的自定义逻辑。
  • 参数:
    • body: 包含输入数据的字典。
  • 用途: 使用自定义逻辑处理输入数据,并返回结果。
def pipe(self, body: dict):
# 逻辑代码写在这里
print(self.valves, 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):
# 逻辑代码写在这里
print(self.valves, body) # 打印配置选项和输入体
model = body.get("model", "")
return f"{model}: Hello, World!"

解释

  • pipes 函数:

    • 返回一个字典列表。
    • 每个字典代表一个带有唯一 idname 键的模型。
    • 这些模型将在 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="在模型名称前添加的前缀。",
)
OPENAI_API_BASE_URL: str = Field(
default="https://api.openai.com/v1",
description="访问 OpenAI API 端点的基本 URL。",
)
OPENAI_API_KEY: str = Field(
default="",
description="用于认证请求到 OpenAI API 的 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": "获取模型时出错。请检查您的 API 密钥。",
},
]
else:
return [
{
"id": "error",
"name": "未提供 API 密钥。",
},
]

def pipe(self, body: dict, __user__: dict):
print(f"pipe:{__name__}")
headers = {
"Authorization": f"Bearer {self.valves.OPENAI_API_KEY}",
"Content-Type": "application/json",
}

# 从模型名称中提取模型 ID
model_id = body["model"][body["model"].find(".") + 1:]

# 更新请求体中的模型 ID
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"错误: {e}"

详细分解

阀门配置

  • NAME_PREFIX:
    • 在 Open WebUI 中显示的模型名称前添加前缀。
    • 默认值: "OPENAI/"
  • OPENAI_API_BASE_URL:
    • 指定 OpenAI API 的基本 URL。
    • 默认值: "https://api.openai.com/v1"
  • OPENAI_API_KEY:
    • 用于认证请求到 OpenAI API 的 API 密钥。
    • 默认值: ""(空字符串;必须提供)。

pipes 函数

  • 用途: 获取可用的 OpenAI 模型并在 Open WebUI 中展示。

  • 流程:

    1. 检查 API 密钥: 确保提供了 API 密钥。
    2. 获取模型: 发起 GET 请求到 OpenAI API 获取可用模型。
    3. 过滤模型: 返回 ID 中包含 "gpt" 的模型。
    4. 错误处理: 如果有问题,返回错误信息。
  • 返回格式: 包含每个模型的 idname 的字典列表。

pipe 函数

  • 用途: 处理对选定 OpenAI 模型的请求并返回响应。

  • 参数:

    • body: 包含请求数据的字典。
    • __user__: 包含用户信息的字典(在此示例中未使用,但可用于身份验证或日志记录)。
  • 流程:

    1. 准备头部信息: 设置包含 API 密钥和内容类型的头部信息。
    2. 提取模型 ID: 从选定的模型名称中提取实际的模型 ID。
    3. 准备负载: 更新请求体中的正确模型 ID。
    4. 发起 API 请求: 向 OpenAI API 的聊天补全端点发送 POST 请求。
    5. 处理流式传输: 如果 streamTrue,则返回行迭代器。
    6. 错误处理: 捕获异常并返回错误消息。

扩展代理管道

你可以修改此代理管道,支持其他服务提供商如 Anthropic、Perplexity 等,只需调整 API 端点、头部信息和 pipespipe 函数中的逻辑即可。


使用内部 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:
# 使用更新后的签名调用统一端点
user = Users.get_user_by_id(__user__["id"])
body["model"] = "llama3.2:latest"
return await generate_chat_completion(__request__, body, user)

解释

  • 导入:

    • Users 来自 open_webui.models.users: 用于获取用户信息。
    • generate_chat_completion 来自 open_webui.utils.chat: 用于使用内部逻辑生成聊天补全。
  • 异步 pipe 函数:

    • 参数:
      • body: 模型的输入数据。
      • __user__: 包含用户信息的字典。
      • __request__: FastAPI 的请求对象(由 generate_chat_completion 需要)。
    • 流程:
      1. 获取用户对象: 使用用户 ID 获取用户对象。
      2. 设置模型: 指定要使用的模型。
      3. 生成补全: 调用 generate_chat_completion 处理输入并生成输出。

重要提示

  • 函数签名: 参考最新的 Open WebUI 代码库或文档以获取最准确的函数签名和参数。
  • 最佳实践: 始终优雅地处理异常和错误,以确保用户体验顺畅。

常见问题解答

Q1: 为什么我要在 Open WebUI 中使用管道?

A: 管道允许你在 Open WebUI 中添加具有自定义逻辑和处理的新“模型”。它是一个灵活的插件系统,使你可以集成外部 API、自定义模型行为并创建创新功能,而无需修改核心代码库。


Q2: 什么是阀门,为什么它们很重要?

A: 阀门是管道的可配置参数。它们就像设置或控件,决定管道的工作方式。通过调整阀门,你可以在不修改底层代码的情况下改变管道的行为。


Q3: 我可以创建没有阀门的管道吗?

A: 是的,如果管道不需要任何持久的配置选项,你可以创建一个简单的管道而不定义 Valves 类。然而,包括阀门是一个好的实践,为了灵活性和未来的扩展性。


Q4: 如何确保我的管道在使用 API 密钥时安全?

A: 不要在管道中硬编码敏感信息,如 API 密钥。相反,使用阀门输入和存储 API 密钥。确保你的代码适当地处理这些密钥,避免记录或暴露它们。


Q5: pipepipes 函数有什么区别?

A:

  • pipe 函数: 主要处理输入数据并生成输出的核心函数。它处理单个模型的逻辑。

  • pipes 函数: 允许你的管道代表多个模型,通过返回模型定义的列表。每个模型将单独出现在 Open WebUI 中。


Q6: 如何在我的管道中处理错误?

A: 在 pipepipes 函数中使用 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!