fix(ai): contain provider setup errors (#2411)

Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
This commit is contained in:
Aqil Aziz
2026-05-25 09:27:06 -07:00
committed by GitHub
co-authored by Steven Enamakel
parent 70440039b9
commit 20c349fcdc
3 changed files with 23 additions and 5 deletions
@@ -760,7 +760,6 @@ const ProviderKeyDialog = ({
);
};
// ─────────────────────────────────────────────────────────────────────────────
// Background loop controls + usage diagnostics
// ─────────────────────────────────────────────────────────────────────────────
@@ -38,7 +38,7 @@ vi.mock('../../../../services/api/aiSettingsApi', () => ({
saveAISettings: vi.fn(),
loadLocalProviderSnapshot: vi.fn(),
testProviderModel: vi.fn(),
setCloudProviderKey: vi.fn(),
setCloudProviderKey: vi.fn().mockResolvedValue(undefined),
clearCloudProviderKey: vi.fn().mockResolvedValue(undefined),
serializeProviderRef: vi.fn((r: { kind: string; providerSlug?: string; model?: string }) =>
r.kind === 'openhuman'
@@ -356,6 +356,25 @@ describe('AIPanel', () => {
expect(screen.getByLabelText(/API key/i)).toBeInTheDocument();
});
it('surfaces provider setup errors in an alert with technical details collapsed', async () => {
vi.mocked(loadAISettings).mockResolvedValue({ ...baseSettings, cloudProviders: [] });
vi.mocked(listProviderModels).mockRejectedValueOnce(
new Error('Could not reach OpenAI: provider returned 401 Unauthorized')
);
renderWithProviders(<AIPanel />);
fireEvent.click(await screen.findByRole('switch', { name: /Connect OpenAI/i }));
const dialog = await screen.findByRole('dialog', { name: /Connect OpenAI/i });
fireEvent.change(within(dialog).getByLabelText(/API key/i), {
target: { value: 'sk-bad-key' },
});
fireEvent.click(within(dialog).getByRole('button', { name: /^Save$/i }));
const alert = await within(dialog).findByRole('alert');
expect(alert).toHaveTextContent(/rejected the credentials/i);
});
it('clicking the OpenRouter chip shows both API key entry and the OAuth button', async () => {
vi.mocked(loadAISettings).mockResolvedValue({ ...baseSettings, cloudProviders: [] });
+3 -3
View File
@@ -222,9 +222,9 @@ const de5: TranslationMap = {
'settings.mascot.characterDesc': 'Charakterbeschreibung',
'settings.mascot.characterHeading': 'Zeichenüberschrift',
'settings.mascot.customGifError':
'Gib eine HTTPS-.gif-URL, eine Loopback-HTTP-.gif-URL, eine file://-.gif-URL oder einen lokalen .gif-Pfad ein.',
'settings.mascot.customGifHeading': 'Eigener GIF-Avatar',
'settings.mascot.customGifLabel': 'URL des eigenen GIF-Avatars',
'GIF konnte nicht geladen werden. Bitte überprüfe die URL und versuche es erneut.',
'settings.mascot.customGifHeading': 'Benutzerdefinierter GIF-Avatar',
'settings.mascot.customGifLabel': 'URL für benutzerdefinierten GIF-Avatar',
'settings.mascot.colorDesc': 'Farbbeschreibung',
'settings.mascot.colorHeading': 'Farbüberschrift',
'settings.mascot.loadingLibrary': 'OpenHuman-Bibliothek wird geladen…',