diff --git a/packages/api/src/endpoints/custom/initialize.spec.ts b/packages/api/src/endpoints/custom/initialize.spec.ts index e2266f936e..b707db88a3 100644 --- a/packages/api/src/endpoints/custom/initialize.spec.ts +++ b/packages/api/src/endpoints/custom/initialize.spec.ts @@ -407,14 +407,17 @@ describe('initializeCustom – native Anthropic provider', () => { mockGetCustomEndpointConfig.mockReturnValue(config); return { req: { - user: { id: 'user-1' }, + user: { id: 'user-1', email: 'user@example.com' }, body: { conversationId: 'convo-1' }, config: {}, } as unknown as BaseInitializeParams['req'], endpoint: 'Claude-Compatible', model_parameters, db: { - getUserKeyValues: jest.fn(), + getUserKeyValues: jest.fn().mockResolvedValue({ + apiKey: 'sk-user-key', + baseURL: 'https://user-controlled.example.com', + }), getUserKey: jest.fn(), } as unknown as BaseInitializeParams['db'], }; @@ -446,6 +449,36 @@ describe('initializeCustom – native Anthropic provider', () => { expect(options.useLegacyContent).toBeUndefined(); }); + it('withholds configured headers when the user supplies the base URL', async () => { + const params = createAnthropicParams({ + provider: 'anthropic', + apiKey: 'sk-ant-custom', + baseURL: AuthType.USER_PROVIDED, + headers: { + Authorization: 'Bearer ${GATEWAY_SECRET}', + 'X-User-Email': '{{LIBRECHAT_USER_EMAIL}}', + }, + models: { default: ['claude-sonnet-4-5'] }, + }); + + const options = await initializeCustom(params); + + expect(mockValidateEndpointURL).toHaveBeenCalledWith( + 'https://user-controlled.example.com', + 'Claude-Compatible', + undefined, + ); + expect(options.llmConfig).toHaveProperty( + 'anthropicApiUrl', + 'https://user-controlled.example.com', + ); + const defaultHeaders = ( + options.llmConfig as { clientOptions?: { defaultHeaders?: Record } } + ).clientOptions?.defaultHeaders; + expect(defaultHeaders?.Authorization).toBeUndefined(); + expect(defaultHeaders?.['X-User-Email']).toBeUndefined(); + }); + it('applies customParams.paramDefinitions defaults on the native path', async () => { const params = createAnthropicParams({ provider: 'anthropic', diff --git a/packages/api/src/endpoints/custom/initialize.ts b/packages/api/src/endpoints/custom/initialize.ts index 59f3aacf7e..8840f86d92 100644 --- a/packages/api/src/endpoints/custom/initialize.ts +++ b/packages/api/src/endpoints/custom/initialize.ts @@ -111,17 +111,19 @@ function buildAnthropicCustomConfig({ baseURL, modelOptions, endpointConfig, + userProvidesURL, }: { apiKey: string; baseURL: string; modelOptions: AnthropicModelOptions; endpointConfig: Partial; + userProvidesURL: boolean; }): InitializeResultBase { const result = getAnthropicLLMConfig(apiKey, { modelOptions, proxy: PROXY ?? undefined, reverseProxyUrl: baseURL, - headers: endpointConfig.headers, + headers: userProvidesURL ? undefined : endpointConfig.headers, addParams: endpointConfig.addParams, dropParams: endpointConfig.dropParams, /** Apply admin `customParams.paramDefinitions` defaults (e.g. promptCache, @@ -289,6 +291,7 @@ export async function initializeCustom({ baseURL, modelOptions: modelOptions as AnthropicModelOptions, endpointConfig, + userProvidesURL, }); options.endpointTokenConfig = endpointTokenConfig; } else {