mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
* feat: Add conversation ID support to custom endpoint headers
- Add LIBRECHAT_CONVERSATION_ID to customUserVars when provided
- Pass conversation ID to header resolution for dynamic headers
- Add comprehensive test coverage
Enables custom endpoints to access conversation context using {{LIBRECHAT_CONVERSATION_ID}} placeholder.
* fix: filter out unresolved placeholders from headers (thanks @MrunmayS)
* feat: add support for request body placeholders in custom endpoint headers
- Add {{LIBRECHAT_BODY_*}} placeholders for conversationId, parentMessageId, messageId
- Update tests to reflect new body placeholder functionality
* refactor resolveHeaders
* style: minor styling cleanup
* fix: type error in unit test
* feat: add body to other endpoints
* feat: add body for mcp tool calls
* chore: remove changes that unnecessarily increase scope after clarification of requirements
* refactor: move http.ts to packages/api and have RequestBody intersect with Express request body
* refactor: processMCPEnv now uses single object argument pattern
* refactor: update processMCPEnv to use 'options' parameter and align types across MCP connection classes
* feat: enhance MCP connection handling with dynamic request headers to pass request body fields
---------
Co-authored-by: Gopal Sharma <gopalsharma@gopal.sharma1>
Co-authored-by: s10gopal <36487439+s10gopal@users.noreply.github.com>
Co-authored-by: Dustin Healy <dustinhealy1@gmail.com>
94 lines
2.8 KiB
JavaScript
94 lines
2.8 KiB
JavaScript
const initializeClient = require('./initialize');
|
|
|
|
jest.mock('@librechat/api', () => ({
|
|
resolveHeaders: jest.fn(),
|
|
getOpenAIConfig: jest.fn(),
|
|
createHandleLLMNewToken: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('librechat-data-provider', () => ({
|
|
CacheKeys: { TOKEN_CONFIG: 'token_config' },
|
|
ErrorTypes: { NO_USER_KEY: 'NO_USER_KEY', NO_BASE_URL: 'NO_BASE_URL' },
|
|
envVarRegex: /\$\{([^}]+)\}/,
|
|
FetchTokenConfig: {},
|
|
extractEnvVariable: jest.fn((value) => value),
|
|
}));
|
|
|
|
jest.mock('@librechat/agents', () => ({
|
|
Providers: { OLLAMA: 'ollama' },
|
|
}));
|
|
|
|
jest.mock('~/server/services/UserService', () => ({
|
|
getUserKeyValues: jest.fn(),
|
|
checkUserKeyExpiry: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('~/server/services/Config', () => ({
|
|
getCustomEndpointConfig: jest.fn().mockResolvedValue({
|
|
apiKey: 'test-key',
|
|
baseURL: 'https://test.com',
|
|
headers: { 'x-user': '{{LIBRECHAT_USER_ID}}', 'x-email': '{{LIBRECHAT_USER_EMAIL}}' },
|
|
models: { default: ['test-model'] },
|
|
}),
|
|
}));
|
|
|
|
jest.mock('~/server/services/ModelService', () => ({
|
|
fetchModels: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('~/app/clients/OpenAIClient', () => {
|
|
return jest.fn().mockImplementation(() => ({
|
|
options: {},
|
|
}));
|
|
});
|
|
|
|
jest.mock('~/server/utils', () => ({
|
|
isUserProvided: jest.fn().mockReturnValue(false),
|
|
}));
|
|
|
|
jest.mock('~/cache/getLogStores', () =>
|
|
jest.fn().mockReturnValue({
|
|
get: jest.fn(),
|
|
}),
|
|
);
|
|
|
|
describe('custom/initializeClient', () => {
|
|
const mockRequest = {
|
|
body: { endpoint: 'test-endpoint' },
|
|
user: { id: 'user-123', email: 'test@example.com' },
|
|
app: { locals: {} },
|
|
};
|
|
const mockResponse = {};
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
it('calls resolveHeaders with headers, user, and body for body placeholder support', async () => {
|
|
const { resolveHeaders } = require('@librechat/api');
|
|
await initializeClient({ req: mockRequest, res: mockResponse, optionsOnly: true });
|
|
expect(resolveHeaders).toHaveBeenCalledWith({
|
|
headers: { 'x-user': '{{LIBRECHAT_USER_ID}}', 'x-email': '{{LIBRECHAT_USER_EMAIL}}' },
|
|
user: { id: 'user-123', email: 'test@example.com' },
|
|
body: { endpoint: 'test-endpoint' }, // body - supports {{LIBRECHAT_BODY_*}} placeholders
|
|
});
|
|
});
|
|
|
|
it('throws if endpoint config is missing', async () => {
|
|
const { getCustomEndpointConfig } = require('~/server/services/Config');
|
|
getCustomEndpointConfig.mockResolvedValueOnce(null);
|
|
await expect(
|
|
initializeClient({ req: mockRequest, res: mockResponse, optionsOnly: true }),
|
|
).rejects.toThrow('Config not found for the test-endpoint custom endpoint.');
|
|
});
|
|
|
|
it('throws if user is missing', async () => {
|
|
await expect(
|
|
initializeClient({
|
|
req: { ...mockRequest, user: undefined },
|
|
res: mockResponse,
|
|
optionsOnly: true,
|
|
}),
|
|
).rejects.toThrow("Cannot read properties of undefined (reading 'id')");
|
|
});
|
|
});
|