mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
* 🔧 chore: Add missing optional `scraperTimeout` to webSearchSchema
* chore: Add missing optional `scraperTimeout` to web search authentication result
* chore: linting
* feat: Integrate attachment handling and citation processing in message components
- Added `useAttachments` hook to manage message attachments and search results.
- Updated `MessageParts`, `ContentParts`, and `ContentRender` components to utilize the new hook for improved attachment handling.
- Enhanced `useCopyToClipboard` to format citations correctly, including support for composite citations and deduplication.
- Introduced utility functions for citation processing and cleanup.
- Added tests for the new `useCopyToClipboard` functionality to ensure proper citation formatting and handling.
* feat: Add configuration for LibreChat Code Interpreter API and Web Search variables
* fix: Update searchResults type to use SearchResultData for better type safety
* feat: Add web search configuration validation and logging
- Introduced `checkWebSearchConfig` function to validate web search configuration values, ensuring they are environment variable references.
- Added logging for proper configuration and warnings for incorrect values.
- Created unit tests for `checkWebSearchConfig` to cover various scenarios, including valid and invalid configurations.
* docs: Update README to include Web Search feature details
- Added a section for the Web Search feature, highlighting its capabilities to search the internet and enhance AI context.
- Included links for further information on the Web Search functionality.
* ci: Add mock for checkWebSearchConfig in AppService tests
* chore: linting
* feat: Enhance Shared Messages with Web Search UI by adding searchResults prop to SearchContent and MinimalHoverButtons components
* chore: linting
* refactor: remove Meilisearch index sync from importConversations function
* feat: update safeSearch implementation to use SafeSearchTypes enum
* refactor: remove commented-out code in loadTools function
* fix: ensure responseMessageId handles latestMessage ID correctly
* feat: enhance Vite configuration for improved chunking and caching
- Added additional globIgnores for map files in Workbox configuration.
- Implemented high-impact chunking for various large libraries to optimize performance.
- Increased chunkSizeWarningLimit from 1200 to 1500 for better handling of larger chunks.
* refactor: move health check hook to Root, fix bad setState for Temporary state
- Enhanced the `useHealthCheck` hook to initiate health checks only when the user is authenticated.
- Added logic for managing health check intervals and handling window focus events.
- Introduced a new test suite for `useHealthCheck` to cover various scenarios including authentication state changes and error handling.
- Removed the health check invocation from `ChatRoute` and added it to `Root` for global health monitoring.
* fix: update font alias in Vite configuration for correct path resolution
203 lines
6 KiB
JavaScript
203 lines
6 KiB
JavaScript
// Mock librechat-data-provider
|
|
jest.mock('librechat-data-provider', () => ({
|
|
...jest.requireActual('librechat-data-provider'),
|
|
extractVariableName: jest.fn(),
|
|
}));
|
|
|
|
// Mock the config logger
|
|
jest.mock('~/config', () => ({
|
|
logger: {
|
|
debug: jest.fn(),
|
|
warn: jest.fn(),
|
|
},
|
|
}));
|
|
|
|
const { checkWebSearchConfig } = require('./checks');
|
|
const { logger } = require('~/config');
|
|
const { extractVariableName } = require('librechat-data-provider');
|
|
|
|
describe('checkWebSearchConfig', () => {
|
|
let originalEnv;
|
|
|
|
beforeEach(() => {
|
|
// Clear all mocks
|
|
jest.clearAllMocks();
|
|
|
|
// Store original environment
|
|
originalEnv = process.env;
|
|
|
|
// Reset process.env
|
|
process.env = { ...originalEnv };
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Restore original environment
|
|
process.env = originalEnv;
|
|
});
|
|
|
|
describe('when webSearchConfig is undefined or null', () => {
|
|
it('should return early without logging when config is undefined', () => {
|
|
checkWebSearchConfig(undefined);
|
|
|
|
expect(logger.debug).not.toHaveBeenCalled();
|
|
expect(logger.warn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return early without logging when config is null', () => {
|
|
checkWebSearchConfig(null);
|
|
|
|
expect(logger.debug).not.toHaveBeenCalled();
|
|
expect(logger.warn).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('when config values are proper environment variable references', () => {
|
|
it('should log debug message for each valid environment variable with value set', () => {
|
|
const config = {
|
|
serperApiKey: '${SERPER_API_KEY}',
|
|
jinaApiKey: '${JINA_API_KEY}',
|
|
};
|
|
|
|
extractVariableName.mockReturnValueOnce('SERPER_API_KEY').mockReturnValueOnce('JINA_API_KEY');
|
|
|
|
process.env.SERPER_API_KEY = 'test-serper-key';
|
|
process.env.JINA_API_KEY = 'test-jina-key';
|
|
|
|
checkWebSearchConfig(config);
|
|
|
|
expect(extractVariableName).toHaveBeenCalledWith('${SERPER_API_KEY}');
|
|
expect(extractVariableName).toHaveBeenCalledWith('${JINA_API_KEY}');
|
|
expect(logger.debug).toHaveBeenCalledWith(
|
|
'Web search serperApiKey: Using environment variable SERPER_API_KEY with value set',
|
|
);
|
|
expect(logger.debug).toHaveBeenCalledWith(
|
|
'Web search jinaApiKey: Using environment variable JINA_API_KEY with value set',
|
|
);
|
|
expect(logger.warn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should log debug message for environment variables not set in environment', () => {
|
|
const config = {
|
|
cohereApiKey: '${COHERE_API_KEY}',
|
|
};
|
|
|
|
extractVariableName.mockReturnValue('COHERE_API_KEY');
|
|
|
|
delete process.env.COHERE_API_KEY;
|
|
|
|
checkWebSearchConfig(config);
|
|
|
|
expect(logger.debug).toHaveBeenCalledWith(
|
|
'Web search cohereApiKey: Using environment variable COHERE_API_KEY (not set in environment, user provided value)',
|
|
);
|
|
expect(logger.warn).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('when config values are actual values instead of environment variable references', () => {
|
|
it('should warn when serperApiKey contains actual API key', () => {
|
|
const config = {
|
|
serperApiKey: 'sk-1234567890abcdef',
|
|
};
|
|
|
|
extractVariableName.mockReturnValue(null);
|
|
|
|
checkWebSearchConfig(config);
|
|
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining(
|
|
'❗ Web search configuration error: serperApiKey contains an actual value',
|
|
),
|
|
);
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining('Current value: "sk-1234567..."'),
|
|
);
|
|
expect(logger.debug).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should warn when firecrawlApiUrl contains actual URL', () => {
|
|
const config = {
|
|
firecrawlApiUrl: 'https://api.firecrawl.dev',
|
|
};
|
|
|
|
extractVariableName.mockReturnValue(null);
|
|
|
|
checkWebSearchConfig(config);
|
|
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining(
|
|
'❗ Web search configuration error: firecrawlApiUrl contains an actual value',
|
|
),
|
|
);
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining('Current value: "https://ap..."'),
|
|
);
|
|
});
|
|
|
|
it('should include documentation link in warning message', () => {
|
|
const config = {
|
|
firecrawlApiKey: 'fc-actual-key',
|
|
};
|
|
|
|
extractVariableName.mockReturnValue(null);
|
|
|
|
checkWebSearchConfig(config);
|
|
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining(
|
|
'More info: https://www.librechat.ai/docs/configuration/librechat_yaml/web_search',
|
|
),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('when config contains mixed value types', () => {
|
|
it('should only process string values and ignore non-string values', () => {
|
|
const config = {
|
|
serperApiKey: '${SERPER_API_KEY}',
|
|
safeSearch: 1,
|
|
scraperTimeout: 7500,
|
|
jinaApiKey: 'actual-key',
|
|
};
|
|
|
|
extractVariableName.mockReturnValueOnce('SERPER_API_KEY').mockReturnValueOnce(null);
|
|
|
|
process.env.SERPER_API_KEY = 'test-key';
|
|
|
|
checkWebSearchConfig(config);
|
|
|
|
expect(extractVariableName).toHaveBeenCalledTimes(2);
|
|
expect(logger.debug).toHaveBeenCalledTimes(1);
|
|
expect(logger.warn).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle config with no web search keys', () => {
|
|
const config = {
|
|
someOtherKey: 'value',
|
|
anotherKey: '${SOME_VAR}',
|
|
};
|
|
|
|
checkWebSearchConfig(config);
|
|
|
|
expect(extractVariableName).not.toHaveBeenCalled();
|
|
expect(logger.debug).not.toHaveBeenCalled();
|
|
expect(logger.warn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should truncate long values in warning messages', () => {
|
|
const config = {
|
|
serperApiKey: 'this-is-a-very-long-api-key-that-should-be-truncated-in-the-warning-message',
|
|
};
|
|
|
|
extractVariableName.mockReturnValue(null);
|
|
|
|
checkWebSearchConfig(config);
|
|
|
|
expect(logger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining('Current value: "this-is-a-..."'),
|
|
);
|
|
});
|
|
});
|
|
});
|