🧹 chore: Clean Up Config Fields (#12537)

* chore: remove unused `interface.endpointsMenu` config field

* chore: address review — restore JSDoc UI-only example, add Zod strip test

* chore: remove unused `interface.sidePanel` config field

* chore: restrict fileStrategy/fileStrategies schema to valid storage backends

* fix: use valid FileStorage value in AppService test

* chore: address review — version bump, exhaustiveness guard, JSDoc, configSchema test

* chore: remove debug logger.log from MessageIcon render path

* fix: rewrite MessageIcon render tests to use render counting instead of logger spying

* chore: bump librechat-data-provider to 0.8.407

* chore: sync example YAML version to 1.3.7
This commit is contained in:
Danny Avila 2026-04-03 12:22:58 -04:00 committed by GitHub
parent b4d97bd888
commit ea28dbfa89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 211 additions and 190 deletions

View file

@ -54,7 +54,7 @@ function createHandlers(overrides = {}) {
toggleConfigActive: jest.fn().mockResolvedValue({ _id: 'c1', isActive: false }),
hasConfigCapability: jest.fn().mockResolvedValue(true),
getAppConfig: jest.fn().mockResolvedValue({ interface: { endpointsMenu: true } }),
getAppConfig: jest.fn().mockResolvedValue({ interface: { modelSelect: true } }),
...overrides,
};
const handlers = createAdminConfigHandlers(deps);
@ -133,7 +133,7 @@ describe('createAdminConfigHandlers', () => {
});
const req = mockReq({
params: { principalType: 'role', principalId: 'admin' },
body: { overrides: { interface: { endpointsMenu: false } } },
body: { overrides: { interface: { modelSelect: false } } },
});
const res = mockRes();
@ -148,7 +148,7 @@ describe('createAdminConfigHandlers', () => {
});
const req = mockReq({
params: { principalType: 'role', principalId: 'admin' },
body: { overrides: { interface: { endpointsMenu: false } } },
body: { overrides: { interface: { modelSelect: false } } },
});
const res = mockRes();
@ -178,7 +178,7 @@ describe('createAdminConfigHandlers', () => {
params: { principalType: 'role', principalId: 'admin' },
body: {
overrides: {
interface: { endpointsMenu: false, prompts: false, agents: { use: false } },
interface: { modelSelect: false, prompts: false, agents: { use: false } },
},
},
});
@ -188,7 +188,7 @@ describe('createAdminConfigHandlers', () => {
expect(res.statusCode).toBe(201);
const savedOverrides = deps.upsertConfig.mock.calls[0][3];
expect(savedOverrides.interface).toEqual({ endpointsMenu: false });
expect(savedOverrides.interface).toEqual({ modelSelect: false });
});
it('preserves UI sub-keys in composite permission fields like mcpServers', async () => {
@ -263,17 +263,13 @@ describe('createAdminConfigHandlers', () => {
const { handlers, deps } = createHandlers();
const req = mockReq({
params: { principalType: 'role', principalId: 'admin' },
query: { fieldPath: 'interface.endpointsMenu' },
query: { fieldPath: 'interface.modelSelect' },
});
const res = mockRes();
await handlers.deleteConfigField(req, res);
expect(deps.unsetConfigField).toHaveBeenCalledWith(
'role',
'admin',
'interface.endpointsMenu',
);
expect(deps.unsetConfigField).toHaveBeenCalledWith('role', 'admin', 'interface.modelSelect');
});
it('allows deleting mcpServers UI sub-key paths', async () => {
@ -343,18 +339,14 @@ describe('createAdminConfigHandlers', () => {
const { handlers, deps } = createHandlers();
const req = mockReq({
params: { principalType: 'role', principalId: 'admin' },
query: { fieldPath: 'interface.endpointsMenu' },
query: { fieldPath: 'interface.modelSelect' },
});
const res = mockRes();
await handlers.deleteConfigField(req, res);
expect(res.statusCode).toBe(200);
expect(deps.unsetConfigField).toHaveBeenCalledWith(
'role',
'admin',
'interface.endpointsMenu',
);
expect(deps.unsetConfigField).toHaveBeenCalledWith('role', 'admin', 'interface.modelSelect');
});
it('returns 400 when fieldPath query param is missing', async () => {
@ -407,7 +399,7 @@ describe('createAdminConfigHandlers', () => {
params: { principalType: 'role', principalId: 'admin' },
body: {
entries: [
{ fieldPath: 'interface.endpointsMenu', value: false },
{ fieldPath: 'interface.modelSelect', value: false },
{ fieldPath: 'interface.prompts', value: false },
],
},
@ -418,7 +410,7 @@ describe('createAdminConfigHandlers', () => {
expect(res.statusCode).toBe(200);
const patchedFields = deps.patchConfigFields.mock.calls[0][3];
expect(patchedFields['interface.endpointsMenu']).toBe(false);
expect(patchedFields['interface.modelSelect']).toBe(false);
expect(patchedFields['interface.prompts']).toBeUndefined();
});
@ -632,21 +624,21 @@ describe('createAdminConfigHandlers', () => {
name: 'upsertConfigOverrides',
reqOverrides: {
params: { principalType: 'role', principalId: 'admin' },
body: { overrides: { interface: { endpointsMenu: false } } },
body: { overrides: { interface: { modelSelect: false } } },
},
},
{
name: 'patchConfigField',
reqOverrides: {
params: { principalType: 'role', principalId: 'admin' },
body: { entries: [{ fieldPath: 'interface.endpointsMenu', value: false }] },
body: { entries: [{ fieldPath: 'interface.modelSelect', value: false }] },
},
},
{
name: 'deleteConfigField',
reqOverrides: {
params: { principalType: 'role', principalId: 'admin' },
query: { fieldPath: 'interface.endpointsMenu' },
query: { fieldPath: 'interface.modelSelect' },
},
},
{
@ -775,7 +767,7 @@ describe('createAdminConfigHandlers', () => {
await handlers.getBaseConfig(req, res);
expect(res.statusCode).toBe(200);
expect(res.body!.config).toEqual({ interface: { endpointsMenu: true } });
expect(res.body!.config).toEqual({ interface: { modelSelect: true } });
});
});
});

View file

@ -2,7 +2,7 @@ import { isValidFieldPath, getTopLevelSection } from './config';
describe('isValidFieldPath', () => {
it('accepts simple dot paths', () => {
expect(isValidFieldPath('interface.endpointsMenu')).toBe(true);
expect(isValidFieldPath('interface.modelSelect')).toBe(true);
expect(isValidFieldPath('registration.socialLogins')).toBe(true);
expect(isValidFieldPath('a')).toBe(true);
expect(isValidFieldPath('a.b.c.d')).toBe(true);
@ -47,7 +47,7 @@ describe('isValidFieldPath', () => {
describe('getTopLevelSection', () => {
it('returns first segment of a dot path', () => {
expect(getTopLevelSection('interface.endpointsMenu')).toBe('interface');
expect(getTopLevelSection('interface.modelSelect')).toBe('interface');
expect(getTopLevelSection('registration.socialLogins.github')).toBe('registration');
});

View file

@ -40,7 +40,7 @@ export function getTopLevelSection(fieldPath: string): string {
* - `"interface.mcpServers.use"` true (permission sub-key)
* - `"interface.mcpServers.placeholder"` false (UI-only sub-key)
* - `"interface.peoplePicker.users"` true (all peoplePicker sub-keys are permissions)
* - `"interface.endpointsMenu"` false (UI-only field)
* - `"interface.modelSelect"` false (UI-only field)
*/
function isInterfacePermissionPath(fieldPath: string): boolean {
const parts = fieldPath.split('.');

View file

@ -85,7 +85,7 @@ describe('AppService', () => {
it('should correctly assign process.env and initialize app config based on custom config', async () => {
const config: Partial<TCustomConfig> = {
registration: { socialLogins: ['testLogin'] },
fileStrategy: 'testStrategy' as FileSources,
fileStrategy: FileSources.s3,
balance: {
enabled: true,
},
@ -93,22 +93,20 @@ describe('AppService', () => {
const result = await AppService({ config, systemTools: mockSystemTools });
expect(process.env.CDN_PROVIDER).toEqual('testStrategy');
expect(process.env.CDN_PROVIDER).toEqual('s3');
expect(result).toEqual(
expect.objectContaining({
config: expect.objectContaining({
fileStrategy: 'testStrategy',
fileStrategy: 's3',
}),
registration: expect.objectContaining({
socialLogins: ['testLogin'],
}),
fileStrategy: 'testStrategy',
fileStrategy: 's3',
interfaceConfig: expect.objectContaining({
endpointsMenu: true,
modelSelect: true,
parameters: true,
sidePanel: true,
presets: true,
}),
mcpConfig: null,

View file

@ -192,16 +192,13 @@ export function checkInterfaceConfig(appConfig: AppConfig) {
if (i === 0) i++;
}
// warn about config.modelSpecs.enforce if true and if any of these, endpointsMenu, modelSelect, presets, or parameters are enabled, that enforcing model specs can conflict with these options.
// warn about config.modelSpecs.enforce if true and if any of these, modelSelect, presets, or parameters are enabled, that enforcing model specs can conflict with these options.
if (
appConfig?.modelSpecs?.enforce &&
(interfaceConfig?.endpointsMenu ||
interfaceConfig?.modelSelect ||
interfaceConfig?.presets ||
interfaceConfig?.parameters)
(interfaceConfig?.modelSelect || interfaceConfig?.presets || interfaceConfig?.parameters)
) {
logger.warn(
"Note: Enforcing model specs can conflict with the interface options: endpointsMenu, modelSelect, presets, and parameters. It's recommended to disable these options from the interface or disable enforcing model specs.",
"Note: Enforcing model specs can conflict with the interface options: modelSelect, presets, and parameters. It's recommended to disable these options from the interface or disable enforcing model specs.",
);
if (i === 0) i++;
}

View file

@ -10,7 +10,7 @@ const createTestAppConfig = (overrides: Partial<AppConfig> = {}): AppConfig => {
version: '1.0.0',
cache: true,
interface: {
endpointsMenu: true,
modelSelect: true,
},
registration: {
socialLogins: [],

View file

@ -34,7 +34,7 @@ function createMockCache(namespace = 'app_config') {
function createDeps(overrides = {}) {
const cache = createMockCache();
const baseConfig = { interfaceConfig: { endpointsMenu: true }, endpoints: ['openAI'] };
const baseConfig = { interfaceConfig: { modelSelect: true }, endpoints: ['openAI'] };
return {
loadBaseConfig: jest.fn().mockResolvedValue(baseConfig),
@ -79,7 +79,7 @@ describe('createAppConfigService', () => {
getApplicableConfigs: jest
.fn()
.mockResolvedValue([
{ priority: 10, overrides: { interface: { endpointsMenu: false } }, isActive: true },
{ priority: 10, overrides: { interface: { modelSelect: false } }, isActive: true },
]),
});
const { getAppConfig } = createAppConfigService(deps);
@ -125,7 +125,7 @@ describe('createAppConfigService', () => {
getApplicableConfigs: jest
.fn()
.mockResolvedValue([
{ priority: 10, overrides: { interface: { endpointsMenu: false } }, isActive: true },
{ priority: 10, overrides: { interface: { modelSelect: false } }, isActive: true },
]),
});
const { getAppConfig } = createAppConfigService(deps);
@ -133,7 +133,7 @@ describe('createAppConfigService', () => {
const config = await getAppConfig({ role: 'ADMIN' });
const merged = config as TestConfig;
expect(merged.interfaceConfig?.endpointsMenu).toBe(false);
expect(merged.interfaceConfig?.modelSelect).toBe(false);
expect(merged.endpoints).toEqual(['openAI']);
});