Repository navigation
fix(gateway): 关闭跨 Provider 模型池时,模型列表返回带前缀的 ID / prefix model ids in list endpoints while the pool is off - #476
Merged
caigee-cmd merged 1 commit intoNov 8, 2026
Conversation
…ider pool is off
## 问题现象
关闭「跨 Provider 模型池」后,控制台「接入与调试」页面无法使用:模型选择器从
/api/models 拿到的仍是裸 ID(如 glm-5.2),选中发送后网关直接拒绝:
provider_prefix_required: cross-provider model pool is disabled; use a
provider-prefixed model ID such as qoder/glm-5.2
/v1/models 的消费方(OpenAI 兼容客户端)同样受影响。列表所见与网关所收不一致,
关闭开关等于让所有按列表选择的请求必然 400。
## 根因
两处行为未对齐:
- 列表侧(catalog_source.go ProviderModelEntry):条目 id 始终是裸 ID,
provider 仅作为 owned_by/provider 元数据字段
- 校验侧(executor/prepare.go RejectsBareModel):CrossProviderPool=false 时
ProviderPrefix(model)=="" 直接 400
ResolveProviderFilter 关闭时的裸 ID 兜底是 provider=qoder,但这发生在
RejectsBareModel 之后,兜底永远没有机会执行。
## 修复
新增 control.PrefixModelEntries:按条目的 provider/owned_by 与 regions 把裸 ID
改写为网关接受的前缀形态(workbuddy 单一 cn 区域 -> workbuddy-cn/…,多区域不带
区域钉 -> trae/…)。两个出口在开关关闭时套用:
- console /api/models(HandleModelsAPI)
- gateway /v1/models(HandleModels)
开关开启时列表保持裸 ID 不变(此时网关本来就接受)。改写发生在过滤/装饰之后,
且对条目做克隆,不污染共享 catalog 缓存。
## 测试
新增 TestPrefixModelEntriesAddsProviderPrefix(含已带前缀透传、单一区域 dash
形式、多区域裸前缀、qoder 视图、源条目不可变)与空列表/nil 用例。
go build ./... / go vet / go test ./... 全部通过。
BrianBoyCN
force-pushed
the
fix/models-prefix-when-pool-off
branch
from
November 8, 2026 04:24
4ac8a84 to
e802cb2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
中文
问题现象
关闭「跨 Provider 模型池」后,控制台「接入与调试」页面无法使用:模型选择器从
/api/models拿到的仍是裸 ID(如glm-5.2),选中发送后网关直接拒绝:/v1/models的消费方(OpenAI 兼容客户端)同样受影响。列表所见与网关所收不一致——关闭开关后,所有按列表选择的请求必然 400,包括控制台自己的试聊。根因
两处行为未对齐:
catalog_source.goProviderModelEntry):条目id始终是裸 ID,provider仅作为owned_by/provider元数据字段executor/prepare.goRejectsBareModel):CrossProviderPool=false时ProviderPrefix(model)==""直接 400ResolveProviderFilter关闭时对裸 ID 的兜底是provider=qoder,但该校验发生在兜底之后,兜底永远没有机会执行——裸请求在校验处就已死。修复
新增
control.PrefixModelEntries:按条目的provider/owned_by与regions把裸 ID 改写为网关接受的前缀形态:glm-5.2qoder/glm-5.2workbuddy-cn/glm-5.3-flash(区域钉)trae/deepseek-v4.1-flash(不带区域钉,池自选)/前缀两个列表出口在开关关闭时套用:
/api/models(HandleModelsAPI)/v1/models(HandleModels)开关开启时列表保持裸 ID 不变(此时网关本来就接受)。改写发生在过滤/装饰之后,且对条目做克隆——不会污染共享 catalog 缓存。前端无需改动。
测试
新增
TestPrefixModelEntriesAddsProviderPrefix(前缀改写、单一区域 dash 形式、多区域裸前缀、已带前缀透传、qoder 视图、源条目不可变)与TestPrefixModelEntriesEmptyAndNil。go build ./...、go vet、go test ./...全部通过;已在真实部署上验证:关闭开关后接入与调试页面恢复可用。English
Problem
With the cross-provider model pool disabled, the console access page breaks: the model picker pulls bare ids (
glm-5.2) from/api/models, and every request built from them is refused by the gateway:/v1/modelsconsumers (OpenAI-compatible clients) hit the same wall. The list shows what the gateway refuses — with the pool off, every list-driven request is a guaranteed 400, including the console's own test chat.Root cause
Two sides disagree:
catalog_source.goProviderModelEntry): entries expose bare ids asid; the provider rides only asowned_by/providermetadataexecutor/prepare.goRejectsBareModel): withCrossProviderPool=false, a missing provider prefix is an immediate 400ResolveProviderFilter's bare-id fallback (provider=qoder) can never run — the gate fires first.Fix
New
control.PrefixModelEntriesrewrites bare ids into the prefixed shapes the gateway accepts, driven by each entry'sprovider/owned_byandregions:glm-5.2→qoder/glm-5.2workbuddy-cn/glm-5.3-flash(region pin)trae/deepseek-v4.1-flash(no region pin, pool picks)/pass through untouchedApplied at both list endpoints while the pool is off — console
/api/models(HandleModelsAPI) and gateway/v1/models(HandleModels). With the pool on, lists stay bare (the gateway accepts them). Rewrites happen after filtering/decoration and operate on cloned entries, so the shared catalog cache is never mutated. No frontend changes needed.Tests
New
TestPrefixModelEntriesAddsProviderPrefix(rewrites, single-region dash form, multi-region bare prefix, pre-prefixed passthrough, qoder view, source immutability) andTestPrefixModelEntriesEmptyAndNil.go build ./...,go vet,go test ./...all pass; verified on a live deployment — the access page works again with the pool disabled.