feat: add file search capability to ephemeral agents, update code interpreter selection based of file upload, consolidate main upload menu for all endpoints

This commit is contained in:
Danny Avila 2025-06-21 19:16:28 -04:00
parent 13c977a670
commit ef7e517c06
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
5 changed files with 36 additions and 25 deletions

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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 <AttachFileMenu disabled={disableInputs} />;
}
if (endpointSupportsFiles && !isUploadDisabled) {
return <AttachFile disabled={disableInputs} />;
return <AttachFileMenu disabled={disableInputs} conversationId={conversationId} />;
}
return null;

View file

@ -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<HTMLInputElement>(null);
const [isPopoverActive, setIsPopoverActive] = useState(false);
const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(conversationId));
const [toolResource, setToolResource] = useState<EToolResources | undefined>();
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: <FileSearch className="icon-md" />,
@ -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: <TerminalSquareIcon className="icon-md" />,
@ -87,7 +99,7 @@ const AttachFile = ({ disabled }: AttachFileProps) => {
}
return items;
}, [capabilities, localize, setToolResource]);
}, [capabilities, localize, setToolResource, setEphemeralAgent]);
const menuTrigger = (
<TooltipAnchor
@ -132,4 +144,4 @@ const AttachFile = ({ disabled }: AttachFileProps) => {
);
};
export default React.memo(AttachFile);
export default React.memo(AttachFileMenu);

View file

@ -98,6 +98,7 @@ export type TEndpointOption = Pick<
export type TEphemeralAgent = {
mcp?: string[];
web_search?: boolean;
file_search?: boolean;
execute_code?: boolean;
};