From cf14f66ad4803dbde19e33af9690e1d761520ec2 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sat, 3 Oct 2026 21:16:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(gateway):=20AUTO=20=E8=A2=AB=E5=AF=86?= =?UTF-8?q?=E9=92=A5=E6=A8=A1=E5=9E=8B=E8=8C=83=E5=9B=B4=E6=8B=A6=E4=B8=8B?= =?UTF-8?q?=E6=97=B6=E6=8A=A5=E9=94=99=E8=AF=AF=E5=AF=BC=20+=20=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8=E7=BB=99=E5=87=BA=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一个密钥的模型范围不含 AUTO 时,它用不了 AUTO —— 范围过滤发生在链之前。 但这两条路径都不说真话: 1. 请求侧:AUTO 走的是通用分支,返回 model_not_found "model \"AUTO\" is not configured",读起来像 AUTO 没配置。非 AUTO 模型早就有 model_not_allowed 的专门提示,AUTO 漏了。补上,并说明修法(把 AUTO 加进该密钥的 models, 或去掉范围限制)。 2. 编辑器侧:per-key AUTO 链编辑器可以正常配链、保存也成功,看起来一切正常, 但该密钥的每个请求都会 403。管理员无从得知。现在弹窗顶部在检测到冲突时 显示警告,并列出当前范围。 刻意不做的事:不自动把 AUTO 加进该密钥的模型范围。那等于悄悄授予运营 没要求的访问权,比一个显眼的警告更糟。 判据 1 条,除确认提示出现外还断言该函数体内没有 PUT/POST/fetch/api —— 它只报告,不得写回。变异(去掉 AUTO 判断)判红。 CDP 实测四种场景:范围含 AUTO → 无提示;范围不含 → 警告并列出范围; 空范围(不受限)→ 无提示;范围含 AUTO → 无提示。 Co-Authored-By: ModelRouter --- internal/gateway/chat.go | 13 ++++++++ internal/gateway/ui/index.html | 49 ++++++++++++++++++++++++++-- internal/gateway/ui_key_auto_test.go | 33 +++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/internal/gateway/chat.go b/internal/gateway/chat.go index 2e3af12..5a70914 100644 --- a/internal/gateway/chat.go +++ b/internal/gateway/chat.go @@ -452,6 +452,19 @@ func (g *Gateway) handleChat(w http.ResponseWriter, r *http.Request) { return } } + // A key whose model scope does not include AUTO cannot use AUTO at all, + // even when it has its own AUTO chain configured. That combination is + // easy to set up by accident and produced a misleading error: the request + // fell through to the generic "model %q is not configured" below, which + // claims AUTO does not exist — it does, this key just may not use it. Name + // the actual reason so the admin can fix the scope. + if isAuto(model) { + if allow := g.allowedModels(r.Context()); allow != nil && !g.hasScopeModel(allow, model) { + writeError(w, http.StatusForbidden, "model_not_allowed", + fmt.Sprintf("model %q is not in this key's model scope, so it cannot use AUTO; add %q to the key's models or remove the scope restriction", model, model)) + return + } + } cands, effective := g.resolveCands(r.Context(), &req) if len(cands) == 0 { writeError(w, http.StatusNotFound, "model_not_found", fmt.Sprintf("model %q is not configured", model)) diff --git a/internal/gateway/ui/index.html b/internal/gateway/ui/index.html index 42328f1..f35a998 100644 --- a/internal/gateway/ui/index.html +++ b/internal/gateway/ui/index.html @@ -517,7 +517,20 @@ #toast{left:12px;right:12px;bottom:12px;text-align:center} th,td{padding:8px 10px} } - + + /* Shown when a key's model scope blocks AUTO: the chain editor can look + fine while every request 403s, so the conflict must be visible here. */ + .warn-box { + margin: 0 0 10px; + padding: 8px 10px; + border-radius: 8px; + border: 1px solid rgba(224, 108, 51, 0.45); + background: rgba(224, 108, 51, 0.1); + color: #b45309; + font-size: 12.5px; + line-height: 1.5; + } +