diff --git a/packages/cli/src/utils/modelConfigUtils.test.ts b/packages/cli/src/utils/modelConfigUtils.test.ts index 97cea9974..abd556308 100644 --- a/packages/cli/src/utils/modelConfigUtils.test.ts +++ b/packages/cli/src/utils/modelConfigUtils.test.ts @@ -442,6 +442,187 @@ describe('modelConfigUtils', () => { ); }); + it('should find modelProvider from OPENAI_MODEL when argv.model is not provided', () => { + const argv = {}; + const modelProvider: ProviderModelConfig = { + id: 'env-openai-model', + name: 'Env OpenAI Model', + generationConfig: { + samplingParams: { temperature: 0.6 }, + }, + }; + const settings = makeMockSettings({ + model: { name: 'settings-model' }, + modelProviders: { + [AuthType.USE_OPENAI]: [ + { id: 'settings-model', name: 'Settings Model' }, + modelProvider, + ], + }, + }); + const selectedAuthType = AuthType.USE_OPENAI; + + vi.mocked(resolveModelConfig).mockReturnValue({ + config: { + model: 'env-openai-model', + apiKey: '', + baseUrl: '', + }, + sources: {}, + warnings: [], + }); + + resolveCliGenerationConfig({ + argv, + settings, + selectedAuthType, + env: { OPENAI_MODEL: 'env-openai-model' }, + }); + + expect(vi.mocked(resolveModelConfig)).toHaveBeenCalledWith( + expect.objectContaining({ + modelProvider, + }), + ); + }); + + it('should find modelProvider from QWEN_MODEL when OPENAI_MODEL is not provided', () => { + const argv = {}; + const modelProvider: ProviderModelConfig = { + id: 'qwen-env-model', + name: 'Qwen Env Model', + generationConfig: { + samplingParams: { temperature: 0.7 }, + }, + }; + const settings = makeMockSettings({ + model: { name: 'settings-model' }, + modelProviders: { + [AuthType.USE_OPENAI]: [ + { id: 'settings-model', name: 'Settings Model' }, + modelProvider, + ], + }, + }); + const selectedAuthType = AuthType.USE_OPENAI; + + vi.mocked(resolveModelConfig).mockReturnValue({ + config: { + model: 'qwen-env-model', + apiKey: '', + baseUrl: '', + }, + sources: {}, + warnings: [], + }); + + resolveCliGenerationConfig({ + argv, + settings, + selectedAuthType, + env: { QWEN_MODEL: 'qwen-env-model' }, + }); + + expect(vi.mocked(resolveModelConfig)).toHaveBeenCalledWith( + expect.objectContaining({ + modelProvider, + }), + ); + }); + + it('should prefer OPENAI_MODEL over QWEN_MODEL and settings.model.name for USE_OPENAI provider lookup', () => { + const argv = {}; + const openAIProvider: ProviderModelConfig = { + id: 'openai-env-model', + name: 'OpenAI Env Model', + }; + const qwenProvider: ProviderModelConfig = { + id: 'qwen-env-model', + name: 'Qwen Env Model', + }; + const settings = makeMockSettings({ + model: { name: 'settings-model' }, + modelProviders: { + [AuthType.USE_OPENAI]: [ + { id: 'settings-model', name: 'Settings Model' }, + qwenProvider, + openAIProvider, + ], + }, + }); + const selectedAuthType = AuthType.USE_OPENAI; + + vi.mocked(resolveModelConfig).mockReturnValue({ + config: { + model: 'openai-env-model', + apiKey: '', + baseUrl: '', + }, + sources: {}, + warnings: [], + }); + + resolveCliGenerationConfig({ + argv, + settings, + selectedAuthType, + env: { + OPENAI_MODEL: 'openai-env-model', + QWEN_MODEL: 'qwen-env-model', + }, + }); + + expect(vi.mocked(resolveModelConfig)).toHaveBeenCalledWith( + expect.objectContaining({ + modelProvider: openAIProvider, + }), + ); + }); + + it('should ignore OPENAI_MODEL for non-USE_OPENAI provider lookup', () => { + const argv = {}; + const settingsModelProvider: ProviderModelConfig = { + id: 'settings-model', + name: 'Settings Model', + }; + const unrelatedOpenAIProvider: ProviderModelConfig = { + id: 'openai-env-model', + name: 'OpenAI Env Model', + }; + const settings = makeMockSettings({ + model: { name: 'settings-model' }, + modelProviders: { + [AuthType.USE_ANTHROPIC]: [ + settingsModelProvider, + unrelatedOpenAIProvider, + ], + }, + }); + const selectedAuthType = AuthType.USE_ANTHROPIC; + + vi.mocked(resolveModelConfig).mockReturnValue({ + config: { + model: 'settings-model', + apiKey: '', + baseUrl: '', + }, + sources: {}, + warnings: [], + }); + + resolveCliGenerationConfig({ + argv, + settings, + selectedAuthType, + env: { OPENAI_MODEL: 'openai-env-model' }, + }); + + expect(vi.mocked(resolveModelConfig)).toHaveBeenCalledWith( + expect.objectContaining({ + modelProvider: settingsModelProvider, + }), + ); + }); it('should not find modelProvider when authType is undefined', () => { const argv = { model: 'test-model' }; const settings = makeMockSettings({ @@ -723,5 +904,71 @@ describe('modelConfigUtils', () => { }), ); }); + + it('should respect precedence: argv.model > OPENAI_MODEL > QWEN_MODEL > settings.model.name', () => { + const mockSettings = makeMockSettings({ + model: { name: 'settings-model' }, + modelProviders: { + [AuthType.USE_OPENAI]: [ + { id: 'settings-model' } as ProviderModelConfig, + { id: 'openai-env-model' } as ProviderModelConfig, + { id: 'qwen-env-model' } as ProviderModelConfig, + { id: 'cli-model' } as ProviderModelConfig, + ], + }, + }); + + vi.mocked(resolveModelConfig).mockReturnValue({ + config: { model: 'cli-model', apiKey: '', baseUrl: '' }, + sources: {}, + warnings: [], + }); + const result1 = resolveCliGenerationConfig({ + argv: { model: 'cli-model' }, + settings: mockSettings, + selectedAuthType: AuthType.USE_OPENAI, + env: { OPENAI_MODEL: 'openai-env-model' }, + }); + expect(result1.model).toBe('cli-model'); + + vi.mocked(resolveModelConfig).mockReturnValue({ + config: { model: 'openai-env-model', apiKey: '', baseUrl: '' }, + sources: {}, + warnings: [], + }); + const result2 = resolveCliGenerationConfig({ + argv: {}, + settings: mockSettings, + selectedAuthType: AuthType.USE_OPENAI, + env: { OPENAI_MODEL: 'openai-env-model', QWEN_MODEL: 'qwen-env-model' }, + }); + expect(result2.model).toBe('openai-env-model'); + + vi.mocked(resolveModelConfig).mockReturnValue({ + config: { model: 'openai-env-model', apiKey: '', baseUrl: '' }, + sources: {}, + warnings: [], + }); + const result3 = resolveCliGenerationConfig({ + argv: {}, + settings: mockSettings, + selectedAuthType: AuthType.USE_OPENAI, + env: { OPENAI_MODEL: 'openai-env-model' }, + }); + expect(result3.model).toBe('openai-env-model'); + + vi.mocked(resolveModelConfig).mockReturnValue({ + config: { model: 'settings-model', apiKey: '', baseUrl: '' }, + sources: {}, + warnings: [], + }); + const result4 = resolveCliGenerationConfig({ + argv: {}, + settings: mockSettings, + selectedAuthType: AuthType.USE_OPENAI, + env: {}, + }); + expect(result4.model).toBe('settings-model'); + }); }); }); diff --git a/packages/cli/src/utils/modelConfigUtils.ts b/packages/cli/src/utils/modelConfigUtils.ts index aa5ac5e82..83dbae1cc 100644 --- a/packages/cli/src/utils/modelConfigUtils.ts +++ b/packages/cli/src/utils/modelConfigUtils.ts @@ -100,8 +100,13 @@ export function resolveCliGenerationConfig( if (authType && settings.modelProviders) { const providers = settings.modelProviders[authType]; if (providers && Array.isArray(providers)) { - // Try to find by requested model (from CLI or settings) - const requestedModel = argv.model || settings.model?.name; + const requestedModel = + authType === AuthType.USE_OPENAI + ? argv.model || + env['OPENAI_MODEL'] || + env['QWEN_MODEL'] || + settings.model?.name + : argv.model || settings.model?.name; if (requestedModel) { modelProvider = providers.find((p) => p.id === requestedModel) as | ProviderModelConfig