From e5d08ccdf16b4fcfff17c873970d2c96f625f9b5 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 22 Jul 2025 17:51:21 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=82=EF=B8=8F=20feat:=20Add=20File=20Se?= =?UTF-8?q?arch=20Toggle=20Permission=20for=20Chat=20Area=20Badge=20(#8605?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/server/services/start/interface.js | 3 + api/server/services/start/interface.spec.js | 135 ++++++++++++++++-- .../src/components/Chat/Input/FileSearch.tsx | 12 +- .../Chat/Input/Files/DragDropModal.tsx | 2 +- .../components/Chat/Input/ToolsDropdown.tsx | 7 +- .../SidePanel/Agents/AgentConfig.tsx | 10 +- librechat.example.yaml | 4 + packages/data-provider/src/config.ts | 2 + packages/data-provider/src/permissions.ts | 10 ++ packages/data-provider/src/roles.ts | 10 +- packages/data-schemas/src/schema/role.ts | 8 +- 11 files changed, 184 insertions(+), 19 deletions(-) diff --git a/api/server/services/start/interface.js b/api/server/services/start/interface.js index 5c08b1af2e..d8d9d2b5d2 100644 --- a/api/server/services/start/interface.js +++ b/api/server/services/start/interface.js @@ -50,6 +50,7 @@ async function loadDefaultInterface(config, configDefaults, roleName = SystemRol temporaryChat: interfaceConfig?.temporaryChat ?? defaults.temporaryChat, runCode: interfaceConfig?.runCode ?? defaults.runCode, webSearch: interfaceConfig?.webSearch ?? defaults.webSearch, + fileSearch: interfaceConfig?.fileSearch ?? defaults.fileSearch, customWelcome: interfaceConfig?.customWelcome ?? defaults.customWelcome, }); @@ -65,6 +66,7 @@ async function loadDefaultInterface(config, configDefaults, roleName = SystemRol [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: loadedInterface.temporaryChat }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: loadedInterface.runCode }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: loadedInterface.webSearch }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: loadedInterface.fileSearch }, }); await updateAccessPermissions(SystemRoles.ADMIN, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: loadedInterface.prompts }, @@ -78,6 +80,7 @@ async function loadDefaultInterface(config, configDefaults, roleName = SystemRol [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: loadedInterface.temporaryChat }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: loadedInterface.runCode }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: loadedInterface.webSearch }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: loadedInterface.fileSearch }, }); let i = 0; diff --git a/api/server/services/start/interface.spec.js b/api/server/services/start/interface.spec.js index 1a05c9cf12..e60e2730f1 100644 --- a/api/server/services/start/interface.spec.js +++ b/api/server/services/start/interface.spec.js @@ -18,6 +18,7 @@ describe('loadDefaultInterface', () => { temporaryChat: true, runCode: true, webSearch: true, + fileSearch: true, }, }; const configDefaults = { interface: {} }; @@ -27,12 +28,13 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: true }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: true }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: true, [Permissions.OPT_OUT]: undefined }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true }, [PermissionTypes.AGENTS]: { [Permissions.USE]: true }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: true }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: true }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: true }, }); }); @@ -47,6 +49,7 @@ describe('loadDefaultInterface', () => { temporaryChat: false, runCode: false, webSearch: false, + fileSearch: false, }, }; const configDefaults = { interface: {} }; @@ -56,12 +59,13 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: false }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: false }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: false, [Permissions.OPT_OUT]: undefined }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: false }, [PermissionTypes.AGENTS]: { [Permissions.USE]: false }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: false }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: false }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: false }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: false }, }); }); @@ -74,12 +78,16 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: undefined }, + [PermissionTypes.MEMORIES]: { + [Permissions.USE]: undefined, + [Permissions.OPT_OUT]: undefined, + }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: undefined }, [PermissionTypes.AGENTS]: { [Permissions.USE]: undefined }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: undefined }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: undefined }, }); }); @@ -94,6 +102,7 @@ describe('loadDefaultInterface', () => { temporaryChat: undefined, runCode: undefined, webSearch: undefined, + fileSearch: undefined, }, }; const configDefaults = { interface: {} }; @@ -103,12 +112,16 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: undefined }, + [PermissionTypes.MEMORIES]: { + [Permissions.USE]: undefined, + [Permissions.OPT_OUT]: undefined, + }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: undefined }, [PermissionTypes.AGENTS]: { [Permissions.USE]: undefined }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: undefined }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: undefined }, }); }); @@ -123,6 +136,7 @@ describe('loadDefaultInterface', () => { temporaryChat: undefined, runCode: false, webSearch: true, + fileSearch: false, }, }; const configDefaults = { interface: {} }; @@ -132,12 +146,13 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: true }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: true }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: true, [Permissions.OPT_OUT]: undefined }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: undefined }, [PermissionTypes.AGENTS]: { [Permissions.USE]: true }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: false }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: true }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: false }, }); }); @@ -153,6 +168,7 @@ describe('loadDefaultInterface', () => { temporaryChat: true, runCode: true, webSearch: true, + fileSearch: true, }, }; @@ -161,12 +177,13 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: true }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: true }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: true, [Permissions.OPT_OUT]: undefined }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true }, [PermissionTypes.AGENTS]: { [Permissions.USE]: true }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: true }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: true }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: true }, }); }); @@ -179,12 +196,16 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: undefined }, + [PermissionTypes.MEMORIES]: { + [Permissions.USE]: undefined, + [Permissions.OPT_OUT]: undefined, + }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true }, [PermissionTypes.AGENTS]: { [Permissions.USE]: undefined }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: undefined }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: undefined }, }); }); @@ -197,12 +218,16 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: undefined }, + [PermissionTypes.MEMORIES]: { + [Permissions.USE]: undefined, + [Permissions.OPT_OUT]: undefined, + }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: false }, [PermissionTypes.AGENTS]: { [Permissions.USE]: undefined }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: undefined }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: undefined }, }); }); @@ -215,12 +240,16 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: undefined }, + [PermissionTypes.MEMORIES]: { + [Permissions.USE]: undefined, + [Permissions.OPT_OUT]: undefined, + }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: undefined }, [PermissionTypes.AGENTS]: { [Permissions.USE]: undefined }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: undefined }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: undefined }, }); }); @@ -234,6 +263,7 @@ describe('loadDefaultInterface', () => { agents: false, temporaryChat: true, runCode: false, + fileSearch: true, }, }; const configDefaults = { interface: {} }; @@ -243,12 +273,13 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: true }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: true }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: true, [Permissions.OPT_OUT]: undefined }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true }, [PermissionTypes.AGENTS]: { [Permissions.USE]: false }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: false }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: true }, }); }); @@ -264,6 +295,7 @@ describe('loadDefaultInterface', () => { temporaryChat: undefined, runCode: undefined, webSearch: undefined, + fileSearch: true, }, }; @@ -272,12 +304,13 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: true }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: false }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: false, [Permissions.OPT_OUT]: undefined }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: false }, [PermissionTypes.AGENTS]: { [Permissions.USE]: undefined }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: undefined }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: true }, }); }); @@ -300,12 +333,90 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: true }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: true }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: true, [Permissions.OPT_OUT]: undefined }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true }, [PermissionTypes.AGENTS]: { [Permissions.USE]: false }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: false }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: undefined }, + }); + }); + + it('should call updateAccessPermissions with the correct parameters when FILE_SEARCH is true', async () => { + const config = { + interface: { + fileSearch: true, + }, + }; + const configDefaults = { interface: {} }; + + await loadDefaultInterface(config, configDefaults); + + expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { + [PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined }, + [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: undefined }, + [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: undefined }, + [PermissionTypes.AGENTS]: { [Permissions.USE]: undefined }, + [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, + [PermissionTypes.RUN_CODE]: { [Permissions.USE]: undefined }, + [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: true }, + }); + }); + + it('should call updateAccessPermissions with false when FILE_SEARCH is false', async () => { + const config = { + interface: { + fileSearch: false, + }, + }; + const configDefaults = { interface: {} }; + + await loadDefaultInterface(config, configDefaults); + + expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { + [PermissionTypes.PROMPTS]: { [Permissions.USE]: undefined }, + [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: undefined }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: undefined }, + [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: undefined }, + [PermissionTypes.AGENTS]: { [Permissions.USE]: undefined }, + [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: undefined }, + [PermissionTypes.RUN_CODE]: { [Permissions.USE]: undefined }, + [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: undefined }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: false }, + }); + }); + + it('should call updateAccessPermissions with all interface options including fileSearch', async () => { + const config = { + interface: { + prompts: true, + bookmarks: false, + memories: true, + multiConvo: true, + agents: false, + temporaryChat: true, + runCode: false, + webSearch: true, + fileSearch: true, + }, + }; + const configDefaults = { interface: {} }; + + await loadDefaultInterface(config, configDefaults); + + expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { + [PermissionTypes.PROMPTS]: { [Permissions.USE]: true }, + [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false }, + [PermissionTypes.MEMORIES]: { [Permissions.USE]: true, [Permissions.OPT_OUT]: undefined }, + [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true }, + [PermissionTypes.AGENTS]: { [Permissions.USE]: false }, + [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true }, + [PermissionTypes.RUN_CODE]: { [Permissions.USE]: false }, + [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: true }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: true }, }); }); }); diff --git a/client/src/components/Chat/Input/FileSearch.tsx b/client/src/components/Chat/Input/FileSearch.tsx index a4952d1fd1..4de6c35b6b 100644 --- a/client/src/components/Chat/Input/FileSearch.tsx +++ b/client/src/components/Chat/Input/FileSearch.tsx @@ -1,14 +1,24 @@ import React, { memo } from 'react'; +import { PermissionTypes, Permissions } from 'librechat-data-provider'; import CheckboxButton from '~/components/ui/CheckboxButton'; +import { useLocalize, useHasAccess } from '~/hooks'; import { useBadgeRowContext } from '~/Providers'; import { VectorIcon } from '~/components/svg'; -import { useLocalize } from '~/hooks'; function FileSearch() { const localize = useLocalize(); const { fileSearch } = useBadgeRowContext(); const { toggleState: fileSearchEnabled, debouncedChange, isPinned } = fileSearch; + const canUseFileSearch = useHasAccess({ + permissionType: PermissionTypes.FILE_SEARCH, + permission: Permissions.USE, + }); + + if (!canUseFileSearch) { + return null; + } + return ( <> {(fileSearchEnabled || isPinned) && ( diff --git a/client/src/components/Chat/Input/Files/DragDropModal.tsx b/client/src/components/Chat/Input/Files/DragDropModal.tsx index 44838271b3..f80af70def 100644 --- a/client/src/components/Chat/Input/Files/DragDropModal.tsx +++ b/client/src/components/Chat/Input/Files/DragDropModal.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; +import { ImageUpIcon, FileSearch, TerminalSquareIcon, FileType2Icon } from 'lucide-react'; import { EToolResources, defaultAgentCapabilities } from 'librechat-data-provider'; -import { FileSearch, ImageUpIcon, FileType2Icon, TerminalSquareIcon } from 'lucide-react'; import { useLocalize, useGetAgentsConfig, useAgentCapabilities } from '~/hooks'; import { OGDialog, OGDialogTemplate } from '~/components/ui'; diff --git a/client/src/components/Chat/Input/ToolsDropdown.tsx b/client/src/components/Chat/Input/ToolsDropdown.tsx index 859a7be745..56c7db1cb2 100644 --- a/client/src/components/Chat/Input/ToolsDropdown.tsx +++ b/client/src/components/Chat/Input/ToolsDropdown.tsx @@ -72,6 +72,11 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => { permission: Permissions.USE, }); + const canUseFileSearch = useHasAccess({ + permissionType: PermissionTypes.FILE_SEARCH, + permission: Permissions.USE, + }); + const showWebSearchSettings = useMemo(() => { const authTypes = webSearchAuthData?.authTypes ?? []; if (authTypes.length === 0) return true; @@ -140,7 +145,7 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => { const dropdownItems: MenuItemProps[] = []; - if (fileSearchEnabled) { + if (fileSearchEnabled && canUseFileSearch) { dropdownItems.push({ onClick: handleFileSearchToggle, hideOnClick: false, diff --git a/client/src/components/SidePanel/Agents/AgentConfig.tsx b/client/src/components/SidePanel/Agents/AgentConfig.tsx index 64c038c518..84229174cf 100644 --- a/client/src/components/SidePanel/Agents/AgentConfig.tsx +++ b/client/src/components/SidePanel/Agents/AgentConfig.tsx @@ -2,14 +2,20 @@ import React, { useState, useMemo, useCallback } from 'react'; import { EModelEndpoint } from 'librechat-data-provider'; import { Controller, useWatch, useFormContext } from 'react-hook-form'; import type { AgentForm, AgentPanelProps, IconComponentTypes } from '~/common'; -import { cn, defaultTextProps, removeFocusOutlines, getEndpointField, getIconKey } from '~/utils'; +import { + removeFocusOutlines, + processAgentOption, + getEndpointField, + defaultTextProps, + getIconKey, + cn, +} from '~/utils'; import { useToastContext, useFileMapContext, useAgentPanelContext } from '~/Providers'; import useAgentCapabilities from '~/hooks/Agents/useAgentCapabilities'; import Action from '~/components/SidePanel/Builder/Action'; import { ToolSelectDialog } from '~/components/Tools'; import { useGetAgentFiles } from '~/data-provider'; import { icons } from '~/hooks/Endpoint/Icons'; -import { processAgentOption } from '~/utils'; import Instructions from './Instructions'; import AgentAvatar from './AgentAvatar'; import FileContext from './FileContext'; diff --git a/librechat.example.yaml b/librechat.example.yaml index 0fb7975cbb..3a9230b508 100644 --- a/librechat.example.yaml +++ b/librechat.example.yaml @@ -16,6 +16,10 @@ interface: # MCP Servers UI configuration mcpServers: placeholder: 'MCP Servers' + # Enable/disable file search as a chatarea selection (default: true) + # Note: This setting does not disable the Agents File Search Capability. + # To disable the Agents Capability, see the Agents Endpoint configuration instead. + fileSearch: true # Privacy policy settings privacyPolicy: externalUrl: 'https://librechat.ai/privacy-policy' diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index 413f47e771..607a78488f 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -517,6 +517,7 @@ export const intefaceSchema = z temporaryChatRetention: z.number().min(1).max(8760).optional(), runCode: z.boolean().optional(), webSearch: z.boolean().optional(), + fileSearch: z.boolean().optional(), }) .default({ endpointsMenu: true, @@ -532,6 +533,7 @@ export const intefaceSchema = z temporaryChat: true, runCode: true, webSearch: true, + fileSearch: true, }); export type TInterfaceConfig = z.infer; diff --git a/packages/data-provider/src/permissions.ts b/packages/data-provider/src/permissions.ts index 1711c8b4e0..a2a1ec7718 100644 --- a/packages/data-provider/src/permissions.ts +++ b/packages/data-provider/src/permissions.ts @@ -36,6 +36,10 @@ export enum PermissionTypes { * Type for using the "Web Search" feature */ WEB_SEARCH = 'WEB_SEARCH', + /** + * Type for using the "File Search" feature + */ + FILE_SEARCH = 'FILE_SEARCH', } /** @@ -103,6 +107,11 @@ export const webSearchPermissionsSchema = z.object({ }); export type TWebSearchPermissions = z.infer; +export const fileSearchPermissionsSchema = z.object({ + [Permissions.USE]: z.boolean().default(true), +}); +export type TFileSearchPermissions = z.infer; + // Define a single permissions schema that holds all permission types. export const permissionsSchema = z.object({ [PermissionTypes.PROMPTS]: promptPermissionsSchema, @@ -113,4 +122,5 @@ export const permissionsSchema = z.object({ [PermissionTypes.TEMPORARY_CHAT]: temporaryChatPermissionsSchema, [PermissionTypes.RUN_CODE]: runCodePermissionsSchema, [PermissionTypes.WEB_SEARCH]: webSearchPermissionsSchema, + [PermissionTypes.FILE_SEARCH]: fileSearchPermissionsSchema, }); diff --git a/packages/data-provider/src/roles.ts b/packages/data-provider/src/roles.ts index 5ec82efaa2..487e9a608c 100644 --- a/packages/data-provider/src/roles.ts +++ b/packages/data-provider/src/roles.ts @@ -7,8 +7,9 @@ import { promptPermissionsSchema, memoryPermissionsSchema, runCodePermissionsSchema, - webSearchPermissionsSchema, bookmarkPermissionsSchema, + webSearchPermissionsSchema, + fileSearchPermissionsSchema, multiConvoPermissionsSchema, temporaryChatPermissionsSchema, } from './permissions'; @@ -74,6 +75,9 @@ const defaultRolesSchema = z.object({ [PermissionTypes.WEB_SEARCH]: webSearchPermissionsSchema.extend({ [Permissions.USE]: z.boolean().default(true), }), + [PermissionTypes.FILE_SEARCH]: fileSearchPermissionsSchema.extend({ + [Permissions.USE]: z.boolean().default(true), + }), }), }), [SystemRoles.USER]: roleSchema.extend({ @@ -118,6 +122,9 @@ export const roleDefaults = defaultRolesSchema.parse({ [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: true, }, + [PermissionTypes.FILE_SEARCH]: { + [Permissions.USE]: true, + }, }, }, [SystemRoles.USER]: { @@ -131,6 +138,7 @@ export const roleDefaults = defaultRolesSchema.parse({ [PermissionTypes.TEMPORARY_CHAT]: {}, [PermissionTypes.RUN_CODE]: {}, [PermissionTypes.WEB_SEARCH]: {}, + [PermissionTypes.FILE_SEARCH]: {}, }, }, }); diff --git a/packages/data-schemas/src/schema/role.ts b/packages/data-schemas/src/schema/role.ts index 1f6b5f5d57..80d1e632a6 100644 --- a/packages/data-schemas/src/schema/role.ts +++ b/packages/data-schemas/src/schema/role.ts @@ -2,7 +2,9 @@ import { Schema } from 'mongoose'; import { PermissionTypes, Permissions } from 'librechat-data-provider'; import type { IRole } from '~/types'; -// Create a sub-schema for permissions. Notice we disable _id for this subdocument. +/** + * Uses a sub-schema for permissions. Notice we disable `_id` for this subdocument. + */ const rolePermissionsSchema = new Schema( { [PermissionTypes.BOOKMARKS]: { @@ -37,6 +39,9 @@ const rolePermissionsSchema = new Schema( [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: { type: Boolean, default: true }, }, + [PermissionTypes.FILE_SEARCH]: { + [Permissions.USE]: { type: Boolean, default: true }, + }, }, { _id: false }, ); @@ -67,6 +72,7 @@ const roleSchema: Schema = new Schema({ [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true }, [PermissionTypes.RUN_CODE]: { [Permissions.USE]: true }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: true }, + [PermissionTypes.FILE_SEARCH]: { [Permissions.USE]: true }, }), }, });