mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
🔁 refactor: Capabilities for Tools/File handling for Direct Endpoints (#8253)
* feat: add useAgentCapabilities hook to manage agent capabilities * refactor: move agents and endpoints configuration to AgentPanel context provider * refactor: implement useGetAgentsConfig hook for consolidated agents and endpoints management * refactor: enhance ToolsDropdown to utilize agent capabilities and streamline dropdown item rendering * chore: reorder return values in useAgentCapabilities for improved clarity * refactor: enhance agent capabilities handling in AttachFileMenu and update file handling logic to allow capabilities to be used for non-agents endpoints
This commit is contained in:
parent
a288ad1d9c
commit
f5511e4a4e
15 changed files with 308 additions and 250 deletions
|
|
@ -1,4 +1,10 @@
|
||||||
const { CacheKeys, EModelEndpoint, orderEndpointsConfig } = require('librechat-data-provider');
|
const {
|
||||||
|
CacheKeys,
|
||||||
|
EModelEndpoint,
|
||||||
|
isAgentsEndpoint,
|
||||||
|
orderEndpointsConfig,
|
||||||
|
defaultAgentCapabilities,
|
||||||
|
} = require('librechat-data-provider');
|
||||||
const loadDefaultEndpointsConfig = require('./loadDefaultEConfig');
|
const loadDefaultEndpointsConfig = require('./loadDefaultEConfig');
|
||||||
const loadConfigEndpoints = require('./loadConfigEndpoints');
|
const loadConfigEndpoints = require('./loadConfigEndpoints');
|
||||||
const getLogStores = require('~/cache/getLogStores');
|
const getLogStores = require('~/cache/getLogStores');
|
||||||
|
|
@ -80,8 +86,12 @@ async function getEndpointsConfig(req) {
|
||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
const checkCapability = async (req, capability) => {
|
const checkCapability = async (req, capability) => {
|
||||||
|
const isAgents = isAgentsEndpoint(req.body?.original_endpoint || req.body?.endpoint);
|
||||||
const endpointsConfig = await getEndpointsConfig(req);
|
const endpointsConfig = await getEndpointsConfig(req);
|
||||||
const capabilities = endpointsConfig?.[EModelEndpoint.agents]?.capabilities ?? [];
|
const capabilities =
|
||||||
|
isAgents || endpointsConfig?.[EModelEndpoint.agents]?.capabilities != null
|
||||||
|
? (endpointsConfig?.[EModelEndpoint.agents]?.capabilities ?? [])
|
||||||
|
: defaultAgentCapabilities;
|
||||||
return capabilities.includes(capability);
|
return capabilities.includes(capability);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import React, { createContext, useContext, useState } from 'react';
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
import { Constants, EModelEndpoint } from 'librechat-data-provider';
|
import { Constants, EModelEndpoint } from 'librechat-data-provider';
|
||||||
import type { TPlugin, AgentToolType, Action, MCP } from 'librechat-data-provider';
|
import type { MCP, Action, TPlugin, AgentToolType } from 'librechat-data-provider';
|
||||||
import type { AgentPanelContextType } from '~/common';
|
import type { AgentPanelContextType } from '~/common';
|
||||||
import { useAvailableToolsQuery, useGetActionsQuery } from '~/data-provider';
|
import { useAvailableToolsQuery, useGetActionsQuery } from '~/data-provider';
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize, useGetAgentsConfig } from '~/hooks';
|
||||||
import { Panel } from '~/common';
|
import { Panel } from '~/common';
|
||||||
|
|
||||||
const AgentPanelContext = createContext<AgentPanelContextType | undefined>(undefined);
|
const AgentPanelContext = createContext<AgentPanelContextType | undefined>(undefined);
|
||||||
|
|
@ -75,21 +75,25 @@ export function AgentPanelProvider({ children }: { children: React.ReactNode })
|
||||||
{} as Record<string, AgentToolType & { tools?: AgentToolType[] }>,
|
{} as Record<string, AgentToolType & { tools?: AgentToolType[] }>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const value = {
|
const { agentsConfig, endpointsConfig } = useGetAgentsConfig();
|
||||||
action,
|
|
||||||
setAction,
|
const value: AgentPanelContextType = {
|
||||||
mcp,
|
mcp,
|
||||||
setMcp,
|
|
||||||
mcps,
|
mcps,
|
||||||
setMcps,
|
|
||||||
activePanel,
|
|
||||||
setActivePanel,
|
|
||||||
setCurrentAgentId,
|
|
||||||
agent_id,
|
|
||||||
groupedTools,
|
|
||||||
/** Query data for actions and tools */
|
/** Query data for actions and tools */
|
||||||
actions,
|
|
||||||
tools,
|
tools,
|
||||||
|
action,
|
||||||
|
setMcp,
|
||||||
|
actions,
|
||||||
|
setMcps,
|
||||||
|
agent_id,
|
||||||
|
setAction,
|
||||||
|
activePanel,
|
||||||
|
groupedTools,
|
||||||
|
agentsConfig,
|
||||||
|
setActivePanel,
|
||||||
|
endpointsConfig,
|
||||||
|
setCurrentAgentId,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <AgentPanelContext.Provider value={value}>{children}</AgentPanelContext.Provider>;
|
return <AgentPanelContext.Provider value={value}>{children}</AgentPanelContext.Provider>;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
import React, { createContext, useContext, useEffect, useRef } from 'react';
|
import React, { createContext, useContext, useEffect, useRef } from 'react';
|
||||||
import { Tools, LocalStorageKeys, AgentCapabilities, Constants } from 'librechat-data-provider';
|
|
||||||
import { useMCPSelect, useToolToggle, useCodeApiKeyForm, useSearchApiKeyForm } from '~/hooks';
|
|
||||||
import { useGetStartupConfig } from '~/data-provider';
|
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
import { Tools, Constants, LocalStorageKeys, AgentCapabilities } from 'librechat-data-provider';
|
||||||
|
import type { TAgentsEndpoint } from 'librechat-data-provider';
|
||||||
|
import {
|
||||||
|
useSearchApiKeyForm,
|
||||||
|
useGetAgentsConfig,
|
||||||
|
useCodeApiKeyForm,
|
||||||
|
useToolToggle,
|
||||||
|
useMCPSelect,
|
||||||
|
} from '~/hooks';
|
||||||
|
import { useGetStartupConfig } from '~/data-provider';
|
||||||
import { ephemeralAgentByConvoId } from '~/store';
|
import { ephemeralAgentByConvoId } from '~/store';
|
||||||
|
|
||||||
interface BadgeRowContextType {
|
interface BadgeRowContextType {
|
||||||
conversationId?: string | null;
|
conversationId?: string | null;
|
||||||
|
agentsConfig?: TAgentsEndpoint | null;
|
||||||
mcpSelect: ReturnType<typeof useMCPSelect>;
|
mcpSelect: ReturnType<typeof useMCPSelect>;
|
||||||
webSearch: ReturnType<typeof useToolToggle>;
|
webSearch: ReturnType<typeof useToolToggle>;
|
||||||
codeInterpreter: ReturnType<typeof useToolToggle>;
|
|
||||||
fileSearch: ReturnType<typeof useToolToggle>;
|
|
||||||
artifacts: ReturnType<typeof useToolToggle>;
|
artifacts: ReturnType<typeof useToolToggle>;
|
||||||
|
fileSearch: ReturnType<typeof useToolToggle>;
|
||||||
|
codeInterpreter: ReturnType<typeof useToolToggle>;
|
||||||
codeApiKeyForm: ReturnType<typeof useCodeApiKeyForm>;
|
codeApiKeyForm: ReturnType<typeof useCodeApiKeyForm>;
|
||||||
searchApiKeyForm: ReturnType<typeof useSearchApiKeyForm>;
|
searchApiKeyForm: ReturnType<typeof useSearchApiKeyForm>;
|
||||||
startupConfig: ReturnType<typeof useGetStartupConfig>['data'];
|
startupConfig: ReturnType<typeof useGetStartupConfig>['data'];
|
||||||
|
|
@ -40,6 +48,7 @@ export default function BadgeRowProvider({
|
||||||
}: BadgeRowProviderProps) {
|
}: BadgeRowProviderProps) {
|
||||||
const hasInitializedRef = useRef(false);
|
const hasInitializedRef = useRef(false);
|
||||||
const lastKeyRef = useRef<string>('');
|
const lastKeyRef = useRef<string>('');
|
||||||
|
const { agentsConfig } = useGetAgentsConfig();
|
||||||
const key = conversationId ?? Constants.NEW_CONVO;
|
const key = conversationId ?? Constants.NEW_CONVO;
|
||||||
const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(key));
|
const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(key));
|
||||||
|
|
||||||
|
|
@ -165,8 +174,9 @@ export default function BadgeRowProvider({
|
||||||
const value: BadgeRowContextType = {
|
const value: BadgeRowContextType = {
|
||||||
mcpSelect,
|
mcpSelect,
|
||||||
webSearch,
|
webSearch,
|
||||||
fileSearch,
|
|
||||||
artifacts,
|
artifacts,
|
||||||
|
fileSearch,
|
||||||
|
agentsConfig,
|
||||||
startupConfig,
|
startupConfig,
|
||||||
conversationId,
|
conversationId,
|
||||||
codeApiKeyForm,
|
codeApiKeyForm,
|
||||||
|
|
|
||||||
|
|
@ -206,9 +206,7 @@ export type AgentPanelProps = {
|
||||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||||
setMcp: React.Dispatch<React.SetStateAction<t.MCP | undefined>>;
|
setMcp: React.Dispatch<React.SetStateAction<t.MCP | undefined>>;
|
||||||
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
|
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
|
||||||
endpointsConfig?: t.TEndpointsConfig;
|
|
||||||
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
agentsConfig?: t.TAgentsEndpoint | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AgentPanelContextType = {
|
export type AgentPanelContextType = {
|
||||||
|
|
@ -225,6 +223,8 @@ export type AgentPanelContextType = {
|
||||||
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
groupedTools?: Record<string, t.AgentToolType & { tools?: t.AgentToolType[] }>;
|
groupedTools?: Record<string, t.AgentToolType & { tools?: t.AgentToolType[] }>;
|
||||||
agent_id?: string;
|
agent_id?: string;
|
||||||
|
agentsConfig?: t.TAgentsEndpoint | null;
|
||||||
|
endpointsConfig?: t.TEndpointsConfig | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AgentModelPanelProps = {
|
export type AgentModelPanelProps = {
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,10 @@ import { useSetRecoilState } from 'recoil';
|
||||||
import * as Ariakit from '@ariakit/react';
|
import * as Ariakit from '@ariakit/react';
|
||||||
import React, { useRef, useState, useMemo } from 'react';
|
import React, { useRef, useState, useMemo } from 'react';
|
||||||
import { FileSearch, ImageUpIcon, TerminalSquareIcon, FileType2Icon } from 'lucide-react';
|
import { FileSearch, ImageUpIcon, TerminalSquareIcon, FileType2Icon } from 'lucide-react';
|
||||||
|
import { EToolResources, EModelEndpoint, defaultAgentCapabilities } from 'librechat-data-provider';
|
||||||
import type { EndpointFileConfig } from 'librechat-data-provider';
|
import type { EndpointFileConfig } from 'librechat-data-provider';
|
||||||
|
import { useLocalize, useGetAgentsConfig, useFileHandling, useAgentCapabilities } from '~/hooks';
|
||||||
import { FileUpload, TooltipAnchor, DropdownPopup, AttachmentIcon } from '~/components';
|
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 { ephemeralAgentByConvoId } from '~/store';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
|
|
@ -23,20 +22,17 @@ const AttachFileMenu = ({ disabled, conversationId, endpointFileConfig }: Attach
|
||||||
const [isPopoverActive, setIsPopoverActive] = useState(false);
|
const [isPopoverActive, setIsPopoverActive] = useState(false);
|
||||||
const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(conversationId));
|
const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(conversationId));
|
||||||
const [toolResource, setToolResource] = useState<EToolResources | undefined>();
|
const [toolResource, setToolResource] = useState<EToolResources | undefined>();
|
||||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
|
||||||
const { handleFileChange } = useFileHandling({
|
const { handleFileChange } = useFileHandling({
|
||||||
overrideEndpoint: EModelEndpoint.agents,
|
overrideEndpoint: EModelEndpoint.agents,
|
||||||
overrideEndpointFileConfig: endpointFileConfig,
|
overrideEndpointFileConfig: endpointFileConfig,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { agentsConfig } = useGetAgentsConfig();
|
||||||
/** TODO: Ephemeral Agent Capabilities
|
/** TODO: Ephemeral Agent Capabilities
|
||||||
* Allow defining agent capabilities on a per-endpoint basis
|
* Allow defining agent capabilities on a per-endpoint basis
|
||||||
* Use definition for agents endpoint for ephemeral agents
|
* Use definition for agents endpoint for ephemeral agents
|
||||||
* */
|
* */
|
||||||
const capabilities = useMemo(
|
const capabilities = useAgentCapabilities(agentsConfig?.capabilities ?? defaultAgentCapabilities);
|
||||||
() => endpointsConfig?.[EModelEndpoint.agents]?.capabilities ?? [],
|
|
||||||
[endpointsConfig],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleUploadClick = (isImage?: boolean) => {
|
const handleUploadClick = (isImage?: boolean) => {
|
||||||
if (!inputRef.current) {
|
if (!inputRef.current) {
|
||||||
|
|
@ -60,7 +56,7 @@ const AttachFileMenu = ({ disabled, conversationId, endpointFileConfig }: Attach
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (capabilities.includes(EToolResources.ocr)) {
|
if (capabilities.ocrEnabled) {
|
||||||
items.push({
|
items.push({
|
||||||
label: localize('com_ui_upload_ocr_text'),
|
label: localize('com_ui_upload_ocr_text'),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
|
@ -71,7 +67,7 @@ const AttachFileMenu = ({ disabled, conversationId, endpointFileConfig }: Attach
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (capabilities.includes(EToolResources.file_search)) {
|
if (capabilities.fileSearchEnabled) {
|
||||||
items.push({
|
items.push({
|
||||||
label: localize('com_ui_upload_file_search'),
|
label: localize('com_ui_upload_file_search'),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
|
@ -83,7 +79,7 @@ const AttachFileMenu = ({ disabled, conversationId, endpointFileConfig }: Attach
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (capabilities.includes(EToolResources.execute_code)) {
|
if (capabilities.codeEnabled) {
|
||||||
items.push({
|
items.push({
|
||||||
label: localize('com_ui_upload_code_files'),
|
label: localize('com_ui_upload_code_files'),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,18 @@ import React, { useState, useMemo, useCallback } from 'react';
|
||||||
import * as Ariakit from '@ariakit/react';
|
import * as Ariakit from '@ariakit/react';
|
||||||
import { Globe, Settings, Settings2, TerminalSquareIcon } from 'lucide-react';
|
import { Globe, Settings, Settings2, TerminalSquareIcon } from 'lucide-react';
|
||||||
import type { MenuItemProps } from '~/common';
|
import type { MenuItemProps } from '~/common';
|
||||||
import { Permissions, PermissionTypes, AuthType, ArtifactModes } from 'librechat-data-provider';
|
import {
|
||||||
|
AuthType,
|
||||||
|
Permissions,
|
||||||
|
ArtifactModes,
|
||||||
|
PermissionTypes,
|
||||||
|
defaultAgentCapabilities,
|
||||||
|
} from 'librechat-data-provider';
|
||||||
import { TooltipAnchor, DropdownPopup } from '~/components';
|
import { TooltipAnchor, DropdownPopup } from '~/components';
|
||||||
|
import { useLocalize, useHasAccess, useAgentCapabilities } from '~/hooks';
|
||||||
import ArtifactsSubMenu from '~/components/Chat/Input/ArtifactsSubMenu';
|
import ArtifactsSubMenu from '~/components/Chat/Input/ArtifactsSubMenu';
|
||||||
import MCPSubMenu from '~/components/Chat/Input/MCPSubMenu';
|
import MCPSubMenu from '~/components/Chat/Input/MCPSubMenu';
|
||||||
import { PinIcon, VectorIcon } from '~/components/svg';
|
import { PinIcon, VectorIcon } from '~/components/svg';
|
||||||
import { useLocalize, useHasAccess } from '~/hooks';
|
|
||||||
import { useBadgeRowContext } from '~/Providers';
|
import { useBadgeRowContext } from '~/Providers';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
|
|
@ -24,11 +30,15 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
|
||||||
mcpSelect,
|
mcpSelect,
|
||||||
artifacts,
|
artifacts,
|
||||||
fileSearch,
|
fileSearch,
|
||||||
|
agentsConfig,
|
||||||
startupConfig,
|
startupConfig,
|
||||||
codeApiKeyForm,
|
codeApiKeyForm,
|
||||||
codeInterpreter,
|
codeInterpreter,
|
||||||
searchApiKeyForm,
|
searchApiKeyForm,
|
||||||
} = useBadgeRowContext();
|
} = useBadgeRowContext();
|
||||||
|
const { codeEnabled, webSearchEnabled, artifactsEnabled, fileSearchEnabled } =
|
||||||
|
useAgentCapabilities(agentsConfig?.capabilities ?? defaultAgentCapabilities);
|
||||||
|
|
||||||
const { setIsDialogOpen: setIsCodeDialogOpen, menuTriggerRef: codeMenuTriggerRef } =
|
const { setIsDialogOpen: setIsCodeDialogOpen, menuTriggerRef: codeMenuTriggerRef } =
|
||||||
codeApiKeyForm;
|
codeApiKeyForm;
|
||||||
const { setIsDialogOpen: setIsSearchDialogOpen, menuTriggerRef: searchMenuTriggerRef } =
|
const { setIsDialogOpen: setIsSearchDialogOpen, menuTriggerRef: searchMenuTriggerRef } =
|
||||||
|
|
@ -128,9 +138,10 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
|
||||||
|
|
||||||
const mcpPlaceholder = startupConfig?.interface?.mcpServers?.placeholder;
|
const mcpPlaceholder = startupConfig?.interface?.mcpServers?.placeholder;
|
||||||
|
|
||||||
const dropdownItems = useMemo(() => {
|
const dropdownItems: MenuItemProps[] = [];
|
||||||
const items: MenuItemProps[] = [];
|
|
||||||
items.push({
|
if (fileSearchEnabled) {
|
||||||
|
dropdownItems.push({
|
||||||
onClick: handleFileSearchToggle,
|
onClick: handleFileSearchToggle,
|
||||||
hideOnClick: false,
|
hideOnClick: false,
|
||||||
render: (props) => (
|
render: (props) => (
|
||||||
|
|
@ -159,117 +170,118 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (canUseWebSearch) {
|
if (canUseWebSearch && webSearchEnabled) {
|
||||||
items.push({
|
dropdownItems.push({
|
||||||
onClick: handleWebSearchToggle,
|
onClick: handleWebSearchToggle,
|
||||||
hideOnClick: false,
|
hideOnClick: false,
|
||||||
render: (props) => (
|
render: (props) => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Globe className="icon-md" />
|
<Globe className="icon-md" />
|
||||||
<span>{localize('com_ui_web_search')}</span>
|
<span>{localize('com_ui_web_search')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{showWebSearchSettings && (
|
{showWebSearchSettings && (
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
setIsSearchDialogOpen(true);
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
'rounded p-1 transition-all duration-200',
|
|
||||||
'hover:bg-surface-secondary hover:shadow-sm',
|
|
||||||
'text-text-secondary hover:text-text-primary',
|
|
||||||
)}
|
|
||||||
aria-label="Configure web search"
|
|
||||||
ref={searchMenuTriggerRef}
|
|
||||||
>
|
|
||||||
<div className="h-4 w-4">
|
|
||||||
<Settings className="h-4 w-4" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsSearchPinned(!isSearchPinned);
|
setIsSearchDialogOpen(true);
|
||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded p-1 transition-all duration-200',
|
'rounded p-1 transition-all duration-200',
|
||||||
'hover:bg-surface-secondary hover:shadow-sm',
|
'hover:bg-surface-secondary hover:shadow-sm',
|
||||||
!isSearchPinned && 'text-text-secondary hover:text-text-primary',
|
'text-text-secondary hover:text-text-primary',
|
||||||
)}
|
)}
|
||||||
aria-label={isSearchPinned ? 'Unpin' : 'Pin'}
|
aria-label="Configure web search"
|
||||||
|
ref={searchMenuTriggerRef}
|
||||||
>
|
>
|
||||||
<div className="h-4 w-4">
|
<div className="h-4 w-4">
|
||||||
<PinIcon unpin={isSearchPinned} />
|
<Settings className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
)}
|
||||||
</div>
|
<button
|
||||||
),
|
type="button"
|
||||||
});
|
onClick={(e) => {
|
||||||
}
|
e.stopPropagation();
|
||||||
|
setIsSearchPinned(!isSearchPinned);
|
||||||
if (canRunCode) {
|
}}
|
||||||
items.push({
|
className={cn(
|
||||||
onClick: handleCodeInterpreterToggle,
|
'rounded p-1 transition-all duration-200',
|
||||||
hideOnClick: false,
|
'hover:bg-surface-secondary hover:shadow-sm',
|
||||||
render: (props) => (
|
!isSearchPinned && 'text-text-secondary hover:text-text-primary',
|
||||||
<div {...props}>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<TerminalSquareIcon className="icon-md" />
|
|
||||||
<span>{localize('com_assistants_code_interpreter')}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
{showCodeSettings && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
setIsCodeDialogOpen(true);
|
|
||||||
}}
|
|
||||||
ref={codeMenuTriggerRef}
|
|
||||||
className={cn(
|
|
||||||
'rounded p-1 transition-all duration-200',
|
|
||||||
'hover:bg-surface-secondary hover:shadow-sm',
|
|
||||||
'text-text-secondary hover:text-text-primary',
|
|
||||||
)}
|
|
||||||
aria-label="Configure code interpreter"
|
|
||||||
>
|
|
||||||
<div className="h-4 w-4">
|
|
||||||
<Settings className="h-4 w-4" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
|
aria-label={isSearchPinned ? 'Unpin' : 'Pin'}
|
||||||
|
>
|
||||||
|
<div className="h-4 w-4">
|
||||||
|
<PinIcon unpin={isSearchPinned} />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canRunCode && codeEnabled) {
|
||||||
|
dropdownItems.push({
|
||||||
|
onClick: handleCodeInterpreterToggle,
|
||||||
|
hideOnClick: false,
|
||||||
|
render: (props) => (
|
||||||
|
<div {...props}>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<TerminalSquareIcon className="icon-md" />
|
||||||
|
<span>{localize('com_assistants_code_interpreter')}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
{showCodeSettings && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsCodePinned(!isCodePinned);
|
setIsCodeDialogOpen(true);
|
||||||
}}
|
}}
|
||||||
|
ref={codeMenuTriggerRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded p-1 transition-all duration-200',
|
'rounded p-1 transition-all duration-200',
|
||||||
'hover:bg-surface-secondary hover:shadow-sm',
|
'hover:bg-surface-secondary hover:shadow-sm',
|
||||||
!isCodePinned && 'text-text-primary hover:text-text-primary',
|
'text-text-secondary hover:text-text-primary',
|
||||||
)}
|
)}
|
||||||
aria-label={isCodePinned ? 'Unpin' : 'Pin'}
|
aria-label="Configure code interpreter"
|
||||||
>
|
>
|
||||||
<div className="h-4 w-4">
|
<div className="h-4 w-4">
|
||||||
<PinIcon unpin={isCodePinned} />
|
<Settings className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsCodePinned(!isCodePinned);
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
'rounded p-1 transition-all duration-200',
|
||||||
|
'hover:bg-surface-secondary hover:shadow-sm',
|
||||||
|
!isCodePinned && 'text-text-primary hover:text-text-primary',
|
||||||
|
)}
|
||||||
|
aria-label={isCodePinned ? 'Unpin' : 'Pin'}
|
||||||
|
>
|
||||||
|
<div className="h-4 w-4">
|
||||||
|
<PinIcon unpin={isCodePinned} />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
),
|
</div>
|
||||||
});
|
),
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Add Artifacts option
|
if (artifactsEnabled) {
|
||||||
items.push({
|
dropdownItems.push({
|
||||||
hideOnClick: false,
|
hideOnClick: false,
|
||||||
render: (props) => (
|
render: (props) => (
|
||||||
<ArtifactsSubMenu
|
<ArtifactsSubMenu
|
||||||
|
|
@ -283,57 +295,24 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (mcpServerNames && mcpServerNames.length > 0) {
|
if (mcpServerNames && mcpServerNames.length > 0) {
|
||||||
items.push({
|
dropdownItems.push({
|
||||||
hideOnClick: false,
|
hideOnClick: false,
|
||||||
render: (props) => (
|
render: (props) => (
|
||||||
<MCPSubMenu
|
<MCPSubMenu
|
||||||
{...props}
|
{...props}
|
||||||
mcpValues={mcpValues}
|
mcpValues={mcpValues}
|
||||||
isMCPPinned={isMCPPinned}
|
isMCPPinned={isMCPPinned}
|
||||||
placeholder={mcpPlaceholder}
|
placeholder={mcpPlaceholder}
|
||||||
mcpServerNames={mcpServerNames}
|
mcpServerNames={mcpServerNames}
|
||||||
setIsMCPPinned={setIsMCPPinned}
|
setIsMCPPinned={setIsMCPPinned}
|
||||||
handleMCPToggle={handleMCPToggle}
|
handleMCPToggle={handleMCPToggle}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
|
||||||
}, [
|
|
||||||
localize,
|
|
||||||
mcpValues,
|
|
||||||
canRunCode,
|
|
||||||
isMCPPinned,
|
|
||||||
isCodePinned,
|
|
||||||
mcpPlaceholder,
|
|
||||||
mcpServerNames,
|
|
||||||
isSearchPinned,
|
|
||||||
setIsMCPPinned,
|
|
||||||
canUseWebSearch,
|
|
||||||
setIsCodePinned,
|
|
||||||
handleMCPToggle,
|
|
||||||
showCodeSettings,
|
|
||||||
setIsSearchPinned,
|
|
||||||
handleShadcnToggle,
|
|
||||||
handleCustomToggle,
|
|
||||||
isFileSearchPinned,
|
|
||||||
isArtifactsPinned,
|
|
||||||
codeMenuTriggerRef,
|
|
||||||
setIsCodeDialogOpen,
|
|
||||||
searchMenuTriggerRef,
|
|
||||||
showWebSearchSettings,
|
|
||||||
setIsFileSearchPinned,
|
|
||||||
artifacts.toggleState,
|
|
||||||
setIsArtifactsPinned,
|
|
||||||
handleWebSearchToggle,
|
|
||||||
setIsSearchDialogOpen,
|
|
||||||
handleFileSearchToggle,
|
|
||||||
handleArtifactsToggle,
|
|
||||||
handleCodeInterpreterToggle,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const menuTrigger = (
|
const menuTrigger = (
|
||||||
<TooltipAnchor
|
<TooltipAnchor
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,20 @@ import { useMemo } from 'react';
|
||||||
import { ChevronLeft } from 'lucide-react';
|
import { ChevronLeft } from 'lucide-react';
|
||||||
import { AgentCapabilities } from 'librechat-data-provider';
|
import { AgentCapabilities } from 'librechat-data-provider';
|
||||||
import { useFormContext, Controller } from 'react-hook-form';
|
import { useFormContext, Controller } from 'react-hook-form';
|
||||||
import type { AgentForm, AgentPanelProps } from '~/common';
|
import type { AgentForm } from '~/common';
|
||||||
|
import { useAgentPanelContext } from '~/Providers';
|
||||||
import MaxAgentSteps from './MaxAgentSteps';
|
import MaxAgentSteps from './MaxAgentSteps';
|
||||||
import AgentChain from './AgentChain';
|
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize } from '~/hooks';
|
||||||
|
import AgentChain from './AgentChain';
|
||||||
import { Panel } from '~/common';
|
import { Panel } from '~/common';
|
||||||
|
|
||||||
export default function AdvancedPanel({
|
export default function AdvancedPanel() {
|
||||||
agentsConfig,
|
|
||||||
setActivePanel,
|
|
||||||
}: Pick<AgentPanelProps, 'setActivePanel' | 'agentsConfig'>) {
|
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const methods = useFormContext<AgentForm>();
|
const methods = useFormContext<AgentForm>();
|
||||||
const { control, watch } = methods;
|
const { control, watch } = methods;
|
||||||
const currentAgentId = watch('id');
|
const currentAgentId = watch('id');
|
||||||
|
|
||||||
|
const { agentsConfig, setActivePanel } = useAgentPanelContext();
|
||||||
const chainEnabled = useMemo(
|
const chainEnabled = useMemo(
|
||||||
() => agentsConfig?.capabilities.includes(AgentCapabilities.chain) ?? false,
|
() => agentsConfig?.capabilities.includes(AgentCapabilities.chain) ?? false,
|
||||||
[agentsConfig],
|
[agentsConfig],
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { useState, useMemo, useCallback } from 'react';
|
import React, { useState, useMemo, useCallback } from 'react';
|
||||||
|
import { EModelEndpoint } from 'librechat-data-provider';
|
||||||
import { Controller, useWatch, useFormContext } from 'react-hook-form';
|
import { Controller, useWatch, useFormContext } from 'react-hook-form';
|
||||||
import { EModelEndpoint, AgentCapabilities } from 'librechat-data-provider';
|
|
||||||
import type { AgentForm, AgentPanelProps, IconComponentTypes } from '~/common';
|
import type { AgentForm, AgentPanelProps, IconComponentTypes } from '~/common';
|
||||||
import { cn, defaultTextProps, removeFocusOutlines, getEndpointField, getIconKey } from '~/utils';
|
import { cn, defaultTextProps, removeFocusOutlines, getEndpointField, getIconKey } from '~/utils';
|
||||||
import { useToastContext, useFileMapContext, useAgentPanelContext } from '~/Providers';
|
import { useToastContext, useFileMapContext, useAgentPanelContext } from '~/Providers';
|
||||||
|
import useAgentCapabilities from '~/hooks/Agents/useAgentCapabilities';
|
||||||
import Action from '~/components/SidePanel/Builder/Action';
|
import Action from '~/components/SidePanel/Builder/Action';
|
||||||
import { ToolSelectDialog } from '~/components/Tools';
|
import { ToolSelectDialog } from '~/components/Tools';
|
||||||
import { icons } from '~/hooks/Endpoint/Icons';
|
import { icons } from '~/hooks/Endpoint/Icons';
|
||||||
|
|
@ -26,17 +27,20 @@ const inputClass = cn(
|
||||||
removeFocusOutlines,
|
removeFocusOutlines,
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function AgentConfig({
|
export default function AgentConfig({ createMutation }: Pick<AgentPanelProps, 'createMutation'>) {
|
||||||
agentsConfig,
|
|
||||||
createMutation,
|
|
||||||
endpointsConfig,
|
|
||||||
}: Pick<AgentPanelProps, 'agentsConfig' | 'createMutation' | 'endpointsConfig'>) {
|
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const fileMap = useFileMapContext();
|
const fileMap = useFileMapContext();
|
||||||
const { showToast } = useToastContext();
|
const { showToast } = useToastContext();
|
||||||
const methods = useFormContext<AgentForm>();
|
const methods = useFormContext<AgentForm>();
|
||||||
const [showToolDialog, setShowToolDialog] = useState(false);
|
const [showToolDialog, setShowToolDialog] = useState(false);
|
||||||
const { actions, setAction, groupedTools: allTools, setActivePanel } = useAgentPanelContext();
|
const {
|
||||||
|
actions,
|
||||||
|
setAction,
|
||||||
|
agentsConfig,
|
||||||
|
setActivePanel,
|
||||||
|
endpointsConfig,
|
||||||
|
groupedTools: allTools,
|
||||||
|
} = useAgentPanelContext();
|
||||||
|
|
||||||
const { control } = methods;
|
const { control } = methods;
|
||||||
const provider = useWatch({ control, name: 'provider' });
|
const provider = useWatch({ control, name: 'provider' });
|
||||||
|
|
@ -45,34 +49,15 @@ export default function AgentConfig({
|
||||||
const tools = useWatch({ control, name: 'tools' });
|
const tools = useWatch({ control, name: 'tools' });
|
||||||
const agent_id = useWatch({ control, name: 'id' });
|
const agent_id = useWatch({ control, name: 'id' });
|
||||||
|
|
||||||
const toolsEnabled = useMemo(
|
const {
|
||||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.tools) ?? false,
|
ocrEnabled,
|
||||||
[agentsConfig],
|
codeEnabled,
|
||||||
);
|
toolsEnabled,
|
||||||
const actionsEnabled = useMemo(
|
actionsEnabled,
|
||||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.actions) ?? false,
|
artifactsEnabled,
|
||||||
[agentsConfig],
|
webSearchEnabled,
|
||||||
);
|
fileSearchEnabled,
|
||||||
const artifactsEnabled = useMemo(
|
} = useAgentCapabilities(agentsConfig?.capabilities);
|
||||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.artifacts) ?? false,
|
|
||||||
[agentsConfig],
|
|
||||||
);
|
|
||||||
const ocrEnabled = useMemo(
|
|
||||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.ocr) ?? false,
|
|
||||||
[agentsConfig],
|
|
||||||
);
|
|
||||||
const fileSearchEnabled = useMemo(
|
|
||||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.file_search) ?? false,
|
|
||||||
[agentsConfig],
|
|
||||||
);
|
|
||||||
const webSearchEnabled = useMemo(
|
|
||||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.web_search) ?? false,
|
|
||||||
[agentsConfig],
|
|
||||||
);
|
|
||||||
const codeEnabled = useMemo(
|
|
||||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.execute_code) ?? false,
|
|
||||||
[agentsConfig],
|
|
||||||
);
|
|
||||||
|
|
||||||
const context_files = useMemo(() => {
|
const context_files = useMemo(() => {
|
||||||
if (typeof agent === 'string') {
|
if (typeof agent === 'string') {
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ import {
|
||||||
Constants,
|
Constants,
|
||||||
SystemRoles,
|
SystemRoles,
|
||||||
EModelEndpoint,
|
EModelEndpoint,
|
||||||
TAgentsEndpoint,
|
|
||||||
TEndpointsConfig,
|
|
||||||
isAssistantsEndpoint,
|
isAssistantsEndpoint,
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
import type { AgentForm, StringOption } from '~/common';
|
import type { AgentForm, StringOption } from '~/common';
|
||||||
|
|
@ -30,19 +28,15 @@ import { Button } from '~/components';
|
||||||
import ModelPanel from './ModelPanel';
|
import ModelPanel from './ModelPanel';
|
||||||
import { Panel } from '~/common';
|
import { Panel } from '~/common';
|
||||||
|
|
||||||
export default function AgentPanel({
|
export default function AgentPanel() {
|
||||||
agentsConfig,
|
|
||||||
endpointsConfig,
|
|
||||||
}: {
|
|
||||||
agentsConfig: TAgentsEndpoint | null;
|
|
||||||
endpointsConfig: TEndpointsConfig;
|
|
||||||
}) {
|
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const { user } = useAuthContext();
|
const { user } = useAuthContext();
|
||||||
const { showToast } = useToastContext();
|
const { showToast } = useToastContext();
|
||||||
const {
|
const {
|
||||||
activePanel,
|
activePanel,
|
||||||
|
agentsConfig,
|
||||||
setActivePanel,
|
setActivePanel,
|
||||||
|
endpointsConfig,
|
||||||
setCurrentAgentId,
|
setCurrentAgentId,
|
||||||
agent_id: current_agent_id,
|
agent_id: current_agent_id,
|
||||||
} = useAgentPanelContext();
|
} = useAgentPanelContext();
|
||||||
|
|
@ -323,14 +317,10 @@ export default function AgentPanel({
|
||||||
<ModelPanel models={models} providers={providers} setActivePanel={setActivePanel} />
|
<ModelPanel models={models} providers={providers} setActivePanel={setActivePanel} />
|
||||||
)}
|
)}
|
||||||
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.builder && (
|
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.builder && (
|
||||||
<AgentConfig
|
<AgentConfig createMutation={create} />
|
||||||
createMutation={create}
|
|
||||||
agentsConfig={agentsConfig}
|
|
||||||
endpointsConfig={endpointsConfig}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.advanced && (
|
{canEditAgent && !agentQuery.isInitialLoading && activePanel === Panel.advanced && (
|
||||||
<AdvancedPanel setActivePanel={setActivePanel} agentsConfig={agentsConfig} />
|
<AdvancedPanel />
|
||||||
)}
|
)}
|
||||||
{canEditAgent && !agentQuery.isInitialLoading && (
|
{canEditAgent && !agentQuery.isInitialLoading && (
|
||||||
<AgentFooter
|
<AgentFooter
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { EModelEndpoint, AgentCapabilities } from 'librechat-data-provider';
|
|
||||||
import type { TConfig, TEndpointsConfig, TAgentsEndpoint } from 'librechat-data-provider';
|
|
||||||
import { AgentPanelProvider, useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
import { AgentPanelProvider, useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
||||||
import { useGetEndpointsQuery } from '~/data-provider';
|
|
||||||
import VersionPanel from './Version/VersionPanel';
|
import VersionPanel from './Version/VersionPanel';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
import ActionsPanel from './ActionsPanel';
|
import ActionsPanel from './ActionsPanel';
|
||||||
|
|
@ -22,21 +19,6 @@ function AgentPanelSwitchWithContext() {
|
||||||
const { conversation } = useChatContext();
|
const { conversation } = useChatContext();
|
||||||
const { activePanel, setCurrentAgentId } = useAgentPanelContext();
|
const { activePanel, setCurrentAgentId } = useAgentPanelContext();
|
||||||
|
|
||||||
// TODO: Implement MCP endpoint
|
|
||||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
|
||||||
|
|
||||||
const agentsConfig = useMemo<TAgentsEndpoint | null>(() => {
|
|
||||||
const config = endpointsConfig?.[EModelEndpoint.agents] ?? null;
|
|
||||||
if (!config) return null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...(config as TConfig),
|
|
||||||
capabilities: Array.isArray(config.capabilities)
|
|
||||||
? config.capabilities.map((cap) => cap as unknown as AgentCapabilities)
|
|
||||||
: ([] as AgentCapabilities[]),
|
|
||||||
} as TAgentsEndpoint;
|
|
||||||
}, [endpointsConfig]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const agent_id = conversation?.agent_id ?? '';
|
const agent_id = conversation?.agent_id ?? '';
|
||||||
if (agent_id) {
|
if (agent_id) {
|
||||||
|
|
@ -57,5 +39,5 @@ function AgentPanelSwitchWithContext() {
|
||||||
if (activePanel === Panel.mcp) {
|
if (activePanel === Panel.mcp) {
|
||||||
return <MCPPanel />;
|
return <MCPPanel />;
|
||||||
}
|
}
|
||||||
return <AgentPanel agentsConfig={agentsConfig} endpointsConfig={endpointsConfig} />;
|
return <AgentPanel />;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
export { default as useAgentsMap } from './useAgentsMap';
|
export { default as useAgentsMap } from './useAgentsMap';
|
||||||
export { default as useSelectAgent } from './useSelectAgent';
|
export { default as useSelectAgent } from './useSelectAgent';
|
||||||
|
export { default as useAgentCapabilities } from './useAgentCapabilities';
|
||||||
|
export { default as useGetAgentsConfig } from './useGetAgentsConfig';
|
||||||
|
|
|
||||||
61
client/src/hooks/Agents/useAgentCapabilities.ts
Normal file
61
client/src/hooks/Agents/useAgentCapabilities.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { AgentCapabilities } from 'librechat-data-provider';
|
||||||
|
|
||||||
|
interface AgentCapabilitiesResult {
|
||||||
|
toolsEnabled: boolean;
|
||||||
|
actionsEnabled: boolean;
|
||||||
|
artifactsEnabled: boolean;
|
||||||
|
ocrEnabled: boolean;
|
||||||
|
fileSearchEnabled: boolean;
|
||||||
|
webSearchEnabled: boolean;
|
||||||
|
codeEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useAgentCapabilities(
|
||||||
|
capabilities: AgentCapabilities[] | undefined,
|
||||||
|
): AgentCapabilitiesResult {
|
||||||
|
const toolsEnabled = useMemo(
|
||||||
|
() => capabilities?.includes(AgentCapabilities.tools) ?? false,
|
||||||
|
[capabilities],
|
||||||
|
);
|
||||||
|
|
||||||
|
const actionsEnabled = useMemo(
|
||||||
|
() => capabilities?.includes(AgentCapabilities.actions) ?? false,
|
||||||
|
[capabilities],
|
||||||
|
);
|
||||||
|
|
||||||
|
const artifactsEnabled = useMemo(
|
||||||
|
() => capabilities?.includes(AgentCapabilities.artifacts) ?? false,
|
||||||
|
[capabilities],
|
||||||
|
);
|
||||||
|
|
||||||
|
const ocrEnabled = useMemo(
|
||||||
|
() => capabilities?.includes(AgentCapabilities.ocr) ?? false,
|
||||||
|
[capabilities],
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileSearchEnabled = useMemo(
|
||||||
|
() => capabilities?.includes(AgentCapabilities.file_search) ?? false,
|
||||||
|
[capabilities],
|
||||||
|
);
|
||||||
|
|
||||||
|
const webSearchEnabled = useMemo(
|
||||||
|
() => capabilities?.includes(AgentCapabilities.web_search) ?? false,
|
||||||
|
[capabilities],
|
||||||
|
);
|
||||||
|
|
||||||
|
const codeEnabled = useMemo(
|
||||||
|
() => capabilities?.includes(AgentCapabilities.execute_code) ?? false,
|
||||||
|
[capabilities],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
ocrEnabled,
|
||||||
|
codeEnabled,
|
||||||
|
toolsEnabled,
|
||||||
|
actionsEnabled,
|
||||||
|
artifactsEnabled,
|
||||||
|
webSearchEnabled,
|
||||||
|
fileSearchEnabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
35
client/src/hooks/Agents/useGetAgentsConfig.ts
Normal file
35
client/src/hooks/Agents/useGetAgentsConfig.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { EModelEndpoint, AgentCapabilities } from 'librechat-data-provider';
|
||||||
|
import type { TAgentsEndpoint, TEndpointsConfig, TConfig } from 'librechat-data-provider';
|
||||||
|
import { useGetEndpointsQuery } from '~/data-provider';
|
||||||
|
|
||||||
|
interface UseGetAgentsConfigOptions {
|
||||||
|
endpointsConfig?: TEndpointsConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useGetAgentsConfig(options?: UseGetAgentsConfigOptions): {
|
||||||
|
agentsConfig?: TAgentsEndpoint | null;
|
||||||
|
endpointsConfig?: TEndpointsConfig | null;
|
||||||
|
} {
|
||||||
|
const { endpointsConfig: providedConfig } = options || {};
|
||||||
|
|
||||||
|
const { data: queriedConfig } = useGetEndpointsQuery({
|
||||||
|
enabled: !providedConfig,
|
||||||
|
});
|
||||||
|
|
||||||
|
const endpointsConfig = providedConfig || queriedConfig;
|
||||||
|
|
||||||
|
const agentsConfig = useMemo<TAgentsEndpoint | null>(() => {
|
||||||
|
const config = endpointsConfig?.[EModelEndpoint.agents] ?? null;
|
||||||
|
if (!config) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...(config as TConfig),
|
||||||
|
capabilities: Array.isArray(config.capabilities)
|
||||||
|
? config.capabilities.map((cap) => cap as unknown as AgentCapabilities)
|
||||||
|
: ([] as AgentCapabilities[]),
|
||||||
|
} as TAgentsEndpoint;
|
||||||
|
}, [endpointsConfig]);
|
||||||
|
|
||||||
|
return { agentsConfig, endpointsConfig };
|
||||||
|
}
|
||||||
|
|
@ -25,10 +25,10 @@ import useUpdateFiles from './useUpdateFiles';
|
||||||
|
|
||||||
type UseFileHandling = {
|
type UseFileHandling = {
|
||||||
fileSetter?: FileSetter;
|
fileSetter?: FileSetter;
|
||||||
fileFilter?: (file: File) => boolean;
|
|
||||||
additionalMetadata?: Record<string, string | undefined>;
|
|
||||||
overrideEndpoint?: EModelEndpoint;
|
overrideEndpoint?: EModelEndpoint;
|
||||||
|
fileFilter?: (file: File) => boolean;
|
||||||
overrideEndpointFileConfig?: EndpointFileConfig;
|
overrideEndpointFileConfig?: EndpointFileConfig;
|
||||||
|
additionalMetadata?: Record<string, string | undefined>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useFileHandling = (params?: UseFileHandling) => {
|
const useFileHandling = (params?: UseFileHandling) => {
|
||||||
|
|
@ -151,6 +151,10 @@ const useFileHandling = (params?: UseFileHandling) => {
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('endpoint', endpoint);
|
formData.append('endpoint', endpoint);
|
||||||
|
formData.append(
|
||||||
|
'original_endpoint',
|
||||||
|
conversation?.endpointType || conversation?.endpoint || '',
|
||||||
|
);
|
||||||
formData.append('file', extendedFile.file as File, encodeURIComponent(filename));
|
formData.append('file', extendedFile.file as File, encodeURIComponent(filename));
|
||||||
formData.append('file_id', extendedFile.file_id);
|
formData.append('file_id', extendedFile.file_id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ export const getAvailableEndpoints = (
|
||||||
|
|
||||||
/** Get the specified field from the endpoint config */
|
/** Get the specified field from the endpoint config */
|
||||||
export function getEndpointField<K extends keyof t.TConfig>(
|
export function getEndpointField<K extends keyof t.TConfig>(
|
||||||
endpointsConfig: t.TEndpointsConfig | undefined,
|
endpointsConfig: t.TEndpointsConfig | undefined | null,
|
||||||
endpoint: EModelEndpoint | string | null | undefined,
|
endpoint: EModelEndpoint | string | null | undefined,
|
||||||
property: K,
|
property: K,
|
||||||
): t.TConfig[K] | undefined {
|
): t.TConfig[K] | undefined {
|
||||||
|
|
@ -246,7 +246,7 @@ export function getIconKey({
|
||||||
endpointIconURL: iconURL,
|
endpointIconURL: iconURL,
|
||||||
}: {
|
}: {
|
||||||
endpoint?: string | null;
|
endpoint?: string | null;
|
||||||
endpointsConfig?: t.TEndpointsConfig;
|
endpointsConfig?: t.TEndpointsConfig | null;
|
||||||
endpointType?: string | null;
|
endpointType?: string | null;
|
||||||
endpointIconURL?: string;
|
endpointIconURL?: string;
|
||||||
}): keyof IconsRecord {
|
}): keyof IconsRecord {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue