mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-04-05 23:37:19 +02:00
🧹 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:
parent
b4d97bd888
commit
ea28dbfa89
17 changed files with 211 additions and 190 deletions
|
|
@ -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 } });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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('.');
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const createTestAppConfig = (overrides: Partial<AppConfig> = {}): AppConfig => {
|
|||
version: '1.0.0',
|
||||
cache: true,
|
||||
interface: {
|
||||
endpointsMenu: true,
|
||||
modelSelect: true,
|
||||
},
|
||||
registration: {
|
||||
socialLogins: [],
|
||||
|
|
|
|||
|
|
@ -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']);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue