fix(cli): respect OPENAI_MODEL precedence in CLI model resolution#3567
Conversation
| const envQwenModel = env['QWEN_MODEL']; | ||
|
|
||
| const requestedModel = | ||
| argv.model || envOpenAIModel || envQwenModel || settings.model?.name; |
There was a problem hiding this comment.
[Critical] OPENAI_MODEL and QWEN_MODEL are now used for provider lookup for every auth type, not just USE_OPENAI. That lets unrelated env vars influence modelProvider selection for Anthropic/Gemini/Vertex flows, while resolveModelConfig() still uses auth-specific env mappings. This can cause provider-scoped settings to come from the wrong provider.
Please gate this fallback to AuthType.USE_OPENAI, or derive the model env var from the selected auth type instead of hardcoding OpenAI/Qwen env vars.
— gpt-5.4 via Qwen Code /review
| selectedAuthType: AuthType.USE_OPENAI, | ||
| env: { OPENAI_MODEL: 'openai-env-model' }, | ||
| }); | ||
| expect(result1.model).toBe('cli-model'); |
There was a problem hiding this comment.
[Suggestion] This test only asserts the mocked resolveModelConfig() return value, so it does not verify which modelProvider was selected before delegation. A precedence regression here could return without this test failing.
Please assert on the modelProvider argument passed to resolveModelConfig() (and ideally add a non-USE_OPENAI auth case as well).
— gpt-5.4 via Qwen Code /review
| @@ -723,5 +860,71 @@ describe('modelConfigUtils', () => { | |||
| }), | |||
| ); | |||
There was a problem hiding this comment.
[Suggestion] The test name says it validates the full precedence chain argv.model > OPENAI_MODEL > QWEN_MODEL > settings.model.name, but the added cases never exercise the QWEN_MODEL-only fallback path. They cover CLI override, OPENAI_MODEL over QWEN_MODEL, OPENAI_MODEL alone, and settings fallback, but not the branch where OPENAI_MODEL is absent and QWEN_MODEL should select the provider.
A regression that removes or reorders the QWEN_MODEL fallback would not be caught despite this test claiming to cover the full precedence chain. Please add a case with env: { QWEN_MODEL: 'qwen-env-model' } and no OPENAI_MODEL, expecting qwen-env-model, or narrow the test name to match the cases actually covered.
— gpt-5.5 via Qwen Code /review
There was a problem hiding this comment.
Good catch. I missed the QWEN_MODEL-only fallback branch. I’ll add a regression test for env: { QWEN_MODEL: ... } with no OPENAI_MODEL so the suite actually covers the full precedence chain.
wenshao
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅ — gpt-5.5 via Qwen Code /review
tmux end-to-end verificationCalled the real Setup:
Raw outputKey repro findingWhen the user sets only LGTM ✅ |
|
I recently fixed provider id resolution drift in #3567. A follow-up problem I noticed is that when provider resolution fails or falls back, users often cannot tell whether the issue is:
I'm going to take a small scoped pass at adding debug diagnostics around provider resolution without changing runtime behavior. |
|
Thanks @B-A-M-N — and great instinct on the follow-up. The "silent fallback with no signal as to which of N reasons fired" pattern is exactly the kind of thing that eats up support time, and a scoped diagnostics-only pass is the right shape for it (no behavior change, easy to review, easy to revert). Looking forward to the follow-up PR. Please tag me when it's up and I'll prioritize the review. |
|
Hi @B-A-M-N, after tracing this end-to-end, we're going to revert this PR. Wanted to leave the reasoning here for the record. Scenario:
Pre-PR, the CLI looked up the provider entry by Post-PR, the lookup uses The contract this breaks: pre-PR, an explicit Worth noting: model is the one field users actively switch via a CLI command ( Revert: If you have a specific user-reported bug this was addressing, please share — happy to take another look at the underlying issue and find a fix that doesn't regress the |
…tion (#3567)" (#3633) This reverts commit 007a109. The change made `OPENAI_MODEL` outrank `settings.model.name` when looking up the active entry in `settings.modelProviders`. Combined with the core resolver's `modelProvider > cli > env > settings` priority, this caused a regression: a `/model` selection (which writes `settings.model.name`) was silently overridden whenever `OPENAI_MODEL` was set in the user's shell, with no warning surfaced. Restoring the previous behavior — looking up the provider entry by `argv.model || settings.model?.name` — preserves the implicit contract that an explicit `modelProviders` config takes precedence over stale shell defaults. Users without a `modelProviders` config are unaffected: env vars still drive model selection through the core resolver. See discussion on #3567.
…ection Fixes model precedence regression from QwenLM#3567 (reverted in QwenLM#3633): - argv.model > settings.model.name > OPENAI_MODEL > QWEN_MODEL - settings.model.name wins over OPENAI_MODEL when it matches a provider - OPENAI_MODEL only used as fallback when no explicit model is configured - Added tests proving both paths: settings wins, and OPENAI_MODEL fallback works Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…3645) * fix(cli): correct OPENAI_MODEL precedence without breaking /model selection Fixes model precedence regression from #3567 (reverted in #3633): - argv.model > settings.model.name > OPENAI_MODEL > QWEN_MODEL - settings.model.name wins over OPENAI_MODEL when it matches a provider - OPENAI_MODEL only used as fallback when no explicit model is configured - Added tests proving both paths: settings wins, and OPENAI_MODEL fallback works Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): separate model selection from provider lookup to prevent env override The previous implementation iterated through candidates [argv.model, settings.model.name, OPENAI_MODEL, QWEN_MODEL] and picked the first matching provider. This caused settings.model.name to be overridden by OPENAI_MODEL when the former didn't match any provider. Fix by: 1. First resolve target model using strict precedence 2. Only look up provider for that specific model 3. Filter OPENAI_MODEL/QWEN_MODEL from env when model was resolved from settings or argv, preventing the core resolver from picking up these env vars Also fixes Edge Case 5 test (was testing buggy behavior) and adds integration test verifying settings.model.name wins over OPENAI_MODEL. * fix(types): remove unused type annotation from mock The mockImplementation used ModelConfigSourcesInput type which wasn't properly resolved. Remove the type annotation to fix TypeScript and ESLint errors. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * trigger CI * chore: trigger clean CI build * fix(test): make mock compatible with CI type checking The mockImplementation was causing TS2345 in CI because the return type didn't match ModelConfigResolutionResult. Fixed by using optional chaining and removing type annotations that caused CI build failures. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(test): make mock compatible with CI type checking The mock of resolveModelConfig returned sources as { model: string } instead of { model: ConfigSource }, causing a type error in CI only (where strictBuild is enabled). Use proper ConfigSource objects. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(test): update mock to match reviewer suggestion Use proper ConfigSource objects with path/envKey details as suggested in the review, matching what resolveModelConfig actually returns. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(cli): add regression tests for model precedence Adds [Regression] tests that guard against the original bug where OPENAI_MODEL incorrectly overrode settings.model.name. Tests cover: - settings.model.name precedence over OPENAI_MODEL - OPENAI_MODEL used when settings.model.name not set - argv.model overriding both settings and env - QWEN_MODEL as fallback when OPENAI_MODEL not set - Non-OpenAI auth ignoring OPENAI_MODEL Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): address reviewer feedback on model precedence PR - Add missing assertion in Non-OpenAI auth test to verify OPENAI_MODEL is filtered from env passed to resolveModelConfig - Clean up modelProvider lookup comment to clarify the old bug is fixed Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): address review feedback on model precedence PR - Sanitize OPENAI_MODEL/QWEN_MODEL in beforeEach to prevent flaky tests - Remove stray "// trigger rebuild" comment - Add AUTH_ENV_MODEL_VARS mapping for all auth types (not just OpenAI) - Fix filteredEnv logic to strip ALL model env vars when model not from env - Use sourceEnvVar tracking to only keep the env var that was actually used Fixes the blocking test failure when OPENAI_MODEL is set in shell env. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): fix test assertion for filtered env, add source-based filtering comments --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
…enLM#3567) * fix(cli): respect OPENAI_MODEL precedence in CLI model resolution * test(cli): cover env-driven model precedence for OpenAI-compatible auth * fix(cli): scope model env precedence by auth type * test(cli): cover QWEN_MODEL fallback precedence
…tion (QwenLM#3567)" (QwenLM#3633) This reverts commit 4420229. The change made `OPENAI_MODEL` outrank `settings.model.name` when looking up the active entry in `settings.modelProviders`. Combined with the core resolver's `modelProvider > cli > env > settings` priority, this caused a regression: a `/model` selection (which writes `settings.model.name`) was silently overridden whenever `OPENAI_MODEL` was set in the user's shell, with no warning surfaced. Restoring the previous behavior — looking up the provider entry by `argv.model || settings.model?.name` — preserves the implicit contract that an explicit `modelProviders` config takes precedence over stale shell defaults. Users without a `modelProviders` config are unaffected: env vars still drive model selection through the core resolver. See discussion on QwenLM#3567.
…wenLM#3645) * fix(cli): correct OPENAI_MODEL precedence without breaking /model selection Fixes model precedence regression from QwenLM#3567 (reverted in QwenLM#3633): - argv.model > settings.model.name > OPENAI_MODEL > QWEN_MODEL - settings.model.name wins over OPENAI_MODEL when it matches a provider - OPENAI_MODEL only used as fallback when no explicit model is configured - Added tests proving both paths: settings wins, and OPENAI_MODEL fallback works Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): separate model selection from provider lookup to prevent env override The previous implementation iterated through candidates [argv.model, settings.model.name, OPENAI_MODEL, QWEN_MODEL] and picked the first matching provider. This caused settings.model.name to be overridden by OPENAI_MODEL when the former didn't match any provider. Fix by: 1. First resolve target model using strict precedence 2. Only look up provider for that specific model 3. Filter OPENAI_MODEL/QWEN_MODEL from env when model was resolved from settings or argv, preventing the core resolver from picking up these env vars Also fixes Edge Case 5 test (was testing buggy behavior) and adds integration test verifying settings.model.name wins over OPENAI_MODEL. * fix(types): remove unused type annotation from mock The mockImplementation used ModelConfigSourcesInput type which wasn't properly resolved. Remove the type annotation to fix TypeScript and ESLint errors. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * trigger CI * chore: trigger clean CI build * fix(test): make mock compatible with CI type checking The mockImplementation was causing TS2345 in CI because the return type didn't match ModelConfigResolutionResult. Fixed by using optional chaining and removing type annotations that caused CI build failures. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(test): make mock compatible with CI type checking The mock of resolveModelConfig returned sources as { model: string } instead of { model: ConfigSource }, causing a type error in CI only (where strictBuild is enabled). Use proper ConfigSource objects. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(test): update mock to match reviewer suggestion Use proper ConfigSource objects with path/envKey details as suggested in the review, matching what resolveModelConfig actually returns. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(cli): add regression tests for model precedence Adds [Regression] tests that guard against the original bug where OPENAI_MODEL incorrectly overrode settings.model.name. Tests cover: - settings.model.name precedence over OPENAI_MODEL - OPENAI_MODEL used when settings.model.name not set - argv.model overriding both settings and env - QWEN_MODEL as fallback when OPENAI_MODEL not set - Non-OpenAI auth ignoring OPENAI_MODEL Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): address reviewer feedback on model precedence PR - Add missing assertion in Non-OpenAI auth test to verify OPENAI_MODEL is filtered from env passed to resolveModelConfig - Clean up modelProvider lookup comment to clarify the old bug is fixed Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): address review feedback on model precedence PR - Sanitize OPENAI_MODEL/QWEN_MODEL in beforeEach to prevent flaky tests - Remove stray "// trigger rebuild" comment - Add AUTH_ENV_MODEL_VARS mapping for all auth types (not just OpenAI) - Fix filteredEnv logic to strip ALL model env vars when model not from env - Use sourceEnvVar tracking to only keep the env var that was actually used Fixes the blocking test failure when OPENAI_MODEL is set in shell env. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): fix test assertion for filtered env, add source-based filtering comments --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
Summary
Fix CLI-side model resolution precedence for OpenAI-compatible auth so env-driven model selection stays aligned with provider lookup.
resolveCliGenerationConfig()now uses this order when selecting the requested model:argv.modelOPENAI_MODELQWEN_MODELsettings.model.nameWhy
For OpenAI-compatible integrations, model selection can be injected through
OPENAI_MODEL. Without this precedence in the CLI resolver, the final resolved model andmodelProviderslookup can drift from the env-selected model and fall back to settings/defaults instead.This change keeps those paths aligned.
Changes
Files:
packages/cli/src/utils/modelConfigUtils.tspackages/cli/src/utils/modelConfigUtils.test.tsImplementation:
argv.model -> OPENAI_MODEL -> QWEN_MODEL -> settings.model.namewhen choosing the requested model for provider lookupTests:
OPENAI_MODELwhenargv.modelis absentOPENAI_MODELtaking precedence overQWEN_MODELScope
This PR is intentionally narrow.
It does not include:
Verification
Ran targeted canonical suites locally:
npm test --workspace=packages/cli -- --run src/utils/modelConfigUtils.test.tsnpm test --workspace=packages/core -- --run src/models/modelConfigResolver.test.tsThese passed against the development checkout used to produce this patch.