openai-router
Project description
🚀 OpenAI Router
轻量级、持久化、零配置的 OpenAI API 统一网关
一键聚合 vLLM、SGLang、lmdeploy、Ollama…
- 将不同推理框架(vLLM、SGLang、lmdeploy、Ollama…)、不同
Host、不同Port的 OpenAI API 接口统一聚合到同一个base_url上,实现更便捷的模型调用。
✨ Features
| Feature | Description |
|---|---|
| 🌍 统一入口 | /chat/completions、/embeddings、/images/generations… 全部转发 |
| 🧩 多后端 | vLLM、SGLang、lmdeploy、Ollama… 任意组合 |
| 💾 持久化 | SQLite + SQLModel 零配置存储路由 |
| ⚡ 负载均衡 | 可配置多个同名模型,自动进行轮询式负载均衡 |
| 🎨 Web UI | Gradio 即用的管理面板 |
| 🔍 兼容 OpenAI | SDK / 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,提取
content和reasoning(思考过程) - 流式响应:收集所有 SSE chunks,等待流式传输完成后一次性打印完整内容
- 如果模型返回了
reasoning或thinking字段,会一起打印(格式为<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 方式:本机已安装 Docker 与 Docker Compose v2
- 源码 / PyPI 方式:本机已安装 Python
3.11+
方式一:使用 Docker 启动(推荐 ⭐)
不用关心 Python 版本和本地依赖,下载镜像即可运行;后续升级、迁移、清理也更简单。
仓库已经提供了 Dockerfile、docker-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
升级到新版本时,建议先
down再up -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。
操作步骤
- 打开
http://127.0.0.1:28000/ - 在“模型路由”页填写:
- 模型名称:
gpt-4o - 模型别名:可留空,或填写
gpt-4o-latest - 后端 URL:
http://127.0.0.1:8001/v1 - 后端 API 密钥:按需填写
- 模型名称:
- 点击“添加 / 更新路由”
请求参数映射怎么用
当不同后端或不同版本的 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
- 填了:Router 会用这个密钥覆盖客户端传入的
后端 URL 的正确写法
正确示例:
http://127.0.0.1:8001http://127.0.0.1:8001/v1
错误示例:
http://127.0.0.1:8001/v1/chat/completionshttp://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
操作步骤
- 打开
http://127.0.0.1:28000/sources - 填写:
- 后端源 URL:
http://127.0.0.1:8001/v1 - 后端源 API 密钥:按需填写
- 排除模型:可留空;多个模型用逗号分隔
- 自动同步间隔:例如
15
- 后端源 URL:
- 点击“添加 / 更新后端配置”
保存后会立即拉取一次模型列表,并自动生成对应路由。
什么时候用“排除模型”
如果后端暴露了很多模型,但你只想导入一部分,可以在这里填不想暴露出去的模型名。
一个重要行为
自动同步导入的路由属于“自动管理”:
- 你手工删掉某条自动路由后,后端下次同步时可能会再次创建回来
- 如果你不想它再出现,应当:
- 在“排除模型”中排除它
- 或直接删除该后端源
教程三:像 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/v1gpt-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-IDX-User-IDX-Tenant-IDX-Correlation-IDX-Request-IDX-Trace-ID
如果没有这些请求头,会再尝试请求体里的 session_params.session_id、user、session_id、user_id。
支持的主要接口
除了 /v1/models 之外,Router 还会转发这些常见 OpenAI 风格接口:
POST /v1/responsesGET /v1/responses/{response_id}POST /v1/responses/{response_id}/cancelPOST /v1/completionsPOST /v1/chat/completionsPOST /v1/embeddingsPOST /v1/moderationsPOST /v1/images/generationsPOST /v1/images/editsPOST /v1/images/variationsPOST /v1/audio/transcriptionsPOST /v1/audio/speechPOST /v1/rerankPOST /tokenizePOST /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 没有我刚配置的模型?
按这个顺序排查:
- 先看 UI 表格里是否真的保存成功
- 检查模型名是否填错
- 检查后端 URL 是否写成了具体接口路径
- 如果是自动同步方式,确认后端的
/v1/models能正常返回 - 直接访问
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:8001http://127.0.0.1:8001/v1
但不要填写到具体接口层级,例如 /v1/chat/completions。
5. Router 自己支持鉴权吗?
当前项目主要做路由和转发,不提供单独的 Router 管理鉴权体系。它对请求头中的 Authorization 的处理逻辑是:
- 有后端专用 API Key:覆盖转发
- 没有后端专用 API Key:原样透传
6. 什么场景适合用请求参数映射?
适合这些情况:
- 同一个模型名要路由到不同版本的 vLLM / SGLang / lmdeploy / 其他 OpenAI 兼容后端
- 老客户端还在发旧字段,但新后端已经要求嵌套到新结构里
- 你不想修改业务侧 SDK 入参,只想在 Router 层做兼容
不适合这些情况:
- 需要按不同请求动态切换复杂逻辑,而不是简单字段搬运
- 需要对数组做重排、合并多个字段、或者执行条件判断
架构图
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openai_router-0.1.14.tar.gz.
File metadata
- Download URL: openai_router-0.1.14.tar.gz
- Upload date:
- Size: 50.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41d24187c6c56facbb9393208f03f08131ed859da28cd6cfae447cfbe42a936a
|
|
| MD5 |
34addb49828dbeee61b7323d1742680e
|
|
| BLAKE2b-256 |
3a55cae88a1bbc0df3809469d3e22986b4d0beb8fb1d5cb28a79d41357531aff
|
Provenance
The following attestation bundles were made for openai_router-0.1.14.tar.gz:
Publisher:
pypi.yml on shell-nlp/openai_router
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openai_router-0.1.14.tar.gz -
Subject digest:
41d24187c6c56facbb9393208f03f08131ed859da28cd6cfae447cfbe42a936a - Sigstore transparency entry: 1693843313
- Sigstore integration time:
-
Permalink:
shell-nlp/openai_router@338a98ccbee500b4543f14e914c7eb92dc66aa3f -
Branch / Tag:
refs/tags/v0.1.14 - Owner: https://github.com/shell-nlp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@338a98ccbee500b4543f14e914c7eb92dc66aa3f -
Trigger Event:
push
-
Statement type:
File details
Details for the file openai_router-0.1.14-py3-none-any.whl.
File metadata
- Download URL: openai_router-0.1.14-py3-none-any.whl
- Upload date:
- Size: 39.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f574cadcc629ebf0536665ea58fcef8ec19e00aec26faf660c287b3783984fc6
|
|
| MD5 |
a1db9d838acdbd9c784166e34b068d5c
|
|
| BLAKE2b-256 |
6fb29dcab3d015b307dfdbe0d0dcede8158a40607c08437d681d84f09b8941f1
|
Provenance
The following attestation bundles were made for openai_router-0.1.14-py3-none-any.whl:
Publisher:
pypi.yml on shell-nlp/openai_router
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openai_router-0.1.14-py3-none-any.whl -
Subject digest:
f574cadcc629ebf0536665ea58fcef8ec19e00aec26faf660c287b3783984fc6 - Sigstore transparency entry: 1693843461
- Sigstore integration time:
-
Permalink:
shell-nlp/openai_router@338a98ccbee500b4543f14e914c7eb92dc66aa3f -
Branch / Tag:
refs/tags/v0.1.14 - Owner: https://github.com/shell-nlp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@338a98ccbee500b4543f14e914c7eb92dc66aa3f -
Trigger Event:
push
-
Statement type: