模型与 Upstream 解析
总体链路
一条请求命中 route 后,当前主链路按下面顺序执行:
- 按路径匹配 active route
- 从请求 JSON 中提取
model - 计算 route / consumer / api key 的分组交集
- 基于
upstream_models和分组约束解析候选 upstream - 过滤不支持当前子路径协议的候选
- 记录当前请求的全部
AvailableUpstreams - 对候选做加权随机无放回排序,最多保留 5 个
- 普通请求按候选顺序转发;
GET /v1/models则基于AvailableUpstreams本地构建 models 响应
1. 可用模型授权
当前“可用模型授权”的实际含义,是请求里的 model 如何参与运行时决策,以及它如何映射成最终发给上游的模型名。
1.1 请求模型如何参与解析
当前语义:
- gateway 会从请求 JSON 中提取
model - 当请求里带了
model时,运行时会要求它在upstream_models.model中命中 - 当请求里没有
model时,运行时会退回到“按分组找首批可用 upstream”
因此,这一层回答的是:
当前这个请求里的模型名,是否能解析出一组明确的 upstream 候选?
1.2 模型名映射
upstream_models 当前同时承担两件事:
model:客户端请求时使用的模型名upstream_model:真正发给上游的模型名
如果命中的是 alias 行,当前实现会把请求里的模型名改写成对应的 upstream_model。
当前改写路径有两条:
- chat 路径会把
RequestIR.Model.UpstreamModel设为候选的ResolvedModel - raw JSON 路径会在出站前把顶层
model字段重写成ResolvedModel
因此,当前已经不是“只用模型名选 upstream”,而是已经把模型映射接进了实际出站请求。
1.3 /v1/models 的当前行为
GET /v1/models 当前不是代理上游结果,而是由 gateway 本地构建。
构建方式:
- 先拿到这次请求解析出的
AvailableUpstreams - 提取去重后的 upstream IDs
- 查询这些 upstream 对应的
upstream_models.model - 去重、按字母序排序
- 返回 OpenAI 兼容的 models list
因此,/v1/models 返回的是:
当前这次请求在当前分组和 upstream 可用性约束下,可见的客户端模型名集合。
它返回的是 upstream_models.model,不是 upstream_models.upstream_model。
1.4 日志里的模型字段
当前日志会区分两层模型名:
requested_model:客户端原始请求中的模型名upstream_requests[].request.model:真正发给上游的模型名
因此,当 alias 被改写时,日志里可以同时看到:
- 用户请求的是哪个别名
- 实际打到上游的是哪个真实模型名
2. 可用 Upstream 选择
当前“可用 upstream”判断的目标,是为本次请求选出一组真实可用的候选,并从中决定最终尝试顺序。
2.1 分组约束
候选 upstream 解析前,运行时会先取下面几组约束的交集:
routes.groupsconsumers.groupsconsumer_api_keys.groups
然后用交集结果去匹配 upstreams.group。
当前语义:
- 任意一侧空数组都表示“不限制”
- 如果最终完全没有显式约束,运行时默认只看
default组 - 如果交集为空,直接视为没有可用 upstream
2.2 按模型解析候选
如果请求里带了 model,运行时会在 upstream_models 中查:
upstream_models.model = requested modelupstreams.group命中允许组upstreams.disabled_at is nullproviders.disabled_at is null
同时会把 upstream 的 API key 展开成独立候选,并过滤掉:
- 没有
key的无效项 - 已禁用 key
- 已过期 key
如果某个 upstream 没有配置任何 API key,当前实现会给它补一个“空 token 候选”,仍允许参与选择。
当前这里没有“模型未命中时退回任意 upstream”的额外回退。
2.3 没有请求模型时的选择
如果请求里没有 model,运行时不会查 upstream_models,而是直接在允许组里查首批 active upstream。
这条路径主要服务于:
- 不带模型的请求
- 需要先拿到 upstream 再由上游决定细节的请求
2.4 协议过滤
运行时会根据请求子路径过滤候选协议:
/chat/completions、/responses、/messages允许:chat-completionsopen-responsesclaude-messages
- 其它路径当前只允许:
chat-completionsopen-responses
因此,数据库里“按模型查得到”不等于“当前请求路径下真的可转发”。
2.5 候选记录、排序与 fallback
过滤后的候选会先完整保存到 state.AvailableUpstreams,供:
/v1/models本地构建使用- 运行时上下文和日志使用
然后运行时会:
- 按
upstream_api_key.lb_weight优先,回退到upstream.lb_weight - 做加权随机无放回排序
- 最多保留前 5 个候选进入真正的转发计划
真正发请求时,gateway 会按排序后的候选顺序逐个尝试:
- raw HTTP 路径上,首个
2xx响应成功即停止 - chat 路径上,transport error、非
2xx、assemble/parse error 都会触发 fallback - 流式 chat 一旦已经向客户端成功写出首个 event,就锁定当前候选,不再切换
所以“最终可用 upstream”指的是:
在分组、模型映射、协议兼容、权重排序和运行时错误之后,当前请求最终实际命中的那个候选。