README.md

June 1, 2026 · View on GitHub

🚀 OpenAI Router

轻量级、持久化、零配置的 OpenAI API 统一网关
一键聚合 vLLM、SGLang、lmdeploy、Ollama…

Docker Image CI PyPI - Downloads Docker Pulls


  • 将不同推理框架(vLLM、SGLang、lmdeploy、Ollama…)、不同 Host、不同 Port 的 OpenAI API 接口统一聚合到同一个 base_url 上,实现更便捷的模型调用。

✨ Features

FeatureDescription
🌍 统一入口/chat/completions/embeddings/images/generations… 全部转发
🧩 多后端vLLM、SGLang、lmdeploy、Ollama… 任意组合
💾 持久化SQLite + SQLModel 零配置存储路由
⚡ 负载均衡可配置多个同名模型,自动进行轮询式负载均衡
🎨 Web UIGradio 即用的管理面板
🔍 兼容 OpenAISDK / LangChain / AutoGen / LlamaIndex / CrewAI 等一行代码都不用改
📝 请求/响应日志自动使用 Jinja2 模板渲染和打印聊天请求与响应内容,支持流式输出和思考过程

📝 请求/响应日志(重点功能 🚀)

⚠️ 其他开源路由(如 vLLM Router、SGLang Model Gateway、OneAPI 等)均不支持此功能!

在大模型开发调试过程中,无法直接看到模型收到的"原始提示词"和"思考过程",只能通过后端日志或额外工具来查看,非常不便。

Router 会自动记录聊天接口(/v1/chat/completions/v1/completions/v1/responses)的请求和响应内容:

请求日志

  • 当请求包含 messages 字段时,使用 chat_template.jinja 模板渲染为完整的提示词字符串
  • 渲染后的提示词会通过 logger.info 打印到日志
  • 你能直接看到模型收到的原始提示词,方便排查提示词工程问题

响应日志

  • 非流式响应:直接解析响应 JSON,提取 contentreasoning(思考过程)
  • 流式响应:收集所有 SSE chunks,等待流式传输完成后一次性打印完整内容
  • 如果模型返回了 reasoningthinking 字段,会一起打印(格式为 <think>\n...\n</think>
  • 你能直接看到模型的思考过程,这对调试思维链模型至关重要

示例输出

请求日志示例:

INFO: Rendered prompt:
<|im_start|>system
你是一个有帮助的助手。<|im_end|>
<|im_start|>user
你好!<|im_end|>
<|im_start|>assistant
<think>

</think>

响应日志示例:

INFO: Model response:
<think>
1. 分析用户输入:用户用中文打招呼
2. 确定意图:只是想开始对话
3. 构思回复:用中文问候,并询问有什么可以帮助的
</think>

你好!很高兴能为你提供帮助。请问今天有什么我可以帮你的吗?😊

有了这个功能,你在调试 LLM 应用时再也不用猜测模型在想什么了!


快速开始

运行前准备

通用前置:

  • 一个已经能正常工作的 OpenAI 兼容后端
  • 后端地址建议先单独验证可用,例如:
curl http://127.0.0.1:8001/v1/models

根据你选择的启动方式,还需:

方式一:使用 Docker 启动(推荐 ⭐)

不用关心 Python 版本和本地依赖,下载镜像即可运行;后续升级、迁移、清理也更简单。

仓库已经提供了 Dockerfiledocker-compose.yml(使用预构建镜像)和 docker-compose-build.yml(从源码本地构建)三份配置,可按需选择其中一种方式启动。

1.1 docker run 直接启动

使用 Docker Hub 上的预构建镜像:

docker run -d \
  --name openai-router \
  --restart unless-stopped \
  -p 8082:8082 \
  -e TZ=Asia/Shanghai \
  -v "$(pwd)/data:/app/data" \
  506610466/openai-router:latest

如果想使用本仓库当前 commit 的代码,可以先本地构建再运行:

docker build -t openai-router:dev -f Dockerfile .

docker run -d \
  --name openai-router \
  --restart unless-stopped \
  -p 8082:8082 \
  -e TZ=Asia/Shanghai \
  -v "$(pwd)/data:/app/data" \
  openai-router:dev

1.2 docker compose 启动(推荐)

更推荐使用 compose,配置和命令更易复用。

使用预构建镜像(默认 docker-compose.yml,从 506610466/openai-router:latest 拉取):

docker compose up -d

从源码本地构建docker-compose-build.yml,会读取当前目录下的 Dockerfile):

docker compose -f docker-compose-build.yml up --build -d

⚠️ 默认的 docker-compose.yml没有 build 段,所以 docker compose up --build -d 不会触发本地构建;如果你想从源码构建,请使用 docker-compose-build.yml,或自行添加 build 配置。

1.3 启动后访问

容器启动后,可在宿主机通过以下地址访问:

  • Web UI:http://127.0.0.1:8082/
  • OpenAI 兼容入口:http://127.0.0.1:8082/v1
  • Swagger 文档:http://127.0.0.1:8082/docs
  • 健康检查:http://127.0.0.1:8082/health

先验证一下服务是否正常:

curl -i http://127.0.0.1:8082/health

返回 HTTP/1.1 200 OK 即表示启动成功。

1.4 常用运维

  • 修改宿主机端口:把 -p 8082:8082 改成 -p <宿主机端口>:8082 即可;如果是 compose 文件,把 ports 一行改为 "<宿主机端口>:8082",然后重新 up -d

  • 数据持久化:容器内 /app/data 已通过 volume 挂载到宿主机 ./data,重启和升级容器后路由配置仍会保留。

  • 查看日志

    # docker run 方式
    docker logs -f openai-router
    
    # docker compose 方式
    docker compose logs -f
    # 或
    docker compose -f docker-compose-build.yml logs -f
    
  • 停止与重建

    # docker run 方式
    docker stop openai-router
    docker rm openai-router
    
    # docker compose 方式
    docker compose down
    # 或
    docker compose -f docker-compose-build.yml down
    

    升级到新版本时,建议先 downup -d(或 pull && up -d),避免旧容器残留。

方式二:在当前仓库内运行

适合你想直接修改本项目源码、立即看到改动的场景。

uv sync
uv run openai-router --host 0.0.0.0 --port 28000

如果你不用 uv,也可以:

pip install -e .
openai-router --host 0.0.0.0 --port 28000

方式三:从 PyPI 安装

uv tool install openai-router
openai-router --host 0.0.0.0 --port 28000

或者:

pip install -U openai-router
openai-router --host 0.0.0.0 --port 28000

uv tool 安装和使用示例

如果你不想进入项目源码目录,也不想自己管理虚拟环境,可以直接用 uv tool

uv tool install openai-router
uv tool run openai-router --host 0.0.0.0 --port 28000

如果你刚安装完,命令还没有生效,可以先执行:

uv tool update-shell

然后重新打开终端,再检查:

openai-router --help

启动后,继续按本文后面的步骤:

  • 打开 http://127.0.0.1:28000/ 配置模型路由
  • 使用 http://127.0.0.1:28000/v1 作为统一 base_url

启动后你应该看到什么

服务启动后可访问:

  • Web UI:http://127.0.0.1:<端口>/
  • Swagger:http://127.0.0.1:<端口>/docs
  • 健康检查:http://127.0.0.1:<端口>/health
  • OpenAI 兼容入口:http://127.0.0.1:<端口>/v1

其中 <端口> 取决于启动方式:

  • Docker 启动<端口> 默认是 8082
  • 源码 / PyPI 启动<端口> 默认是 28000

先验证健康检查:

# Docker 方式
curl -i http://127.0.0.1:8082/health

# 源码 / PyPI 方式
curl -i http://127.0.0.1:28000/health

返回 HTTP/1.1 200 OK 说明服务已启动。


教程一:手动添加一条模型路由

这是最直接、最不容易出错的用法,建议第一次先用这个方式跑通。

场景

假设你的后端服务地址是:

http://127.0.0.1:8001/v1

并且这个后端支持模型 gpt-4o

操作步骤

  1. 打开 http://127.0.0.1:28000/
  2. 在“模型路由”页填写:
    • 模型名称:gpt-4o
    • 模型别名:可留空,或填写 gpt-4o-latest
    • 后端 URL:http://127.0.0.1:8001/v1
    • 后端 API 密钥:按需填写
  3. 点击“添加 / 更新路由”

请求参数映射怎么用

当不同后端或不同版本的 OpenAI 兼容服务对请求体字段要求不一致时,可以在“模型路由”页为单个模型配置“请求参数映射”。

界面上使用两列表格配置:

  • 左列 key:原始请求体里的参数路径
  • 右列 value:转发给后端时应写入的目标路径

例如,老版本客户端发送:

{
  "model": "deepseek-r1",
  "messages": [],
  "enable_thinking": false
}

而新版本后端要求:

{
  "model": "deepseek-r1",
  "messages": [],
  "chat_template_kwargs": {
    "enable_thinking": false
  }
}

那么你只需要添加一行映射:

  • enable_thinking -> chat_template_kwargs.enable_thinking

保存后,Router 会在请求转发前自动完成字段搬运。

请求参数映射的规则

  • 映射是“模型级”的:只对当前这条模型路由生效
  • 映射发生在真正转发到后端之前
  • 如果源字段不存在,该条映射会被忽略,不会报错
  • 支持点路径写法,例如 metadata.request.trace_id
  • 当前界面配置方式是 key/value 表格,但底层仍以 JSON 对象持久化保存

每个字段怎么理解

  • 模型名称:客户端请求时 model 字段使用的名字
  • 模型别名:可选,多个别名用英文逗号分隔
  • 后端 URL:填写后端“基地址”,不要填写具体接口路径
  • 后端 API 密钥:
    • 填了:Router 会用这个密钥覆盖客户端传入的 Authorization
    • 不填:Router 会透传客户端原始 Authorization

后端 URL 的正确写法

正确示例:

  • http://127.0.0.1:8001
  • http://127.0.0.1:8001/v1

错误示例:

  • http://127.0.0.1:8001/v1/chat/completions
  • http://127.0.0.1:8001/v1/models

原因是 Router 会自动把请求路径拼接到你填写的后端 URL 后面。


教程二:自动同步一个后端源

如果你的后端支持模型列表接口,这个方式更省事。

Router 的自动发现规则

当你添加“后端源”时,Router 会立即尝试获取模型列表:

  • 如果你填的是 http://127.0.0.1:8001,会优先请求 /v1/models,再尝试 /models
  • 如果你填的是 http://127.0.0.1:8001/v1,会请求 /v1/models

操作步骤

  1. 打开 http://127.0.0.1:28000/sources
  2. 填写:
    • 后端源 URL:http://127.0.0.1:8001/v1
    • 后端源 API 密钥:按需填写
    • 排除模型:可留空;多个模型用逗号分隔
    • 自动同步间隔:例如 15
  3. 点击“添加 / 更新后端配置”

保存后会立即拉取一次模型列表,并自动生成对应路由。

什么时候用“排除模型”

如果后端暴露了很多模型,但你只想导入一部分,可以在这里填不想暴露出去的模型名。

一个重要行为

自动同步导入的路由属于“自动管理”:

  • 你手工删掉某条自动路由后,后端下次同步时可能会再次创建回来
  • 如果你不想它再出现,应当:
    • 在“排除模型”中排除它
    • 或直接删除该后端源

教程三:像 OpenAI 官方 SDK 一样调用

配置好路由后,业务代码只需要把 base_url 指向 Router。

先列出模型确认路由是否生效

curl http://127.0.0.1:28000/v1/models \
  -H "Authorization: Bearer sk-test"

你应该能看到刚才配置的模型名或别名。

Python 示例

from openai import OpenAI

client = OpenAI(
    base_url="http://127.0.0.1:28000/v1",
    api_key="sk-test",
)

resp = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "hello"}],
    stream=False,
)

print(resp.choices[0].message.content)

cURL 示例

如果你没有在 Router 中给该后端配置专用 API Key,那么这里的 Authorization 需要是真实可用的后端密钥。

curl http://127.0.0.1:28000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-test" \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "你好"}]
  }'

流式输出示例

from openai import OpenAI

client = OpenAI(
    base_url="http://127.0.0.1:28000/v1",
    api_key="sk-test",
)

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "请介绍一下你自己"}],
    stream=True,
)

for chunk in stream:
    text = chunk.choices[0].delta.content or ""
    print(text, end="")

多后端负载均衡怎么用

如果你给同一个模型名配置了多个后端,例如:

  • gpt-4o -> http://127.0.0.1:8001/v1
  • gpt-4o -> http://127.0.0.1:8002/v1

那么 Router 会把同一个 model 的请求分发到这些后端。

当前支持两种策略:

  • round_robin:默认策略,轮询分发
  • consistent_hash:同一会话尽量稳定落到同一后端

http://127.0.0.1:28000/sources 页面可以切换策略。

consistent_hash 会优先参考这些请求头:

  • X-Session-ID
  • X-User-ID
  • X-Tenant-ID
  • X-Correlation-ID
  • X-Request-ID
  • X-Trace-ID

如果没有这些请求头,会再尝试请求体里的 session_params.session_idusersession_iduser_id


支持的主要接口

除了 /v1/models 之外,Router 还会转发这些常见 OpenAI 风格接口:

  • POST /v1/responses
  • GET /v1/responses/{response_id}
  • POST /v1/responses/{response_id}/cancel
  • POST /v1/completions
  • POST /v1/chat/completions
  • POST /v1/embeddings
  • POST /v1/moderations
  • POST /v1/images/generations
  • POST /v1/images/edits
  • POST /v1/images/variations
  • POST /v1/audio/transcriptions
  • POST /v1/audio/speech
  • POST /v1/rerank
  • POST /tokenize
  • POST /detokenize

GET /v1/responses/{response_id}POST /v1/responses/{response_id}/cancel 会根据 Router 在创建 POST /v1/responses 时记录的 response_id -> backend 映射进行转发,因此要求创建、查询、取消请求经过同一个 Router 进程;如果 Router 重启,这类映射会失效。


数据持久化

使用 SQLite 持久化保存路由配置。

源码方式运行时,数据库默认在:

./data/routes.db

因此你重启服务后,路由配置仍然会保留。

另外,请求参数映射同样保存在 ModelRoute 记录上。如果你是从旧版本升级到包含该字段的新版本,当前项目的 schema 检测逻辑可能会判断本地 SQLite 结构已变化,并重建 data/routes.db。如果你本地库里已有重要路由配置,升级前建议先备份。


常见问题

1. 为什么 /v1/models 没有我刚配置的模型?

按这个顺序排查:

  1. 先看 UI 表格里是否真的保存成功
  2. 检查模型名是否填错
  3. 检查后端 URL 是否写成了具体接口路径
  4. 如果是自动同步方式,确认后端的 /v1/models 能正常返回
  5. 直接访问 http://127.0.0.1:28000/v1/models 看返回结果

2. 客户端的 API Key 应该填什么?

  • 如果你在 Router 里为后端配置了 API Key:客户端可以填任意非空值,例如 sk-test
  • 如果你没有在 Router 里配置后端 API Key:客户端必须传真实后端可用的 Bearer Token

3. 为什么我删除了一条自动同步的模型路由,它又出现了?

因为该模型还存在于后端的模型列表中,下次自动同步会重新导入。

正确做法是:

  • 在“排除模型”中排除它
  • 或删除对应后端源

4. 后端 URL 到底填不填 /v1

两种都可以:

  • http://127.0.0.1:8001
  • http://127.0.0.1:8001/v1

但不要填写到具体接口层级,例如 /v1/chat/completions

5. Router 自己支持鉴权吗?

当前项目主要做路由和转发,不提供单独的 Router 管理鉴权体系。它对请求头中的 Authorization 的处理逻辑是:

  • 有后端专用 API Key:覆盖转发
  • 没有后端专用 API Key:原样透传

6. 什么场景适合用请求参数映射?

适合这些情况:

  • 同一个模型名要路由到不同版本的 vLLM / SGLang / lmdeploy / 其他 OpenAI 兼容后端
  • 老客户端还在发旧字段,但新后端已经要求嵌套到新结构里
  • 你不想修改业务侧 SDK 入参,只想在 Router 层做兼容

不适合这些情况:

  • 需要按不同请求动态切换复杂逻辑,而不是简单字段搬运
  • 需要对数组做重排、合并多个字段、或者执行条件判断

架构图