diff --git a/api/server/services/ModelService.js b/api/server/services/ModelService.js index 6cbc018824..28660c4795 100644 --- a/api/server/services/ModelService.js +++ b/api/server/services/ModelService.js @@ -80,7 +80,9 @@ const fetchModels = async ({ try { const options = { - headers: {}, + headers: { + ...(headers ?? {}), + }, timeout: 5000, }; diff --git a/api/server/services/ModelService.spec.js b/api/server/services/ModelService.spec.js index 81c1203461..ca07d9ee71 100644 --- a/api/server/services/ModelService.spec.js +++ b/api/server/services/ModelService.spec.js @@ -81,6 +81,70 @@ describe('fetchModels', () => { ); }); + it('should pass custom headers to the API request', async () => { + const customHeaders = { + 'X-Custom-Header': 'custom-value', + 'X-API-Version': 'v2', + }; + + await fetchModels({ + user: 'user123', + apiKey: 'testApiKey', + baseURL: 'https://api.test.com', + name: 'TestAPI', + headers: customHeaders, + }); + + expect(axios.get).toHaveBeenCalledWith( + expect.stringContaining('https://api.test.com/models'), + expect.objectContaining({ + headers: expect.objectContaining({ + 'X-Custom-Header': 'custom-value', + 'X-API-Version': 'v2', + Authorization: 'Bearer testApiKey', + }), + }), + ); + }); + + it('should handle null headers gracefully', async () => { + await fetchModels({ + user: 'user123', + apiKey: 'testApiKey', + baseURL: 'https://api.test.com', + name: 'TestAPI', + headers: null, + }); + + expect(axios.get).toHaveBeenCalledWith( + expect.stringContaining('https://api.test.com/models'), + expect.objectContaining({ + headers: expect.objectContaining({ + Authorization: 'Bearer testApiKey', + }), + }), + ); + }); + + it('should handle undefined headers gracefully', async () => { + await fetchModels({ + user: 'user123', + apiKey: 'testApiKey', + baseURL: 'https://api.test.com', + name: 'TestAPI', + headers: undefined, + }); + + expect(axios.get).toHaveBeenCalledWith( + expect.stringContaining('https://api.test.com/models'), + expect.objectContaining({ + headers: expect.objectContaining({ + Authorization: 'Bearer testApiKey', + }), + }), + ); + }); + afterEach(() => { jest.clearAllMocks(); }); @@ -410,6 +474,64 @@ describe('getAnthropicModels', () => { const models = await getAnthropicModels(); expect(models).toEqual(['claude-1', 'claude-2']); }); + + it('should use Anthropic-specific headers when fetching models', async () => { + delete process.env.ANTHROPIC_MODELS; + process.env.ANTHROPIC_API_KEY = 'test-anthropic-key'; + + axios.get.mockResolvedValue({ + data: { + data: [{ id: 'claude-3' }, { id: 'claude-4' }], + }, + }); + + await fetchModels({ + user: 'user123', + apiKey: 'test-anthropic-key', + baseURL: 'https://api.anthropic.com/v1', + name: EModelEndpoint.anthropic, + }); + + expect(axios.get).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + headers: { + 'x-api-key': 'test-anthropic-key', + 'anthropic-version': expect.any(String), + }, + }), + ); + }); + + it('should pass custom headers for Anthropic endpoint', async () => { + const customHeaders = { + 'X-Custom-Header': 'custom-value', + }; + + axios.get.mockResolvedValue({ + data: { + data: [{ id: 'claude-3' }], + }, + }); + + await fetchModels({ + user: 'user123', + apiKey: 'test-anthropic-key', + baseURL: 'https://api.anthropic.com/v1', + name: EModelEndpoint.anthropic, + headers: customHeaders, + }); + + expect(axios.get).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + headers: { + 'x-api-key': 'test-anthropic-key', + 'anthropic-version': expect.any(String), + }, + }), + ); + }); }); describe('getGoogleModels', () => {