Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions api/server/services/Config/loadCustomConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,26 +85,32 @@ Please specify a correct \`imageOutputType\` value (case-sensitive).
let errorMessage = `Invalid custom config file at ${configPath}:
${JSON.stringify(result.error, null, 2)}`;

if (i === 0) {
logger.error(errorMessage);
const speechError = result.error.errors.find(
(err) =>
err.code === 'unrecognized_keys' &&
(err.message?.includes('stt') || err.message?.includes('tts')),
);
logger.error(errorMessage);
const speechError = result.error.errors.find(
(err) =>
err.code === 'unrecognized_keys' &&
(err.message?.includes('stt') || err.message?.includes('tts')),
);

if (speechError) {
logger.warn(`
if (speechError) {
logger.warn(`
The Speech-to-text and Text-to-speech configuration format has recently changed.
If you're getting this error, please refer to the latest documentation:

https://www.librechat.ai/docs/configuration/stt_tts`);
}
}

i++;
if (process.env.CONFIG_BYPASS_VALIDATION === 'true') {
logger.warn(
'CONFIG_BYPASS_VALIDATION is enabled. Continuing with default configuration despite validation errors.',
);
return null;
}

return null;
logger.error(
'Exiting due to invalid configuration. Set CONFIG_BYPASS_VALIDATION=true to bypass this check.',
);
process.exit(1);
} else {
if (printConfig) {
logger.info('Custom config file loaded:');
Expand Down
35 changes: 35 additions & 0 deletions api/server/services/Config/loadCustomConfig.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,25 @@
const loadCustomConfig = require('./loadCustomConfig');

describe('loadCustomConfig', () => {
const originalExit = process.exit;
const mockExit = jest.fn((code) => {
throw new Error(`process.exit called with "${code}"`);
});

beforeAll(() => {
process.exit = mockExit;
});

afterAll(() => {
process.exit = originalExit;
});

beforeEach(() => {
jest.resetAllMocks();
// Re-apply the exit mock implementation after resetAllMocks
mockExit.mockImplementation((code) => {
throw new Error(`process.exit called with "${code}"`);
});
delete process.env.CONFIG_PATH;
});

Expand Down Expand Up @@ -94,20 +111,38 @@
it('should return null and log if config schema validation fails', async () => {
const invalidConfig = { invalidField: true };
process.env.CONFIG_PATH = 'invalidConfig.yaml';
process.env.CONFIG_BYPASS_VALIDATION = 'true';
loadYaml.mockReturnValueOnce(invalidConfig);

const result = await loadCustomConfig();

expect(result).toBeNull();
expect(logger.warn).toHaveBeenCalledWith(
'CONFIG_BYPASS_VALIDATION is enabled. Continuing with default configuration despite validation errors.',
);
delete process.env.CONFIG_BYPASS_VALIDATION;
});

it('should call process.exit(1) when config validation fails without bypass', async () => {
const invalidConfig = { invalidField: true };
process.env.CONFIG_PATH = 'invalidConfig.yaml';
loadYaml.mockReturnValueOnce(invalidConfig);

await expect(loadCustomConfig()).rejects.toThrow('process.exit called with "1"');
expect(logger.error).toHaveBeenCalledWith(
'Exiting due to invalid configuration. Set CONFIG_BYPASS_VALIDATION=true to bypass this check.',
);
});

it('should handle and return null on YAML parse error for a string response from remote', async () => {
process.env.CONFIG_PATH = 'http://example.com/config.yaml';
process.env.CONFIG_BYPASS_VALIDATION = 'true';
axios.get.mockResolvedValue({ data: 'invalidYAMLContent' });

const result = await loadCustomConfig();

expect(result).toBeNull();
delete process.env.CONFIG_BYPASS_VALIDATION;
});

it('should return the custom config object for a valid remote config file', async () => {
Expand Down Expand Up @@ -144,7 +179,7 @@
expect(result).toBeNull();
});

it('should not cache the config if cache is set to false', async () => {

Check warning on line 182 in api/server/services/Config/loadCustomConfig.spec.js

View workflow job for this annotation

GitHub Actions / Run ESLint Linting

Test has no assertions
const mockConfig = {
version: '1.0',
cache: false,
Expand Down