添加新模型
本指南写给想让网关多提供一个模型的运维人员。如果网关已经支持这个 provider(任何 OpenAI 兼容 API、OpenRouter、Anthropic、Vertex 上的 Claude 或 Gemini),只需要改配置:在模型注册表里加一个条目,配好凭据,然后重启。
如果是你自己用 vLLM、SGLang 或 Ollama 部署的模型,见添加新的本地模型。
如果 provider 有自己的一套非 OpenAI API,需要先写一个 adapter;见编写 provider adapter。
配置了数据库时,也可以在网关运行期间从管理控制台添加模型、路由或 key;见从管理控制台做运行时配置。不过在控制台创建的模型没有模型目录所需的元数据,所以需要设置上下文长度、模态或别名的模型,还是要写进注册表。
模型注册表在哪里
网关使用 MODELS_CONFIG_PATH 指定的文件;没设置时,用已启用的发行版 manifest 指定的文件;两者都没有时,用自带的示例 config/examples/models.openrouter.yaml。详见网关如何找到自己的配置。
因此,要使用自己的注册表,有两种做法:
指向一个文件。注册表放在任意位置,设置
MODELS_CONFIG_PATH=/path/to/models.yaml即可。README.md里的快速开始就是这么做的。建一个发行版。创建
distributions/<name>/,里面放一份distribution.yamlmanifest 和一份config/models.yaml,然后设置DISTRIBUTION_CONFIG_PATH=distributions/<name>/distribution.yaml和DISTRIBUTION_CONFIG_MODE=active。distributions/example/是一个可以直接复制的可用发行版;config/examples/distribution.example.yaml是一份带注释的 manifest。
本指南中所说的「你的模型注册表」,指的就是上述解析过程最终选中的那个文件。
如果解析出的路径上没有注册表,后端会记一条错误日志,/v1/models 返回空列表,请求任何模型都会报 not found。
添加模型
在模型注册表中添加一条模型条目:
models:
- id: your-model-id
name: Your Model Display Name
provider: existing_provider # e.g. "gemini", "deepseek"
provider_model_id: "actual-provider-model-id"
base_url: ${PROVIDER_BASE_URL}
api_key: ${PROVIDER_API_KEY}
quantization: "bf16"
input_modalities: ["text"]
output_modalities: ["text"]
context_length: 8192
max_output_length: 4096
supports_tools: true
supports_structured_output: true
supported_params: [temperature, top_p, max_tokens, stop]
pricing:
prompt: "0"
completion: "0"
image: "0"
request: "0"
input_cache_reads: "0"
input_cache_writes: "0"
route:
- kind: existing_provider
weight: 1.0
路由会继承模型的 base_url 和 api_key,所以路由上只需写不一样的部分。如果第二条路由指向别的地址,就在那条路由上把这两项再写一遍。
在仓库根目录的
.env里设置环境变量(后端的配置加载器读的就是这个文件):
PROVIDER_BASE_URL=https://api.provider.example/v1
PROVIDER_API_KEY=your-api-key
如果 api_key、api_keys 或 base_url 用到的某个 ${VAR} 没有设置或为空,网关会跳过整个模型,并在日志里写明缺的是哪个变量。给路由加上 optional: true,就只跳过那一条路由。
重启后端以加载新模型。
按下文的方法验证它。
关于别名:如果想让客户端也能用另一个名字调用这个模型,比如 OpenRouter 风格的厂商 slug,或者你的推理运行时使用的原始模型路径,就把这些名字列进 aliases。它们会解析到同一组路由。
通过网关验证
启动后端。直接从源码运行时:
MODELS_CONFIG_PATH=/path/to/your/models.yaml \
uv run uvicorn serving.servers.app:app --no-proxy-headers --port 8080
如果用仓库自带的 Docker Compose,则改用 make build s=backend 重新构建并重启后端。
GET /v1/models 接受匿名请求——它解析 API key 只是为了决定要不要包含仅管理员可见的条目:
curl -s http://localhost:8080/v1/models | jq
POST /v1/chat/completions 没有有效的网关 API key 就会返回 401,除非后端以 USER_AUTH_ENABLED=false 运行。请求时要带上你在自己网关上签发的 key(是网关的 key,不是上游 provider 的):
export GATEWAY_API_KEY=<your gateway API key>
curl -s -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer $GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "your-model-id",
"messages": [{"role": "user", "content": "Hello!"}]
}' | jq
流式测试:
curl -N -s -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer $GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "your-model-id",
"messages": [{"role": "user", "content": "Stream test"}],
"stream": true,
"max_tokens": 64
}'
配置参考
模型字段
这些键可以写在模型条目上。其中大多数也可以写在路由条目上,此时以路由上的值为准;id 和 name 标识的是模型本身,只在模型层级读取。
字段 |
类型 |
必填 |
说明 |
|---|---|---|---|
|
string |
是 |
唯一的模型标识;客户端发送的就是这个名字 |
|
string |
是 |
展示名称 |
|
string |
是 |
provider/adapter 的 kind;没有写 |
|
string |
是,写在这里或每条路由上 |
API 端点的 base URL |
|
string |
否 |
API 鉴权密钥 |
|
string |
否 |
provider 那边的模型标识(实际发给上游时用它代替 |
|
string |
否 |
|
|
list[string] |
否 |
可用于路由的其他名称 |
|
string |
否 |
量化格式(默认: |
|
list[string] |
否 |
输入类型: |
|
list[string] |
否 |
输出类型: |
|
int |
否 |
最大上下文窗口(默认:8192) |
|
int |
否 |
最大输出 token 数(默认:4096); |
|
bool |
否 |
是否支持 function calling(默认:false) |
|
bool |
否 |
是否支持 JSON mode(默认:false) |
|
list[string] |
否 |
允许的参数名(默认: |
|
list[string] |
否 |
这个模型的 |
|
bool |
否 |
模型在共享 GPU 上按需加载(首次请求时启动,空闲时停止)。这个标记会在 |
|
string |
否 |
为 |
|
dict |
否 |
合并进 OpenAI 兼容上游请求体的默认字段。核心字段和已校验的客户端参数优先 |
|
bool |
否 |
该端点上的 sglang 服务器是带 |
|
dict |
否 |
每条路由上的自由格式元数据,供路由策略读取 |
|
dict |
否 |
基础价格。 |
|
dict |
否 |
一个生效时间(只支持 UTC),加上每天循环的价格时段: |
模型条目还接受 route(见下文)、router 和 router_params(见为每个模型选择 router),以及 required_role——能看到并调用该模型的最低用户角色(admin_only: true 是 required_role: admin 的旧写法)。
路由配置
路由让一个模型拥有多个端点,并按权重分配流量:
route:
# Local vLLM deployment
- kind: vllm
weight: 0.7 # 70% of traffic
base_url: http://localhost:8000
provider_model_id: "/models/local-model"
# Remote API fallback
- kind: your_provider
weight: 0.3 # 30% of traffic
base_url: https://api.provider.example
api_key: ${API_KEY}
除上面的模型字段之外,路由条目还接受:
字段 |
类型 |
说明 |
|---|---|---|
|
string |
adapter 的 kind(见下)。默认取模型的 |
|
float |
流量的相对占比(默认 1.0)。 |
|
list[string] |
这个端点的 key 池,用来替代 |
|
string |
拼在该路由 |
|
bool |
如果 key、 |
|
string |
只覆盖分析统计里的标签——它重命名的是仪表盘里的那一行,不会选择 adapter,选 adapter 的是 |
|
string |
RouteWise 的成本类别: |
|
— |
RouteWise 的资源池与预算元数据 |
一条路由用自己的 kind 作为 provider 标签上报——这个值记录在 api_logs.provider 里,所有按 provider 划分的管理视图都以它分组。因此同一个 kind 的两条路由会共用仪表盘上的同一行;用 provider: 才能把它们分开。
自定义 embeddings 路径
有些 OpenAI 兼容网关的 API 前缀不是 /v1。在对应的路由上设置 embeddings_path,就不会被多拼上一段 /v1:
models:
- id: text-embedding-example
name: Example embeddings
provider: openai_compat
model_type: embedding
route:
- kind: openai_compat
base_url: https://gateway.example/api/v2
api_key: ${EMBEDDING_API_KEY}
embeddings_path: /embeddings
这样请求会发送到 https://gateway.example/api/v2/embeddings。这个覆盖只属于这一条路由,不影响它的回退路由。不设置时,以 /v1 结尾的 base URL 追加 /embeddings,其他 base URL 追加 /v1/embeddings。拼接路径前会先去掉 base URL 末尾的斜杠。
embeddings_path 只能写在 route: 里。写在模型层级会被拒绝,没有路由列表的简写模型也一样。路径前后的空白会被去掉,但纯空白字符串、除 null 以外的非字符串值,以及父级路径段(..)都会被拒绝。显式写 null 或 "" 时,仍按默认规则推导 URL。
整个值写成 ${VAR} 时,它必须解析成非空白的路径;变量缺失并不意味着改用默认值。如果路由不是可选的,变量未设置或为空白都会导致启动失败。设置了 optional: true 时,只跳过这一条路由,并记一条写明模型、路由和变量名的警告。
其他任何非法的 embeddings_path 都会导致启动失败,可选路由也不例外,免得网关带着残缺的注册表启动。
支持的 adapter kind
路由条目里的 kind 字段决定用哪个 adapter。所有标为OpenAI 兼容的 kind 共用同一个 OpenAICompatAdapter 实现,并自动套用各 provider 专属的 profile。
kind |
类别 |
备注 |
|---|---|---|
|
OpenAI 兼容 |
通用的 OpenAI 兼容端点;没有更贴切的 kind 时用它 |
|
OpenAI 兼容 |
|
|
OpenAI 兼容 |
本地 vLLM 推理服务器 |
|
OpenAI 兼容 |
本地 SGLang 推理服务器 |
|
OpenAI 兼容 |
本地或远程的 Ollama 服务器 |
|
OpenAI 兼容 |
Chutes.ai 托管推理 |
|
OpenAI 兼容 |
Featherless.ai 托管推理 |
|
OpenAI 兼容 |
面向 OpenAI 兼容模型的 CLI 代理端点 |
|
OpenAI 兼容 |
DeepSeek API(套用 DeepSeek 用量 profile) |
|
OpenAI 兼容 |
Moonshot/Kimi 位于 |
|
OpenAI 兼容 |
Z.AI 位于 |
|
OpenAI 兼容 |
MiniMax API(套用 MiniMax 用量 profile) |
|
自定义 |
OpenRouter 聚合器。用方括号形式 |
|
自定义 |
Google Gemini API(需要转换消息格式) |
|
自定义 |
经 Google Vertex 访问的 Anthropic Claude |
|
自定义 |
直连 Anthropic Messages API 的客户端 |
其他 kind 需要由后端扩展注册,否则加载注册表时会报 ValueError: Unknown adapter kind。
路由之间的权重
一个模型的各条路由按注册表里写的权重分摊它的流量;管理控制台可以覆盖某个权重,而不必改文件。路由配置文件也可以在本地路由和远程路由之间调整权重,但只在没有数据库的网关上生效;见路由配置文件。每个请求如何选定路由,见路由。
故障排查
模型没有出现在 /v1/models 里
确认后端实际加载的是哪份注册表。它在启动时会记录
Registered N routes from <path>;如果那个路径上没有注册表,则会记录一条写明路径的错误日志。检查
models:下的 YAML 语法和缩进。检查有没有模型被跳过:如果某条路由的 key 或
base_url引用的${VAR}解析为空,整个模型都会被丢弃,日志里会写明模型名和未设置的变量。如果用了
aliases,确认规范的id只出现一次,且别名没有和别的模型撞名。重复的别名会解析到最后加载的那个模型,并记录一条警告。
鉴权失败
网关返回的 401 表示你的网关 API key 缺失或无效,或者鉴权已开启而你没有发送
Authorization头。从路由返回的 401 可能说明上游的 key 不对。不过有些网关对匹配不到的路径也返回 401,所以在换掉一个其实有效的 key 之前,先检查请求 URL 和
embeddings_path。对话接口的路由层把这类失败记为upstream_auth_misconfig;embeddings 接口则记一条Embedding request failed for model=...,并附上上游的状态码和错误信息。确认
${ENV_VAR}展开成功:只有恰好是${NAME}这种形式的取值才会被展开,而且只对base_url、api_key、api_keys和provider_model_id,以及路由级的embeddings_path生效。
另见
添加新的本地模型——注册自建的 vLLM/SGLang/Ollama 服务器
快速开始——从第一个请求到接入本地服务器,一步步搭起一个能跑的部署
通过 OpenRouter 路由——架构与端点
路由——集中式权重覆盖与策略
配置——环境变量与 YAML 配置