| |
| |
| |
| |
| |
|
|
| import { describe, it, expect, vi, beforeEach } from 'vitest'; |
| import { applyModelSelection } from './policyHelpers.js'; |
| import type { Config } from '../config/config.js'; |
| import { |
| PREVIEW_GEMINI_MODEL, |
| PREVIEW_GEMINI_FLASH_MODEL, |
| PREVIEW_GEMINI_MODEL_AUTO, |
| } from '../config/models.js'; |
| import { ModelAvailabilityService } from './modelAvailabilityService.js'; |
| import { ModelConfigService } from '../services/modelConfigService.js'; |
| import { DEFAULT_MODEL_CONFIGS } from '../config/defaultModelConfigs.js'; |
|
|
| describe('Fallback Integration', () => { |
| let config: Config; |
| let availabilityService: ModelAvailabilityService; |
| let modelConfigService: ModelConfigService; |
|
|
| beforeEach(() => { |
| |
| config = { |
| getModel: () => PREVIEW_GEMINI_MODEL_AUTO, |
| getActiveModel: () => PREVIEW_GEMINI_MODEL_AUTO, |
| setActiveModel: vi.fn(), |
| getUserTier: () => undefined, |
| getHasAccessToPreviewModel: () => true, |
| getModelAvailabilityService: () => availabilityService, |
| modelConfigService: undefined as unknown as ModelConfigService, |
| } as unknown as Config; |
|
|
| availabilityService = new ModelAvailabilityService(); |
| modelConfigService = new ModelConfigService(DEFAULT_MODEL_CONFIGS); |
| |
| (config as any).modelConfigService = modelConfigService; |
| }); |
|
|
| it('should select fallback model when primary model is terminal and config is in AUTO mode', () => { |
| |
| |
| availabilityService.markTerminal(PREVIEW_GEMINI_MODEL, 'quota'); |
|
|
| |
| const requestedModel = PREVIEW_GEMINI_MODEL; |
|
|
| |
| const result = applyModelSelection(config, { |
| model: requestedModel, |
| isChatModel: true, |
| }); |
|
|
| |
| expect(result.model).toBe(PREVIEW_GEMINI_FLASH_MODEL); |
|
|
| |
| expect(config.setActiveModel).toHaveBeenCalledWith( |
| PREVIEW_GEMINI_FLASH_MODEL, |
| ); |
| }); |
|
|
| it('should fallback for Gemini 3 models even if config is NOT in AUTO mode', () => { |
| |
| vi.spyOn(config, 'getModel').mockReturnValue(PREVIEW_GEMINI_MODEL); |
|
|
| |
| availabilityService.markTerminal(PREVIEW_GEMINI_MODEL, 'quota'); |
|
|
| |
| const requestedModel = PREVIEW_GEMINI_MODEL; |
|
|
| |
| const result = applyModelSelection(config, { model: requestedModel }); |
|
|
| |
| expect(result.model).toBe(PREVIEW_GEMINI_FLASH_MODEL); |
| }); |
|
|
| it('should fallback to Flash after failures and restore Pro on next turn', () => { |
| const requestedModel = PREVIEW_GEMINI_MODEL; |
|
|
| |
| const result1 = applyModelSelection(config, { |
| model: requestedModel, |
| isChatModel: true, |
| }); |
| expect(result1.model).toBe(PREVIEW_GEMINI_MODEL); |
| expect(result1.maxAttempts).toBe(3); |
|
|
| |
| availabilityService.markRetryOncePerTurn(PREVIEW_GEMINI_MODEL, 3); |
| availabilityService.consumeStickyAttempt(PREVIEW_GEMINI_MODEL); |
|
|
| |
| const result2 = applyModelSelection(config, { |
| model: requestedModel, |
| isChatModel: true, |
| }); |
| expect(result2.model).toBe(PREVIEW_GEMINI_FLASH_MODEL); |
|
|
| |
| availabilityService.resetTurn(); |
|
|
| |
| const result3 = applyModelSelection(config, { |
| model: requestedModel, |
| isChatModel: true, |
| }); |
| expect(result3.model).toBe(PREVIEW_GEMINI_MODEL); |
| expect(result3.maxAttempts).toBe(3); |
| }); |
| }); |
|
|