mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-24 04:10:15 +01:00
🧠 feat: Prompt caching switch, prompt query params; refactor: static cache, prompt/markdown styling, trim copied code, switch new chat to convo URL (#3784)
* refactor: Update staticCache to use oneDayInSeconds for sMaxAge and maxAge * refactor: role updates * style: first pass cursor * style: Update nested list styles in style.css * feat: setIsSubmitting to true in message handler to prevent edge case where submitting turns false during message stream * feat: Add logic to redirect to conversation page after creating a new conversation * refactor: Trim code string before copying in CodeBlock component * feat: configSchema bookmarks and presets defaults * feat: Update loadDefaultInterface to handle undefined config * refactor: use for compression check * feat: first pass, query params * fix: styling issues for prompt cards * feat: anthropic prompt caching UI switch * chore: Update static file cache control defaults/comments in .env.example * ci: fix tests * ci: fix tests * chore: use "submitting" class server error connection suspense fallback
This commit is contained in:
parent
bd701c197e
commit
5694ad4e55
31 changed files with 519 additions and 112 deletions
|
|
@ -16,9 +16,9 @@ const validateImageRequest = require('./middleware/validateImageRequest');
|
|||
const errorController = require('./controllers/ErrorController');
|
||||
const configureSocialLogins = require('./socialLogins');
|
||||
const AppService = require('./services/AppService');
|
||||
const staticCache = require('./utils/staticCache');
|
||||
const noIndex = require('./middleware/noIndex');
|
||||
const routes = require('./routes');
|
||||
const staticCache = require('./utils/staticCache');
|
||||
|
||||
const { PORT, HOST, ALLOW_SOCIAL_LOGIN, DISABLE_COMPRESSION } = process.env ?? {};
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ const startServer = async () => {
|
|||
app.set('trust proxy', 1); /* trust first proxy */
|
||||
app.use(cors());
|
||||
|
||||
if (DISABLE_COMPRESSION !== 'true') {
|
||||
if (!isEnabled(DISABLE_COMPRESSION)) {
|
||||
app.use(compression());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
jest.mock('~/models/Role', () => ({
|
||||
initializeRoles: jest.fn(),
|
||||
updatePromptsAccess: jest.fn(),
|
||||
updateAccessPermissions: jest.fn(),
|
||||
getRoleByName: jest.fn(),
|
||||
updateRoleByName: jest.fn(),
|
||||
}));
|
||||
|
|
@ -30,7 +30,7 @@ jest.mock('./start/checks', () => ({
|
|||
const AppService = require('./AppService');
|
||||
const { loadDefaultInterface } = require('./start/interface');
|
||||
|
||||
describe('AppService interface.prompts configuration', () => {
|
||||
describe('AppService interface configuration', () => {
|
||||
let app;
|
||||
let mockLoadCustomConfig;
|
||||
|
||||
|
|
@ -41,33 +41,47 @@ describe('AppService interface.prompts configuration', () => {
|
|||
mockLoadCustomConfig = require('./Config/loadCustomConfig');
|
||||
});
|
||||
|
||||
it('should set prompts to true when loadDefaultInterface returns true', async () => {
|
||||
it('should set prompts and bookmarks to true when loadDefaultInterface returns true for both', async () => {
|
||||
mockLoadCustomConfig.mockResolvedValue({});
|
||||
loadDefaultInterface.mockResolvedValue({ prompts: true });
|
||||
loadDefaultInterface.mockResolvedValue({ prompts: true, bookmarks: true });
|
||||
|
||||
await AppService(app);
|
||||
|
||||
expect(app.locals.interfaceConfig.prompts).toBe(true);
|
||||
expect(app.locals.interfaceConfig.bookmarks).toBe(true);
|
||||
expect(loadDefaultInterface).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set prompts to false when loadDefaultInterface returns false', async () => {
|
||||
mockLoadCustomConfig.mockResolvedValue({ interface: { prompts: false } });
|
||||
loadDefaultInterface.mockResolvedValue({ prompts: false });
|
||||
it('should set prompts and bookmarks to false when loadDefaultInterface returns false for both', async () => {
|
||||
mockLoadCustomConfig.mockResolvedValue({ interface: { prompts: false, bookmarks: false } });
|
||||
loadDefaultInterface.mockResolvedValue({ prompts: false, bookmarks: false });
|
||||
|
||||
await AppService(app);
|
||||
|
||||
expect(app.locals.interfaceConfig.prompts).toBe(false);
|
||||
expect(app.locals.interfaceConfig.bookmarks).toBe(false);
|
||||
expect(loadDefaultInterface).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not set prompts when loadDefaultInterface returns undefined', async () => {
|
||||
it('should not set prompts and bookmarks when loadDefaultInterface returns undefined for both', async () => {
|
||||
mockLoadCustomConfig.mockResolvedValue({});
|
||||
loadDefaultInterface.mockResolvedValue({});
|
||||
|
||||
await AppService(app);
|
||||
|
||||
expect(app.locals.interfaceConfig.prompts).toBeUndefined();
|
||||
expect(app.locals.interfaceConfig.bookmarks).toBeUndefined();
|
||||
expect(loadDefaultInterface).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set prompts and bookmarks to different values when loadDefaultInterface returns different values', async () => {
|
||||
mockLoadCustomConfig.mockResolvedValue({ interface: { prompts: true, bookmarks: false } });
|
||||
loadDefaultInterface.mockResolvedValue({ prompts: true, bookmarks: false });
|
||||
|
||||
await AppService(app);
|
||||
|
||||
expect(app.locals.interfaceConfig.prompts).toBe(true);
|
||||
expect(app.locals.interfaceConfig.bookmarks).toBe(false);
|
||||
expect(loadDefaultInterface).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ jest.mock('./Files/Firebase/initialize', () => ({
|
|||
}));
|
||||
jest.mock('~/models/Role', () => ({
|
||||
initializeRoles: jest.fn(),
|
||||
updatePromptsAccess: jest.fn(),
|
||||
updateBookmarksAccess: jest.fn(),
|
||||
updateAccessPermissions: jest.fn(),
|
||||
}));
|
||||
jest.mock('./ToolService', () => ({
|
||||
loadAndFormatTools: jest.fn().mockReturnValue({
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const buildOptions = (endpoint, parsedBody) => {
|
|||
promptPrefix,
|
||||
maxContextTokens,
|
||||
resendFiles = true,
|
||||
promptCache = true,
|
||||
iconURL,
|
||||
greeting,
|
||||
spec,
|
||||
|
|
@ -17,6 +18,7 @@ const buildOptions = (endpoint, parsedBody) => {
|
|||
modelLabel,
|
||||
promptPrefix,
|
||||
resendFiles,
|
||||
promptCache,
|
||||
iconURL,
|
||||
greeting,
|
||||
spec,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
const { SystemRoles, Permissions, removeNullishValues } = require('librechat-data-provider');
|
||||
const { updatePromptsAccess, updateBookmarksAccess } = require('~/models/Role');
|
||||
const {
|
||||
SystemRoles,
|
||||
Permissions,
|
||||
PermissionTypes,
|
||||
removeNullishValues,
|
||||
} = require('librechat-data-provider');
|
||||
const { updateAccessPermissions } = require('~/models/Role');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
|
|
@ -28,8 +33,10 @@ async function loadDefaultInterface(config, configDefaults, roleName = SystemRol
|
|||
prompts: interfaceConfig?.prompts ?? defaults.prompts,
|
||||
});
|
||||
|
||||
await updatePromptsAccess(roleName, { [Permissions.USE]: loadedInterface.prompts });
|
||||
await updateBookmarksAccess(roleName, { [Permissions.USE]: loadedInterface.bookmarks });
|
||||
await updateAccessPermissions(roleName, {
|
||||
[PermissionTypes.PROMPTS]: { [Permissions.USE]: loadedInterface.prompts },
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: loadedInterface.bookmarks },
|
||||
});
|
||||
|
||||
let i = 0;
|
||||
const logSettings = () => {
|
||||
|
|
|
|||
|
|
@ -1,52 +1,81 @@
|
|||
const { SystemRoles, Permissions } = require('librechat-data-provider');
|
||||
const { updatePromptsAccess } = require('~/models/Role');
|
||||
const { SystemRoles, Permissions, PermissionTypes } = require('librechat-data-provider');
|
||||
const { updateAccessPermissions } = require('~/models/Role');
|
||||
const { loadDefaultInterface } = require('./interface');
|
||||
|
||||
jest.mock('~/models/Role', () => ({
|
||||
updatePromptsAccess: jest.fn(),
|
||||
updateBookmarksAccess: jest.fn(),
|
||||
updateAccessPermissions: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('loadDefaultInterface', () => {
|
||||
it('should call updatePromptsAccess with the correct parameters when prompts is true', async () => {
|
||||
const config = { interface: { prompts: true } };
|
||||
it('should call updateAccessPermissions with the correct parameters when prompts and bookmarks are true', async () => {
|
||||
const config = { interface: { prompts: true, bookmarks: true } };
|
||||
const configDefaults = { interface: {} };
|
||||
|
||||
await loadDefaultInterface(config, configDefaults);
|
||||
|
||||
expect(updatePromptsAccess).toHaveBeenCalledWith(SystemRoles.USER, { [Permissions.USE]: true });
|
||||
});
|
||||
|
||||
it('should call updatePromptsAccess with false when prompts is false', async () => {
|
||||
const config = { interface: { prompts: false } };
|
||||
const configDefaults = { interface: {} };
|
||||
|
||||
await loadDefaultInterface(config, configDefaults);
|
||||
|
||||
expect(updatePromptsAccess).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[Permissions.USE]: false,
|
||||
expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
});
|
||||
});
|
||||
|
||||
it('should call updatePromptsAccess with undefined when prompts is not specified in config', async () => {
|
||||
it('should call updateAccessPermissions with false when prompts and bookmarks are false', async () => {
|
||||
const config = { interface: { prompts: false, bookmarks: false } };
|
||||
const configDefaults = { interface: {} };
|
||||
|
||||
await loadDefaultInterface(config, configDefaults);
|
||||
|
||||
expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false },
|
||||
});
|
||||
});
|
||||
|
||||
it('should call updateAccessPermissions with undefined when prompts and bookmarks are not specified in config', async () => {
|
||||
const config = {};
|
||||
const configDefaults = { interface: {} };
|
||||
|
||||
await loadDefaultInterface(config, configDefaults);
|
||||
|
||||
expect(updatePromptsAccess).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[Permissions.USE]: undefined,
|
||||
expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined },
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined },
|
||||
});
|
||||
});
|
||||
|
||||
it('should call updatePromptsAccess with undefined when prompts is explicitly undefined', async () => {
|
||||
const config = { interface: { prompts: undefined } };
|
||||
it('should call updateAccessPermissions with undefined when prompts and bookmarks are explicitly undefined', async () => {
|
||||
const config = { interface: { prompts: undefined, bookmarks: undefined } };
|
||||
const configDefaults = { interface: {} };
|
||||
|
||||
await loadDefaultInterface(config, configDefaults);
|
||||
|
||||
expect(updatePromptsAccess).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[Permissions.USE]: undefined,
|
||||
expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined },
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined },
|
||||
});
|
||||
});
|
||||
|
||||
it('should call updateAccessPermissions with mixed values for prompts and bookmarks', async () => {
|
||||
const config = { interface: { prompts: true, bookmarks: false } };
|
||||
const configDefaults = { interface: {} };
|
||||
|
||||
await loadDefaultInterface(config, configDefaults);
|
||||
|
||||
expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false },
|
||||
});
|
||||
});
|
||||
|
||||
it('should call updateAccessPermissions with true when config is undefined', async () => {
|
||||
const config = undefined;
|
||||
const configDefaults = { interface: { prompts: true, bookmarks: true } };
|
||||
|
||||
await loadDefaultInterface(config, configDefaults);
|
||||
|
||||
expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
const express = require('express');
|
||||
|
||||
const oneWeekInSeconds = 24 * 60 * 60 * 7;
|
||||
const oneDayInSeconds = 24 * 60 * 60;
|
||||
|
||||
const sMaxAge = process.env.STATIC_CACHE_S_MAX_AGE || oneWeekInSeconds;
|
||||
const maxAge = process.env.STATIC_CACHE_MAX_AGE || oneWeekInSeconds * 4;
|
||||
const sMaxAge = process.env.STATIC_CACHE_S_MAX_AGE || oneDayInSeconds;
|
||||
const maxAge = process.env.STATIC_CACHE_MAX_AGE || oneDayInSeconds * 2;
|
||||
|
||||
const staticCache = (staticPath) =>
|
||||
express.static(staticPath, {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue