diff --git a/api/models/Agent.js b/api/models/Agent.js index d33ca8a8bf..04ba8b020e 100644 --- a/api/models/Agent.js +++ b/api/models/Agent.js @@ -70,6 +70,9 @@ const loadEphemeralAgent = async ({ req, agent_id, endpoint, model_parameters: _ if (ephemeralAgent?.execute_code === true) { tools.push(Tools.execute_code); } + if (ephemeralAgent?.file_search === true) { + tools.push(Tools.file_search); + } if (ephemeralAgent?.web_search === true) { tools.push(Tools.web_search); } diff --git a/client/src/components/Chat/Input/CodeInterpreter.tsx b/client/src/components/Chat/Input/CodeInterpreter.tsx index 411f1e27b3..f4b380f5b1 100644 --- a/client/src/components/Chat/Input/CodeInterpreter.tsx +++ b/client/src/components/Chat/Input/CodeInterpreter.tsx @@ -1,5 +1,5 @@ import debounce from 'lodash/debounce'; -import React, { memo, useMemo, useCallback, useRef } from 'react'; +import React, { memo, useMemo, useCallback, useEffect, useRef } from 'react'; import { useRecoilState } from 'recoil'; import { TerminalSquareIcon } from 'lucide-react'; import { @@ -45,6 +45,9 @@ function CodeInterpreter({ conversationId }: { conversationId?: string | null }) return ephemeralAgent?.execute_code ?? false; }, [ephemeralAgent?.execute_code]); + /** Track previous value to prevent infinite loops */ + const prevIsCodeToggleEnabled = useRef(isCodeToggleEnabled); + const { data } = useVerifyAgentToolAuth( { toolId: Tools.execute_code }, { @@ -60,7 +63,7 @@ function CodeInterpreter({ conversationId }: { conversationId?: string | null }) (isChecked: boolean) => { setEphemeralAgent((prev) => ({ ...prev, - execute_code: isChecked, + [Tools.execute_code]: isChecked, })); }, [setEphemeralAgent], @@ -90,6 +93,13 @@ function CodeInterpreter({ conversationId }: { conversationId?: string | null }) [handleChange], ); + useEffect(() => { + if (prevIsCodeToggleEnabled.current !== isCodeToggleEnabled) { + setRunCode(isCodeToggleEnabled); + } + prevIsCodeToggleEnabled.current = isCodeToggleEnabled; + }, [isCodeToggleEnabled, runCode, setRunCode]); + if (!canRunCode) { return null; } diff --git a/client/src/components/Chat/Input/Files/AttachFileChat.tsx b/client/src/components/Chat/Input/Files/AttachFileChat.tsx index 11bca082fe..f120b4f8f1 100644 --- a/client/src/components/Chat/Input/Files/AttachFileChat.tsx +++ b/client/src/components/Chat/Input/Files/AttachFileChat.tsx @@ -1,32 +1,20 @@ -import { memo, useMemo } from 'react'; -import { useRecoilValue } from 'recoil'; +import { memo } from 'react'; import { Constants, supportsFiles, mergeFileConfig, - isAgentsEndpoint, - isEphemeralAgent, EndpointFileConfig, fileConfig as defaultFileConfig, } from 'librechat-data-provider'; import { useChatContext } from '~/Providers'; import { useGetFileConfig } from '~/data-provider'; -import { ephemeralAgentByConvoId } from '~/store'; import AttachFileMenu from './AttachFileMenu'; -import AttachFile from './AttachFile'; function AttachFileChat({ disableInputs }: { disableInputs: boolean }) { const { conversation } = useChatContext(); - + const conversationId = conversation?.conversationId ?? Constants.NEW_CONVO; const { endpoint: _endpoint, endpointType } = conversation ?? { endpoint: null }; - const key = conversation?.conversationId ?? Constants.NEW_CONVO; - const ephemeralAgent = useRecoilValue(ephemeralAgentByConvoId(key)); - const isAgents = useMemo( - () => isAgentsEndpoint(_endpoint) || isEphemeralAgent(_endpoint, ephemeralAgent), - [_endpoint, ephemeralAgent], - ); - const { data: fileConfig = defaultFileConfig } = useGetFileConfig({ select: (data) => mergeFileConfig(data), }); @@ -38,11 +26,8 @@ function AttachFileChat({ disableInputs }: { disableInputs: boolean }) { const endpointSupportsFiles: boolean = supportsFiles[endpointType ?? _endpoint ?? ''] ?? false; const isUploadDisabled = (disableInputs || endpointFileConfig?.disabled) ?? false; - if (isAgents) { - return ; - } if (endpointSupportsFiles && !isUploadDisabled) { - return ; + return ; } return null; diff --git a/client/src/components/Chat/Input/Files/AttachFileMenu.tsx b/client/src/components/Chat/Input/Files/AttachFileMenu.tsx index 85df07f24f..dbb794849d 100644 --- a/client/src/components/Chat/Input/Files/AttachFileMenu.tsx +++ b/client/src/components/Chat/Input/Files/AttachFileMenu.tsx @@ -1,21 +1,25 @@ +import { useSetRecoilState } from 'recoil'; import * as Ariakit from '@ariakit/react'; import React, { useRef, useState, useMemo } from 'react'; import { FileSearch, ImageUpIcon, TerminalSquareIcon, FileType2Icon } from 'lucide-react'; -import { EToolResources, EModelEndpoint, defaultAgentCapabilities } from 'librechat-data-provider'; import { FileUpload, TooltipAnchor, DropdownPopup, AttachmentIcon } from '~/components'; +import { EToolResources, EModelEndpoint } from 'librechat-data-provider'; import { useGetEndpointsQuery } from '~/data-provider'; import { useLocalize, useFileHandling } from '~/hooks'; +import { ephemeralAgentByConvoId } from '~/store'; import { cn } from '~/utils'; -interface AttachFileProps { +interface AttachFileMenuProps { + conversationId: string; disabled?: boolean | null; } -const AttachFile = ({ disabled }: AttachFileProps) => { +const AttachFileMenu = ({ disabled, conversationId }: AttachFileMenuProps) => { const localize = useLocalize(); const isUploadDisabled = disabled ?? false; const inputRef = useRef(null); const [isPopoverActive, setIsPopoverActive] = useState(false); + const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(conversationId)); const [toolResource, setToolResource] = useState(); const { data: endpointsConfig } = useGetEndpointsQuery(); const { handleFileChange } = useFileHandling({ @@ -69,6 +73,10 @@ const AttachFile = ({ disabled }: AttachFileProps) => { label: localize('com_ui_upload_file_search'), onClick: () => { setToolResource(EToolResources.file_search); + setEphemeralAgent((prev) => ({ + ...prev, + [EToolResources.file_search]: true, + })); handleUploadClick(); }, icon: , @@ -80,6 +88,10 @@ const AttachFile = ({ disabled }: AttachFileProps) => { label: localize('com_ui_upload_code_files'), onClick: () => { setToolResource(EToolResources.execute_code); + setEphemeralAgent((prev) => ({ + ...prev, + [EToolResources.execute_code]: true, + })); handleUploadClick(); }, icon: , @@ -87,7 +99,7 @@ const AttachFile = ({ disabled }: AttachFileProps) => { } return items; - }, [capabilities, localize, setToolResource]); + }, [capabilities, localize, setToolResource, setEphemeralAgent]); const menuTrigger = ( { ); }; -export default React.memo(AttachFile); +export default React.memo(AttachFileMenu); diff --git a/packages/data-provider/src/types.ts b/packages/data-provider/src/types.ts index 275c405c1a..469c378aba 100644 --- a/packages/data-provider/src/types.ts +++ b/packages/data-provider/src/types.ts @@ -98,6 +98,7 @@ export type TEndpointOption = Pick< export type TEphemeralAgent = { mcp?: string[]; web_search?: boolean; + file_search?: boolean; execute_code?: boolean; };