fix(core): keep active runtime model in default getAllConfiguredModels listing#5729
Conversation
…s listing QwenLM#5089 switched getAllConfiguredModels from Object.values(AuthType) to modelRegistry.getAuthTypes(), which only returns authTypes that have registry models. A runtime model resolved from env/CLI overrides (e.g. OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL with no modelProviders entry) uses an authType that has no registry models, so it was dropped from the default listing — even when it is the active/current model. This hid the active model from the ACP `availableModels` list and the interactive `/model` picker, and deterministically failed the ACP `set_config_option` integration test (`expect(openaiModel).toBeDefined()`) on every main run since QwenLM#5089. Include the active runtime model's authType in the default (no-filter) case so it is enumerated alongside registry models. Explicit-authType callers are unchanged. Add a core unit regression test.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Hi @wenshao — thanks for the detailed write-up! The technical content here is excellent and the root-cause analysis is thorough.
Unfortunately, the PR body doesn't follow our PR template. Several required headings are missing:
- Why it's needed — your "Root cause" section covers this, but needs the template heading
- Reviewer Test Plan — with sub-sections: "How to verify", "Evidence (Before & After)", "Tested on" table
- Linked Issues — e.g.
Fixes #Nor references to #5721, #5724, #5089, #5728 - 中文说明 — should be wrapped in a
<details>tag per the template
Could you reformat the PR body to match the template? The content is all there — it just needs to be organized under the right headings so reviewers can find things quickly.
中文说明
@wenshao 你好——PR 的技术内容写得很好,根因分析也很到位。
不过 PR 正文没有按照我们的 PR 模板 来写,缺少几个必填的标题:
- Why it's needed — 你的"根因"部分已经覆盖了,但需要用模板里的标题
- Reviewer Test Plan — 需要包含"How to verify"、"Evidence (Before & After)"、"Tested on"表格等子标题
- Linked Issues — 例如
Fixes #N或引用 #5721、#5724、#5089、#5728 - 中文说明 — 需要按模板用
<details>标签包裹
麻烦按照模板重新整理一下正文,内容都有了,只是需要放到对应的标题下面。
— Qwen Code · qwen3.7-max
|
@qwen-code /triage Thanks for the catch — I've reformatted the body to follow the PR template: added Why it's needed, Reviewer Test Plan (How to verify / Evidence Before & After / Tested on table / Environment), Risk & Scope, and Linked Issues (#5089, #5721, #5724, #5728), with the full 中文说明 wrapped in a 已按模板重排正文:补齐了 Why it's needed、Reviewer Test Plan(How to verify / Evidence / Tested on / Environment)、Risk & Scope、Linked Issues 等标题,中文说明也折叠进 |
|
@qwen-code /triage |
|
Thanks for the PR, @wenshao! Template looks good ✓ — all required sections present and well-filled. On direction: this is a clear regression fix. #5089 narrowed the default auth type iteration in On approach: the fix is minimal and surgical — 2 files, +87/−7. It moves One observation (not a blocker): the CI is all green (Test × 3 platforms, Lint, CodeQL, Integration Tests). Moving on to code review. 🔍 中文说明感谢贡献,@wenshao! 模板完整 ✓ — 所有必要章节都有且填写充分。 方向:这是一个明确的回归修复。#5089 把 方案:修复极简且精准——2 个文件,+87/−7。把 一个小观察(不阻塞): CI 全绿(三平台测试、Lint、CodeQL、集成测试)。进入代码审查 🔍 — Qwen Code · qwen3.7-max |
doudouOUC
left a comment
There was a problem hiding this comment.
Reviewed the diff and relevant model-listing paths. Verified with build during clean install, targeted modelsConfig unit tests, diff check, and typecheck. No blocking issues found.
|
@qwen-code /triage |
wenshao
left a comment
There was a problem hiding this comment.
No review findings. Downgraded from Approve to Comment: self-PR; CI still running.
Reviewed the runtime-model listing change and the explicit authType filter behavior. Local verification passed: npx vitest run src/models/modelsConfig.test.ts, qwen review deterministic, npm run build, and npm run typecheck after build.
— GPT-5 Codex via Qwen Code /review
yiliang114
left a comment
There was a problem hiding this comment.
No review findings. LGTM!
Code ReviewIndependent proposal: I would fix this by, in the default branch of Comparison with the diff: The PR's approach matches my proposal exactly. It introduces No critical blockers. No AGENTS.md violations. The change is 13 net production lines doing exactly one thing — no over-abstraction, no duplication, no code in the wrong package. TestingRan the PR's new regression test against both Before (main branch — bug present)After (this PR — bug fixed)Full test suite (worktree, PR branch)All 75 tests in CIAll green: Test (macOS, Ubuntu, Windows), Lint, CodeQL, Integration Tests (No-AK Smoke). 中文说明代码审查独立方案: 我会在 与 diff 对比: PR 的方案与我的独立方案完全一致。引入 无关键阻塞问题,无 AGENTS.md 违规。生产代码净增 13 行,只做一件事——无过度抽象、无重复、无错位代码。 测试在 tmux 里把 PR 的新回归测试分别对着 修复前: CI 全绿:三平台测试、Lint、CodeQL、集成测试。 — Qwen Code · qwen3.7-max |
|
This is a clean regression fix that does exactly what it says and nothing more. The bug is real and deterministic: #5089 narrowed The fix is the simplest possible approach — 13 production lines that add the runtime model's authType to the iteration set in the default path only. Explicit-filter callers are untouched. The The test design is solid: one test captures the bug (and fails with the exact CI error on LGTM. Approving. ✅ 中文说明这是一个干净且极简的回归修复。 Bug 真实且确定性复现:#5089 把 修复方案是最简路径——13 行生产代码,仅在默认路径里把 runtime 模型的 authType 加入遍历集合。显式过滤的调用方不受影响。 测试设计合理:一个用例复现 bug(在 可以合入。✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Fix is correct and minimal — the runtime model's authType is properly injected into uniqueAuthTypes before the qwen-oauth reorder, seen is kept in sync, and the !hasExplicitAuthTypes guard preserves the explicit-filter contract. Two new tests cover the primary regression and exclusion guard well. One minor code quality note inline.
| : this.modelRegistry.getAuthTypes(); | ||
| const hasExplicitAuthTypes = !!authTypes && authTypes.length > 0; | ||
| const inputAuthTypes = hasExplicitAuthTypes | ||
| ? authTypes! |
There was a problem hiding this comment.
[Suggestion] The authTypes! non-null assertion loses the type narrowing that the original ternary provided. The existing pattern authTypes && authTypes.length > 0 ? authTypes : ... lets TypeScript narrow authTypes from AuthType[] | undefined to AuthType[] without an escape hatch. Splitting into a separate hasExplicitAuthTypes boolean loses that narrowing, forcing ! to compensate.
| ? authTypes! | |
| const inputAuthTypes = hasExplicitAuthTypes | |
| ? authTypes | |
| : this.modelRegistry.getAuthTypes(); |
If the hasExplicitAuthTypes name is desired for the guard below, it can still be extracted without sacrificing the ternary's narrowing — just don't split the assignment into a separate statement.
— qwen3.7-max via Qwen Code /review
…M#5089) Reverts the structural changes from QwenLM#5089 back to the pre-QwenLM#5089 shape: AuthType stays a fixed enum (not `string`), the Protocol enum is removed, modelProviders is `Record<authType, ModelConfig[]>` again (not `{ protocol, models }`), and createContentGenerator dispatches on authType. The v4->v5 settings migration is removed and SETTINGS_VERSION reverts to 4. Features merged on top of QwenLM#5089 are kept and re-adapted to the old enum+array structure (not reverted): - QwenLM#5632 fastOnly/voiceOnly model flags (test fixtures reshaped to arrays) - QwenLM#5638 workspace provider defaults (readProviderModels already tolerates both shapes; test fixtures reshaped to arrays) - QwenLM#5729 active-runtime-model listing (pre-QwenLM#5089 getAllConfiguredModels already enumerates Object.values(AuthType), so the runtime model is included natively) - QwenLM#5728 ACP set_config_option deterministic provider fixture (reshaped to array; the flake fix is preserved) KNOWN DOWNGRADE CAVEAT: settings already migrated to $version:5 (shipped in v0.19.0) retain the v5 `{ protocol, models }` modelProviders shape, which the reverted ModelRegistry consumes as an array. Such settings will throw on load until re-configured. A v5->v4 downgrade guard/migration is a separate follow-up if backward compatibility for migrated users is needed.
#5745) * revert(core): revert Protocol enum & model-identity decoupling (#5089) Reverts the structural changes from #5089 back to the pre-#5089 shape: AuthType stays a fixed enum (not `string`), the Protocol enum is removed, modelProviders is `Record<authType, ModelConfig[]>` again (not `{ protocol, models }`), and createContentGenerator dispatches on authType. The v4->v5 settings migration is removed and SETTINGS_VERSION reverts to 4. Features merged on top of #5089 are kept and re-adapted to the old enum+array structure (not reverted): - #5632 fastOnly/voiceOnly model flags (test fixtures reshaped to arrays) - #5638 workspace provider defaults (readProviderModels already tolerates both shapes; test fixtures reshaped to arrays) - #5729 active-runtime-model listing (pre-#5089 getAllConfiguredModels already enumerates Object.values(AuthType), so the runtime model is included natively) - #5728 ACP set_config_option deterministic provider fixture (reshaped to array; the flake fix is preserved) KNOWN DOWNGRADE CAVEAT: settings already migrated to $version:5 (shipped in v0.19.0) retain the v5 `{ protocol, models }` modelProviders shape, which the reverted ModelRegistry consumes as an array. Such settings will throw on load until re-configured. A v5->v4 downgrade guard/migration is a separate follow-up if backward compatibility for migrated users is needed. * feat(cli): add v5->v4 settings downgrade migration for #5089 revert After reverting #5089, settings already migrated to $version:5 (shipped in v0.19.0) carry a modelProviders `{ protocol, models }` shape that the reverted v4 readers consume as arrays, throwing "models is not iterable" on load. This adds the inverse migration so those configs auto-converge to v4 on load (the user-facing "automatically migrate $version:5 to 4"). - V5ToV4Migration: unwraps each modelProviders `{ protocol, models }` back to its `models` array, drops the now-implicit protocol (warning only when the explicit protocol differs from the key-derived one), and resets $version to 4. - DOWNGRADE_MIGRATIONS keeps the downgrade out of the ascending forward ALL_MIGRATIONS chain (preserving its invariants); runMigrations and needsMigration consider both via a combined convergence set. - needsMigration now gates on `=== SETTINGS_VERSION` instead of `>=`, so a newer-but-handled version (v5) is reported as needing migration while a genuinely unknown newer version (v6+) is still left untouched. Covered by unit tests for the migration, the framework wiring, and an end-to-end loadSettings downgrade-on-load test. * fix(test): align integration settings-version constant with reverted v4 The integration suites hard-coded CURRENT_SETTINGS_VERSION = 5 (introduced by #5676), which mismatched the reverted SETTINGS_VERSION = 4 and failed the migration assertions ($version now writes 4, not 5). Revert the constant to 4 in both settings-migration and qwen-config-dir integration tests. Verified: QWEN_SANDBOX=false vitest run --root ./integration-tests cli/settings-migration.test.ts cli/qwen-config-dir.test.ts → 21 passed. * fix: harden v5-era settings handling on the #5089 revert path Addresses /qreview feedback on the revert: - vscode findOpenaiModels: restore read-side tolerance for the V5 { protocol, models } shape. The extension reads/writes settings.json without running the CLI v5->v4 migration, so a not-yet-downgraded $version:5 file would otherwise return [] and silently drop existing OpenAI models on the next write. (Critical) - modelRegistry.registerAuthTypeModels: guard against a non-array provider value (skip + warn) instead of throwing an opaque "models is not iterable" — covers hand-edited or unmigrated files the downgrade misses. - needsMigration JSDoc: update the stale ">= SETTINGS_VERSION" wording to match the "=== SETTINGS_VERSION, else fall through" logic the downgrade path depends on. - settings.test.ts: also assert the v5->v4 downgrade is persisted to disk (.tmp write-back), not just the in-memory merged result. Adds tests for the registry guard and the vscode V5 read tolerance. * fix(core): break contentGenerator import cycle + cover reverted error paths Addresses /review suggestions on the revert: - contentGenerator: import PROVIDER_SOURCED_FIELDS from constants.js (where it is actually defined) instead of modelsConfig.js, breaking the runtime import cycle contentGenerator -> modelsConfig -> contentGenerator. constants.js only references contentGenerator at the type level, which is erased at runtime, so no cycle remains. - contentGenerator.test: add coverage for the two authType error paths the revert restored (missing authType -> "must have an authType"; unknown authType -> "Unsupported authType"), which #5089's protocol-based tests had replaced. Neither was covered before. The acpAgent z.nativeEnum(AuthType).parse(methodId) suggestion is left as-is: that line is byte-identical to pre-#5089, so it is pre-existing behavior the revert faithfully restores rather than a regression of this PR.
What this PR does
Fixes a core regression where an active runtime model is dropped from
ModelsConfig.getAllConfiguredModels()when itsauthTypehas no registry models. In the default (no explicitauthTypesfilter) case the method now includes the active runtime model'sauthTypein the iteration, so it is enumerated alongside registry models. Callers that pass an explicitauthTypesfilter are unchanged — they still get exactly the requested set (a matching runtime model was, and remains, included by the existing loop).Why it's needed
#5089 changed the default branch of
getAllConfiguredModelsfromObject.values(AuthType)tothis.modelRegistry.getAuthTypes(), which only returns auth types that have registry models. A runtime model resolved purely from env/CLI overrides — e.g.OPENAI_API_KEY/OPENAI_BASE_URL/OPENAI_MODELwith nomodelProvidersentry — hasauthType: 'openai', which has no registry models, so'openai'is absent from the iteration and the runtime model is never emitted. This happens even when the runtime model is the current/active model:currentModelIdpoints at$runtime|openai|…(openai)whileavailableModelscontains onlycoder-model(qwen-oauth).User-facing impact: ACP clients (Zed / VSCode companion) cannot see or re-select the active openai model; the interactive
/modelpicker (modelCommand.tscallsgetAllConfiguredModels()with no filter) omits it too; and the ACP integration testsupports session/set_config_option for mode and modelfailsexpect(openaiModel).toBeDefined()on everymainrun since #5089. The failure is deterministic, not a parallel-settings race — which is why #5721 (assertion shape) and #5724 (QWEN_HOMEisolation) did not fix it.Reviewer Test Plan
How to verify
packages/core/src/models/modelsConfig.test.tscaptures a runtime openai model (nomodelProviders) and asserts it appears ingetAllConfiguredModels(); a second test asserts an explicitauthTypesfilter does not inject it. Run:npx vitest run packages/core/src/models/modelsConfig.test.ts.Expected: pass —
availableModelsnow contains the…(openai)runtime model that the test looks for.Evidence (Before & After)
Non-UI change (core model-listing logic). Observed via a script driving the real ACP protocol (
initialize→authenticate openai→session/new) withOPENAI_*set:Before — runtime model is the current model but missing from the list:
After — runtime model is enumerated:
The new unit test fails with
expected undefined to be defined(the exact CI error) before the fix and passes after. Historically, the pre-#5089 E2E run had all 11acp-integrationtests green; every run since fails this one assertion.Tested on
Linux: ran the new unit tests, the target integration test (against the bundle, fake
OPENAI_*), plustsc/prettier/eslint— all green. macOS / Windows not run locally; the authoritative check is CI.Environment (optional)
npm run bundle+vitest run --root ./integration-testswith fakeOPENAI_*env andQWEN_SANDBOX=false. Core unit tests viavitest runat repo root. No real provider auth needed (the assertions exercise model enumeration, not inference).Risk & Scope
getFastModel, etc.) are unaffected.Linked Issues
Fixes the deterministic ACP
set_config_optionfailure introduced by #5089. Follow-up to the prior attempts #5721 and #5724 (which addressed the symptom, not the cause). Complementary to #5728: that PR hardens the test by injecting an openaimodelProvidersentry (giving openai a registry model, which sidesteps the symptom); this PR fixes the underlying product bug, so the original assertion and real env-only-auth users work without any injection. The two do not conflict.中文说明
这个 PR 做了什么
修复一个核心回归:当当前激活的 runtime 模型所属的
authType没有 registry 模型时,它会被ModelsConfig.getAllConfiguredModels()丢弃。现在在默认分支(未显式传authTypes过滤)里,会把当前激活 runtime 模型的authType也纳入遍历,使其与 registry 模型一起被枚举。显式传authTypes的调用方行为不变——仍然只得到所请求的集合(匹配的 runtime 模型本来就由原有循环包含)。为什么需要
#5089 把
getAllConfiguredModels的默认分支从Object.values(AuthType)改成了this.modelRegistry.getAuthTypes(),而后者只返回有 registry 模型的 authType。一个仅由环境变量 / CLI 覆盖解析出来的 runtime 模型(例如只设了OPENAI_API_KEY/OPENAI_BASE_URL/OPENAI_MODEL、没有modelProviders配置)的authType是'openai',而 openai 没有 registry 模型,于是'openai'不在遍历集合里,这个 runtime 模型就永远不会被输出。即使它就是当前激活模型也一样:currentModelId指向$runtime|openai|…(openai),而availableModels里只有coder-model(qwen-oauth)。用户可见影响:ACP 客户端(Zed / VSCode companion)看不到、也无法重新选中当前激活的 openai 模型;交互式
/model选择器(modelCommand.ts无参调用getAllConfiguredModels())同样漏掉它;ACP 集成测试supports session/set_config_option for mode and model自 #5089 起在每次main跑都会在expect(openaiModel).toBeDefined()失败。这是确定性失败,不是并发 settings 竞争——所以 #5721(改断言形态)和 #5724(隔离QWEN_HOME)都没修好。评审验证
如何验证
packages/core/src/models/modelsConfig.test.ts新增核心单测回归:捕获一个 runtime openai 模型(无modelProviders),断言它出现在getAllConfiguredModels()里;另一个用例断言显式authTypes过滤不会注入它。运行:npx vitest run packages/core/src/models/modelsConfig.test.ts。预期通过——
availableModels现在包含测试要找的…(openai)runtime 模型。证据(Before & After)
非 UI 改动(核心模型列表逻辑)。通过一个驱动真实 ACP 协议的脚本观察(
initialize→authenticate openai→session/new),并设置了OPENAI_*:Before——runtime 模型是当前模型,却不在列表里:
After——runtime 模型被枚举出来:
新单测在修复前以
expected undefined to be defined(即 CI 的报错)失败,修复后通过。历史上 #5089 之前的 E2E 跑里 11 个acp-integration用例全绿;自 #5089 起每次都挂在这一条断言上。测试平台
Linux:跑了新单测、目标集成测试(对着 bundle、fake
OPENAI_*),以及tsc/prettier/eslint,全部通过。macOS / Windows 未本地运行;以 CI 为权威校验。运行环境(可选)
npm run bundle+vitest run --root ./integration-tests,fakeOPENAI_*环境变量、QWEN_SANDBOX=false。核心单测在仓库根用vitest run跑。无需真实 provider 鉴权(断言验证的是模型枚举,不是推理)。风险与范围
getFastModel等)不受影响。关联 Issue
修复 #5089 引入的、确定性的 ACP
set_config_option失败。是此前两次尝试 #5721、#5724 的后续(那两次处理的是症状,不是根因)。与 #5728 互补:#5728 通过注入一个 openaimodelProviders条目来加固测试(给 openai 造了个 registry 模型,从而绕开症状);本 PR 修的是底层产品 bug,因此原断言以及真实的纯 env 认证用户无需任何注入即可正常工作。两者不冲突。