diff --git a/frontend-modern/src/components/AI/Chat/MessageItem.tsx b/frontend-modern/src/components/AI/Chat/MessageItem.tsx index 85a37df7d..6a4f558ca 100644 --- a/frontend-modern/src/components/AI/Chat/MessageItem.tsx +++ b/frontend-modern/src/components/AI/Chat/MessageItem.tsx @@ -374,8 +374,8 @@ export const MessageItem: Component = (props) => { const alternative = props.modelRouteAlternative; if (!alternative) return ''; return alternative.kind === 'same-model-route' - ? `Retry with ${alternative.providerLabel} route` - : `Retry with ${alternative.providerLabel} model route`; + ? `Switch to ${alternative.providerLabel} route and retry` + : `Switch to ${alternative.providerLabel} model route and retry`; }; const hasPreviousModelRoute = (event: StreamDisplayEvent) => { const model = event.model?.trim(); diff --git a/frontend-modern/src/components/AI/Chat/__tests__/AIChat.test.tsx b/frontend-modern/src/components/AI/Chat/__tests__/AIChat.test.tsx index 9dc82d358..bf6b49d38 100644 --- a/frontend-modern/src/components/AI/Chat/__tests__/AIChat.test.tsx +++ b/frontend-modern/src/components/AI/Chat/__tests__/AIChat.test.tsx @@ -320,8 +320,8 @@ vi.mock('../ChatMessages', () => ({ } > {recovery().alternative.kind === 'same-model-route' - ? `Retry with ${recovery().alternative.providerLabel} route` - : `Retry with ${recovery().alternative.providerLabel} model route`} + ? `Switch to ${recovery().alternative.providerLabel} route and retry` + : `Switch to ${recovery().alternative.providerLabel} model route and retry`} )} @@ -1234,6 +1234,27 @@ describe('AIChat', () => { expect(document.activeElement).toBe(textarea); }); + it('treats provider readiness request failures as recoverable route status', async () => { + const readinessError = new Error('Model provider does not match test provider'); + mockAIAPI.testProvider.mockRejectedValueOnce(readinessError); + + renderChat(); + + await screen.findByText('Selected model route issue'); + + expect(screen.getByLabelText('Assistant selected model route status')).toHaveTextContent( + 'Pulse could not verify the selected model route.', + ); + expect(logger.warn).toHaveBeenCalledWith( + '[AIChat] Failed to check selected provider readiness:', + readinessError, + ); + expect(logger.error).not.toHaveBeenCalledWith( + '[AIChat] Failed to check selected provider readiness:', + readinessError, + ); + }); + it('offers an equivalent configured-provider route when the selected provider fails', async () => { mockAIAPI.getSettings.mockResolvedValue({ model: 'deepseek:deepseek-v4-pro', @@ -1279,9 +1300,9 @@ describe('AIChat', () => { renderChat(); const switchButton = await screen.findByRole('button', { - name: 'Use OpenRouter route', + name: 'Switch to OpenRouter route', }); - expect(switchButton).toHaveTextContent('Use OpenRouter route'); + expect(switchButton).toHaveTextContent('Switch to OpenRouter route'); fireEvent.click(switchButton); @@ -1330,7 +1351,7 @@ describe('AIChat', () => { renderChat(); await screen.findByRole('button', { - name: 'Use OpenRouter route', + name: 'Switch to OpenRouter route', }); const textarea = screen.getByPlaceholderText( diff --git a/frontend-modern/src/components/AI/Chat/__tests__/MessageItem.test.tsx b/frontend-modern/src/components/AI/Chat/__tests__/MessageItem.test.tsx index 7b08388ca..963eb7a38 100644 --- a/frontend-modern/src/components/AI/Chat/__tests__/MessageItem.test.tsx +++ b/frontend-modern/src/components/AI/Chat/__tests__/MessageItem.test.tsx @@ -252,9 +252,9 @@ describe('MessageItem', () => { expect(alert.textContent).toContain('billing or quota reasons'); const retryViaOpenRouter = screen.getByRole('button', { - name: 'Retry with OpenRouter route', + name: 'Switch to OpenRouter route and retry', }); - expect(retryViaOpenRouter).toHaveTextContent('Retry with OpenRouter route'); + expect(retryViaOpenRouter).toHaveTextContent('Switch to OpenRouter route and retry'); fireEvent.click(retryViaOpenRouter); expect(onUseModelRoute).toHaveBeenCalledWith('openrouter:deepseek/deepseek-v4-pro', 'msg-1'); diff --git a/frontend-modern/src/components/AI/Chat/index.tsx b/frontend-modern/src/components/AI/Chat/index.tsx index 7a273bee2..26c744dde 100644 --- a/frontend-modern/src/components/AI/Chat/index.tsx +++ b/frontend-modern/src/components/AI/Chat/index.tsx @@ -1973,7 +1973,7 @@ export const AIChat: Component = (props) => { }); } catch (error) { if (requestId !== providerReadinessRequestId) return; - logger.error('[AIChat] Failed to check selected provider readiness:', error); + logger.warn('[AIChat] Failed to check selected provider readiness:', error); setProviderReadiness({ status: 'error', provider, @@ -2234,8 +2234,8 @@ export const AIChat: Component = (props) => { const alternative = providerReadinessAlternative(); if (!alternative) return ''; return alternative.kind === 'same-model-route' - ? `Use ${alternative.providerLabel} route` - : `Use ${alternative.providerLabel} model route`; + ? `Switch to ${alternative.providerLabel} route` + : `Switch to ${alternative.providerLabel} model route`; }; createEffect(() => {