配置
本页说明运行中的网关怎样找到自己的配置:读哪些文件,这些文件放在哪里,以及多个层级都给出了同一个配置文件的路径时,以哪一层为准。本页讲概念;模型注册表的逐字段参考见添加新模型,路由引擎怎样使用这些配置见路由。
发行版 manifest、品牌、UI 模块和后端扩展见发行版定制。
网关启动时读什么
类型 |
存放的内容 |
|---|---|
|
模型注册表:网关对外提供的每一个模型 id,以及它背后的上游路由 |
|
可选的部署级设置:健康探测,以及本地/远程之间的权重划分 |
|
告警规则与阈值 |
环境变量 |
所有敏感信息和与主机相关的设置:凭据、数据库连接、功能开关 |
要承接流量,必需的只有模型注册表。没有它,网关照样能启动、提供 /health,只是 GET /v1/models 会返回空列表。没有路由配置文件,每条路由保持注册表给它的权重;没有告警配置文件,则使用内置阈值。
有了数据库之后,配置就不只来自文件了。管理控制台会把 provider、key、路由、权重和按模型的覆盖项写进运行数据存储,网关每次启动时都会在加载好的注册表之上重新应用这些状态。这一层见从管理控制台做运行时配置。
配置放在哪里
config/examples/ 里是网关的参考配置,包括 models.openrouter.yaml 和 routing.minimal.yaml。每个部署把自己的模型注册表、路由规则和告警放在自己的配置目录里,例如 distributions/<name>/config/,再通过显式的环境变量或已启用的发行版 manifest 选用这些文件。manifest 和 overlay 的目录结构见发行版定制。
网关运行期间,也可以在管理控制台里添加 provider、key、路由乃至整个模型。这些状态保存在 Postgres 里,详见下文。
网关如何找到自己的配置
优先级依次是:环境变量、manifest、内置默认值。
每类文件都单独查找,顺序如下:
显式设置的环境变量。
MODELS_CONFIG_PATH、ROUTING_CONFIG_PATH、ALERTS_CONFIG_PATH。(旧名MODELS_CONFIG和ROUTING_CONFIG仍然可用;两者都设置时,以规范名*_CONFIG_PATH为准。)发行版 manifest 的
paths:部分——仅在DISTRIBUTION_CONFIG_MODE=active时生效。默认情况下只检查 manifest,不应用它;见启用 manifest。内置默认值,指向参考示例:
config/examples/models.openrouter.yaml和config/examples/routing.minimal.yaml。alerts的默认值是config/alerts.yaml,本仓库并不带这个文件——缺少告警配置文件就表示「使用内置阈值」。
这意味着两件事:
环境变量优先于 manifest。如果部署已经有 manifest,你又设置了
MODELS_CONFIG_PATH,manifest 里的models:路径就会被忽略,网关会记一条explicit env override ... wins over manifest value ...日志。两种方式选一种就好。来自环境变量的路径,相对于进程的工作目录解析;manifest 里的相对路径,则相对于 manifest 文件自己所在的目录解析。
如果第 1 层或第 2 层给出的路径实际不存在,网关只会发出警告,不会启动失败:它记录 Models config not found: <path> (source=env),然后在一个模型都没注册的状态下继续运行。
全新克隆、完全没有配置时会怎样
在一个干净的仓库里启动网关,不设 MODELS_CONFIG_PATH,也没有 manifest 和 overlay,这时用的是内置默认值:网关加载 config/examples/models.openrouter.yaml。这个文件注册了三个模型:两个直接走 OpenRouter,另一个优先用本地的 OpenAI 兼容服务器,失败时自动回退到 OpenRouter。三个模型都只需要同一个凭据。
export OPENROUTER_API_KEY=sk-or-...
uv run uvicorn serving.servers.app:app --no-proxy-headers --port 8080
不设这个变量,文件里的每个模型都会被跳过(每条路由唯一的 API key 展开后是空的),网关在开始服务之前就会给出提示:
No models are available: every model in config/examples/models.openrouter.yaml was
skipped because its credential is unset. Set OPENROUTER_API_KEY and restart.
/v1/models will stay empty until then, and requests will report the model as not found.
这是让网关跑上真实流量的最快路径。等你想清楚要提供哪些模型,再用自己的注册表替换这个文件。
模型注册表
字段参考见添加新模型。有三件事放在本页讲,因为它们属于配置加载的行为,而不是某个字段的含义:
路由的 kind: 决定用哪个 adapter;模型的 provider: 是它的默认值。路由省略 kind: 时,使用模型顶层的 provider:;模型完全省略 route: 时,会用顶层的 provider:、base_url 和 api_key 构造出一条路由。provider 同时也是写入 api_logs.provider、并在指标里展示的标签,所以一条路由可以用 provider: 单独覆盖它。
内置的 kind 如下。部署可以通过后端扩展添加更多 kind,查找时会先匹配扩展添加的 kind。
类型 |
adapter |
|---|---|
|
|
|
|
|
|
|
|
|
|
本地推理服务器没有专用 adapter:vllm、sglang 和 ollama 都是 OpenAI 兼容的 kind,区别只在 provider 标签和用量处理上。其他任何 kind 都会让注册表加载失败,报错 ValueError: Unknown adapter kind: <kind>。
注册表里的环境变量插值只支持整值替换。在 models.yaml 里,只有 base_url、api_key、api_keys、provider_model_id 和路由的 embeddings_path 会被展开,而且只在整个值恰好是 ${VAR} 时才展开。不支持 ${VAR:-default},也不支持嵌在字符串中间的替换:
base_url: ${LOCAL_BASE_URL} # expanded
base_url: ${LOCAL_BASE_URL:-http://x/v1} # NOT expanded — treated as a var
# named "LOCAL_BASE_URL:-http://x/v1",
# resolves empty, model is skipped
base_url: http://${LOCAL_HOST}/v1 # NOT expanded — the literal string,
# including "${LOCAL_HOST}", is used
路由配置文件和告警配置文件的展开规则更宽松(见下文),所以别把一种文件里的写法习惯带到另一种文件里。
路由配置文件
routing.yaml 是可选的,大多数部署都可以不要它。依赖它之前先读完这一节:在带数据库的网关上(标准 Docker 服务栈就是这种),它的权重划分不起作用,它的健康探测也从不改变路由。流量去哪里,由模型注册表里的权重、在管理控制台里设置的权重覆盖,以及路由里介绍的 router 共同决定。
所有键及其默认值:
键 |
默认值 |
含义 |
|---|---|---|
|
|
没有用 |
|
|
一次健康探测等待连接的秒数。 |
|
|
两次健康探测之间的秒数; |
|
|
视为本地的端点。 |
|
|
视为远程的端点。 |
|
|
自由格式的映射。 |
每个 deployment 条目都必须同时有 endpoint:(必须以 http:// 或 https:// 开头)和非空的 models: 列表;models: 为空的条目会让整个文件校验失败,网关会记录 RoutingManager failed to initialize,然后继续使用注册表自己的权重。而 endpoint: 展开为空的条目只会被单独丢弃,同时发出一条警告,列出因此失去归属的模型。这样,某个变量没设置也不会导致其余端点全部失效。
default_router: fixed
timeout: 2
health_check: 30
local_deployment:
- endpoint: ${LOCAL_BASE_URL:-http://localhost:8000}
models: [<model-id>]
remote_deployment:
- endpoint: https://api.your-provider.example/v1
models: [<model-id>]
权重划分只对不带数据库的网关生效。启动时,网关把每个模型的路由分成本地和远程两组:只有路由的 base_url 与某个条目的 endpoint: 完全一致,并且模型也列在这个条目的 models: 里,这条路由才归入对应的组。然后网关给两组各分一半流量(除非已废弃的 routing_parameter.local_fraction 设置了别的比例),在组内平均分摊,再把每个模型的权重缩放到总和为 1.0。如果某个模型有一组为空,全部流量归另一组。什么都匹配不上的模型,保留它 route: 列表里的权重。在带数据库的网关上,路由读的是注册表里的权重加上管理员设置的覆盖值,根本不会用到这次划分。
健康探测只针对 local_deployment 里的端点:远程 provider 没有网关要探测的 /health 路径,探测的话会被误判为不健康。每次探测都是向端点 origin 的根路径加 /health 发一个 GET。
探测只报告结果,不会改变路由。即使某个端点每次探测都失败,它也保有原来的权重,照样接收流量。每次状态变化都会记一条 WARNING 日志,GET /routing 在 manager_status.endpoint_health 下展示最新结果,旁边是 endpoint_health_enforced: false。真正把故障端点暂时移出的是熔断器。
和模型注册表不同,这个文件的变量展开支持 ${VAR}、${VAR:-default} 和嵌在长字符串里的变量,而且在任意嵌套层级都有效。
routing_strategy: 和 routing_parameter: 分别是 default_router: 和按模型配置的 router_params: 的旧写法,已废弃。它们仍然能加载,但会记录一条废弃警告。按模型选择 router(模型注册表里的 router: / router_params:)会覆盖 default_router,详见路由。
目前不生效的设置
网关加载配置时接受这些设置,设置了也不会导致启动失败,但目前它们不起任何作用;其中路由配置文件的权重划分,是在有数据库的网关上不起作用。
设置项 |
位置 |
实际效果 |
|---|---|---|
|
路由配置文件 |
只在不带数据库的网关上生效;见路由配置文件 |
|
路由配置文件 |
记入日志,并在 |
|
模型注册表, |
接受并校验,但不会读取 |
|
发行版 manifest |
会出现在 |
|
发行版 manifest |
接受,但没有任何组件读取它 |
|
发行版 manifest |
接受,但不会加载到条款页;请改用 Site UI 模块 |
|
发行版 manifest |
只是个标签;不会选择主机,也不会构建任何东西 |
从管理控制台做运行时配置
上面这些文件只在启动时读一次。运维人员对路由的其他改动,都经由管理控制台(或者它背后的 /admin/* 端点)写进运行数据存储,所以不用重启就能生效,重启后也不会丢。如果某个模型、provider 或 key 需要马上在运行中的网关上可用,又不想改 overlay、重新部署,就用这一层。
这一层需要数据库。DB_ENABLED 默认为 true(连接配置见数据库);设成 DB_ENABLED=false 就没有运行数据存储,下面所有端点都返回 500 Database not configured,调用这些端点的控制台标签页也就无处可写。所有端点都要求在 Authorization: Bearer ... 头里带上管理员的 JWT 或 ADMIN_TOKEN。
Providers 标签页
做什么 |
控制台位置 |
端点 |
存在哪张表 |
|---|---|---|---|
添加自定义的 OpenAI 兼容 provider,同时添加它的第一个 key |
Overview → Add provider |
|
|
编辑或删除自定义 provider |
Overview → Edit provider |
|
|
添加 provider API key |
Keys → Add a new key |
|
|
禁用、重新启用或删除 key |
Keys |
|
|
把 key 预留给某个层级 |
Keys → Reserved for |
|
|
暂时停用某个 provider |
Availability → Enabled |
|
|
Overview 标签页上的注册表列出了网关能路由到的所有 provider,但只有自定义 provider 能在这里编辑。已经由代码或模型注册表定义的 provider(内置的 adapter kind,以及路由里声明的所有 provider: 标签)在这张表里是只读的;如果数据库里存的定义用了其中某个 slug,启动时会跳过这条定义,不让它顶替原有的 provider。自定义 provider 只能是 openai_compat;通用 adapter 处理不了的协议需要专门的 adapter,这就要改代码了(见编写 provider adapter)。
在这里添加的 key 和注册表里用 ${VAR} 引用的 key 进入同一个池子,一起轮换使用。来自环境变量的 key 在数据库里没有对应的行,所以控制台可以禁用它、把它预留给某个层级,但不能删除;要删除,就去掉这个环境变量再重启。
Routing 标签页
做什么 |
控制台位置 |
端点 |
存在哪张表 |
|---|---|---|---|
创建注册表里没有的模型 |
Create model |
|
|
给已有模型加一条路由 |
Add provider route |
|
|
把注册表里的一条路由指向别处 |
编辑路由的 Target |
|
|
改一条路由的权重 |
每条路由上的权重字段 |
|
|
在 |
策略选择器 |
|
|
为在出站队列中等待过久、或迟迟等不到引擎首个 token 的请求预留一条路由 |
Queue offload |
|
|
为单个模型调整 RouteWise 参数 |
RouteWise 设置 |
|
|
GET /admin/routing/provider-routes(或 .../{model_id})返回网关当前在用的每一条路由,每条都标着 source: yaml、override 或 runtime;想看两层叠加后的结果,这是最快的办法。
这个标签页上的 provider 选择器会列出所有自定义 provider,以及注册表、当前路由表或已配置凭据中已经出现的内置 kind。provider 能以哪种路由类型添加(on_demand、quota 或 concurrency),取决于部署和这家供应商的约定,用 PROVIDER_ROUTE_TYPES 声明(见环境变量);没有列出的 provider 三种都可以用。
在这里创建的模型是运行时模型:它只存在于数据库里,包含 id、第一条路由、定价表、router 策略和 required_role(默认 admin,所以在你调低之前,普通用户看不到这个新模型),每次启动时都会恢复。它没有注册表条目里声明的目录元数据(上下文长度、模态、支持的参数、别名),这些都取 ModelConfig 的默认值:context_length 8192,max_output_length 4096,只支持文本输入输出,不支持工具调用。如果模型需要其中任何一项,就把它写进注册表。
Settings 标签页
做什么 |
控制台位置 |
端点 |
存在哪张表 |
|---|---|---|---|
修改模型对谁可见 |
Model Visibility |
|
|
让某个模型不受每用户并发上限的限制 |
Model Concurrency Limit |
|
|
两层如何叠加
启动时先加载注册表,再按下面的顺序把已存状态叠加上去;管理员的每次改动在保存时也会立即应用到运行中的 router 上。
自定义 provider 定义;代码或注册表里已有的 slug 会被跳过。
已存的 provider key,加入各个 provider 的 key 池,与环境变量里的 key 并列。
按模型设置的 router 策略覆盖项。
运行时路由。运行时模型只有在创建过程完整结束后才会被恢复;如果一条已存路由对应的模型已经不在注册表里,它会被跳过并记录警告,所以从 YAML 里删掉一个模型,不会因为一行残留数据又把它带回来。
路由覆盖项,把对应的注册表路由改指到新的目标。
key 的层级预留:此时所有 provider 都已加载,所以再读一遍。然后是权重覆盖项、已禁用的 provider、卸载路由和模型可见性,这些会加载到解析器里,供 router 在处理请求时查询。
由这个顺序可以得出两条规则。第一,已存的改动从不修改它覆盖的文件:删掉覆盖项,注册表里的路由、权重或 router: 值就恢复了;运行时添加的候选路由和注册表的路由并存,而不是取代它们。第二,存储里没有记录的内容仍以文件为准,所以要改目录元数据、别名或新增 adapter kind,还是得改注册表再重启。
环境变量
网关会读取工作目录下的 .env 文件和进程环境变量,不区分大小写;两者冲突时以进程环境变量为准。仓库根目录下的 .env.example 是带注释的变量清单——把它复制成 .env 再改。安装列出了你最可能要设置的变量。密钥只能放在这里:不要放进模型注册表(在那里用 ${VAR} 引用),也不要放进 manifest。
用你自己的配置运行
uv run uvicorn serving.servers.app:app --no-proxy-headers --port 8080
然后检查实际加载了什么:
curl -s localhost:8080/health # includes routes_configured
curl -s localhost:8080/v1/models # generated from the registered adapters
启动日志会告诉你加载了什么。Registered N routes from <path> 指出实际使用的是哪个注册表文件;[distribution dark mode] ... 这几行显示 manifest 启用后会改变什么;Skipping model '<id>' ... after env expansion 逐个列出凭据没有设置的模型。
警告
GET /routing 不需要任何凭据,会返回每条已发布路由的上游 base_url、provider 标签和权重——也就是你完整的上游拓扑,包括 base URL 里的主机和端口。如果网关能从公网访问,而你又不打算公开这份拓扑,就在反向代理上屏蔽 /routing。GET /admin/routing 还会返回未发布的路由,需要在 Authorization: Bearer ... 头里带上管理员的 JWT 或 ADMIN_TOKEN。两个接口的输出都要当作敏感信息。