Skip to main content

openai-router

Project description

🚀 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

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,提取 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 层做兼容

不适合这些情况:

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

架构图

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openai_router-0.1.15.tar.gz (51.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openai_router-0.1.15-py3-none-any.whl (40.1 kB view details)

Uploaded Python 3

File details

Details for the file openai_router-0.1.15.tar.gz.

File metadata

  • Download URL: openai_router-0.1.15.tar.gz
  • Upload date:
  • Size: 51.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openai_router-0.1.15.tar.gz
Algorithm Hash digest
SHA256 452a8ab1b9f6ab6cba6226646fed5f18211edfab6b533dfc8ad26dae38346c18
MD5 996af326081e2f255a737a2c148d99c7
BLAKE2b-256 01bb56f8356da9071f6be15fe0ce6abbe6545808d28e32505e1258007ddc4480

See more details on using hashes here.

Provenance

The following attestation bundles were made for openai_router-0.1.15.tar.gz:

Publisher: pypi.yml on shell-nlp/openai_router

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openai_router-0.1.15-py3-none-any.whl.

File metadata

  • Download URL: openai_router-0.1.15-py3-none-any.whl
  • Upload date:
  • Size: 40.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openai_router-0.1.15-py3-none-any.whl
Algorithm Hash digest
SHA256 3a2cb2f8ed5624c6088e863f7191ce750fd77be904f52be66c2f531d0a9361ed
MD5 0d03c825a9613e80e5487fa440ec9425
BLAKE2b-256 2d0eb0d9c8447a52cde96f7c7f66b21d9e7bbbf137e3017aab7c1ac0df22bf8f

See more details on using hashes here.

Provenance

The following attestation bundles were made for openai_router-0.1.15-py3-none-any.whl:

Publisher: pypi.yml on shell-nlp/openai_router

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page