From 947d729fd95ec4673933ffd2f232e335c6b1c090 Mon Sep 17 00:00:00 2001 From: vgcman16 <155417613+vgcman16@users.noreply.github.com> Date: Thu, 5 Jun 2025 21:34:37 -0500 Subject: [PATCH] Improve OpenAI provider error handling and add tests --- app/lib/modules/llm/providers/openai.spec.ts | 35 ++++++++++++++++++++ app/lib/modules/llm/providers/openai.ts | 14 +++++++- vite.config.ts | 4 ++- 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 app/lib/modules/llm/providers/openai.spec.ts diff --git a/app/lib/modules/llm/providers/openai.spec.ts b/app/lib/modules/llm/providers/openai.spec.ts new file mode 100644 index 00000000..d0c624d0 --- /dev/null +++ b/app/lib/modules/llm/providers/openai.spec.ts @@ -0,0 +1,35 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; + +vi.mock('../manager', () => ({ + LLMManager: class { + static getInstance() { + return { env: {} }; + } + }, +})); + +import OpenAIProvider from './openai'; + +const provider = new OpenAIProvider(); + +describe('OpenAIProvider.getDynamicModels', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('throws error when fetch fails', async () => { + const fetchMock = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ error: { message: 'Invalid API key' } }), { + status: 401, + statusText: 'Unauthorized', + }), + ); + vi.stubGlobal('fetch', fetchMock); + + vi.spyOn(provider as any, 'getProviderBaseUrlAndKey').mockReturnValue({ + apiKey: 'bad-key', + }); + + await expect(provider.getDynamicModels()).rejects.toThrow('Failed to fetch models from OpenAI: Invalid API key'); + }); +}); diff --git a/app/lib/modules/llm/providers/openai.ts b/app/lib/modules/llm/providers/openai.ts index b6193b91..4ab00a5d 100644 --- a/app/lib/modules/llm/providers/openai.ts +++ b/app/lib/modules/llm/providers/openai.ts @@ -43,7 +43,19 @@ export default class OpenAIProvider extends BaseProvider { }, }); - const res = (await response.json()) as any; + let res: any; + + try { + res = await response.json(); + } catch (error) { + throw new Error(`Failed to parse response from ${this.name}: ${(error as Error).message}`); + } + + if (!response.ok) { + const message = res?.error?.message || response.statusText; + throw new Error(`Failed to fetch models from ${this.name}: ${message}`); + } + const staticModelIds = this.staticModels.map((m) => m.name); const data = res.data.filter( diff --git a/vite.config.ts b/vite.config.ts index 60ea52bf..427821c5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -128,7 +128,9 @@ export default defineConfig((config) => { }, }), UnoCSS(), - tsconfigPaths(), + tsconfigPaths({ + ignoreConfigErrors: true, + }), chrome129IssuePlugin(), config.mode === 'production' && optimizeCssModules({ apply: 'build' }), ],