import React, { useState, useMemo, useCallback } from 'react'; import * as Ariakit from '@ariakit/react'; import { Globe, Settings, Settings2, TerminalSquareIcon } from 'lucide-react'; import type { MenuItemProps } from '~/common'; import { Permissions, PermissionTypes, AuthType } from 'librechat-data-provider'; import { TooltipAnchor, DropdownPopup } from '~/components'; import MCPSubMenu from '~/components/Chat/Input/MCPSubMenu'; import { PinIcon, VectorIcon } from '~/components/svg'; import { useLocalize, useHasAccess } from '~/hooks'; import { useBadgeRowContext } from '~/Providers'; import { cn } from '~/utils'; interface ToolsDropdownProps { disabled?: boolean; } const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => { const localize = useLocalize(); const isDisabled = disabled ?? false; const [isPopoverActive, setIsPopoverActive] = useState(false); const { webSearch, mcpSelect, fileSearch, startupConfig, codeApiKeyForm, codeInterpreter, searchApiKeyForm, } = useBadgeRowContext(); const { setIsDialogOpen: setIsCodeDialogOpen, menuTriggerRef: codeMenuTriggerRef } = codeApiKeyForm; const { setIsDialogOpen: setIsSearchDialogOpen, menuTriggerRef: searchMenuTriggerRef } = searchApiKeyForm; const { isPinned: isSearchPinned, setIsPinned: setIsSearchPinned, authData: webSearchAuthData, } = webSearch; const { isPinned: isCodePinned, setIsPinned: setIsCodePinned, authData: codeAuthData, } = codeInterpreter; const { isPinned: isFileSearchPinned, setIsPinned: setIsFileSearchPinned } = fileSearch; const { mcpValues, mcpServerNames, isPinned: isMCPPinned, setIsPinned: setIsMCPPinned, } = mcpSelect; const canUseWebSearch = useHasAccess({ permissionType: PermissionTypes.WEB_SEARCH, permission: Permissions.USE, }); const canRunCode = useHasAccess({ permissionType: PermissionTypes.RUN_CODE, permission: Permissions.USE, }); const showWebSearchSettings = useMemo(() => { const authTypes = webSearchAuthData?.authTypes ?? []; if (authTypes.length === 0) return true; return !authTypes.every(([, authType]) => authType === AuthType.SYSTEM_DEFINED); }, [webSearchAuthData?.authTypes]); const showCodeSettings = useMemo( () => codeAuthData?.message !== AuthType.SYSTEM_DEFINED, [codeAuthData?.message], ); const handleWebSearchToggle = useCallback(() => { const newValue = !webSearch.toggleState; webSearch.debouncedChange({ isChecked: newValue }); }, [webSearch]); const handleCodeInterpreterToggle = useCallback(() => { const newValue = !codeInterpreter.toggleState; codeInterpreter.debouncedChange({ isChecked: newValue }); }, [codeInterpreter]); const handleFileSearchToggle = useCallback(() => { const newValue = !fileSearch.toggleState; fileSearch.debouncedChange({ isChecked: newValue }); }, [fileSearch]); const handleMCPToggle = useCallback( (serverName: string) => { const currentValues = mcpSelect.mcpValues ?? []; const newValues = currentValues.includes(serverName) ? currentValues.filter((v) => v !== serverName) : [...currentValues, serverName]; mcpSelect.setMCPValues(newValues); }, [mcpSelect], ); const mcpPlaceholder = startupConfig?.interface?.mcpServers?.placeholder; const dropdownItems = useMemo(() => { const items: MenuItemProps[] = []; items.push({ onClick: handleFileSearchToggle, hideOnClick: false, render: (props) => (
{localize('com_assistants_file_search')}
), }); if (canUseWebSearch) { items.push({ onClick: handleWebSearchToggle, hideOnClick: false, render: (props) => (
{localize('com_ui_web_search')}
{showWebSearchSettings && ( )}
), }); } if (canRunCode) { items.push({ onClick: handleCodeInterpreterToggle, hideOnClick: false, render: (props) => (
{localize('com_assistants_code_interpreter')}
{showCodeSettings && ( )}
), }); } if (mcpServerNames && mcpServerNames.length > 0) { items.push({ hideOnClick: false, render: (props) => ( ), }); } return items; }, [ localize, mcpValues, canRunCode, isMCPPinned, isCodePinned, mcpPlaceholder, mcpServerNames, isSearchPinned, setIsMCPPinned, canUseWebSearch, setIsCodePinned, handleMCPToggle, showCodeSettings, setIsSearchPinned, isFileSearchPinned, codeMenuTriggerRef, setIsCodeDialogOpen, searchMenuTriggerRef, showWebSearchSettings, setIsFileSearchPinned, handleWebSearchToggle, setIsSearchDialogOpen, handleFileSearchToggle, handleCodeInterpreterToggle, ]); const menuTrigger = (
} id="tools-dropdown-button" description={localize('com_ui_tools')} disabled={isDisabled} /> ); return ( ); }; export default React.memo(ToolsDropdown);