From 69c6d023e194b16faca02db9b1a90f750083acaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anthony=20Qu=C3=A9r=C3=A9?= <47711333+Anthony-Jhoiro@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:49:51 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=A8=20feat:=20Pass=20Custom=20Headers?= =?UTF-8?q?=20to=20Model=20Discovery=20(`v1/models`)=20(#10564)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/server/services/ModelService.js | 4 +- api/server/services/ModelService.spec.js | 122 +++++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) 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', () => {