mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
🤖 refactor: Side Panel Agent UI To Account For Ephemeral Agents (#9763)
* refactor: Remove unused imports and consolidate ephemeral agent logic * refactor: Side Panel agent handling to account for ephemeral agents for UI * refactor: Replace Constants.EPHEMERAL_AGENT_ID checks with isEphemeralAgent utility for consistency * ci: AgentPanel tests with additional mock configurations and utility functions
This commit is contained in:
parent
a6bf2b6ce3
commit
8a60e8990f
20 changed files with 87 additions and 77 deletions
|
|
@ -9,7 +9,7 @@ import {
|
||||||
useMCPToolsQuery,
|
useMCPToolsQuery,
|
||||||
} from '~/data-provider';
|
} from '~/data-provider';
|
||||||
import { useLocalize, useGetAgentsConfig, useMCPConnectionStatus } from '~/hooks';
|
import { useLocalize, useGetAgentsConfig, useMCPConnectionStatus } from '~/hooks';
|
||||||
import { Panel } from '~/common';
|
import { Panel, isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
const AgentPanelContext = createContext<AgentPanelContextType | undefined>(undefined);
|
const AgentPanelContext = createContext<AgentPanelContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
|
@ -32,15 +32,15 @@ export function AgentPanelProvider({ children }: { children: React.ReactNode })
|
||||||
|
|
||||||
const { data: startupConfig } = useGetStartupConfig();
|
const { data: startupConfig } = useGetStartupConfig();
|
||||||
const { data: actions } = useGetActionsQuery(EModelEndpoint.agents, {
|
const { data: actions } = useGetActionsQuery(EModelEndpoint.agents, {
|
||||||
enabled: !!agent_id,
|
enabled: !isEphemeralAgent(agent_id),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: regularTools } = useAvailableToolsQuery(EModelEndpoint.agents, {
|
const { data: regularTools } = useAvailableToolsQuery(EModelEndpoint.agents, {
|
||||||
enabled: !!agent_id,
|
enabled: !isEphemeralAgent(agent_id),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: mcpData } = useMCPToolsQuery({
|
const { data: mcpData } = useMCPToolsQuery({
|
||||||
enabled: !!agent_id && startupConfig?.mcpServers != null,
|
enabled: !isEphemeralAgent(agent_id) && startupConfig?.mcpServers != null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { agentsConfig, endpointsConfig } = useGetAgentsConfig();
|
const { agentsConfig, endpointsConfig } = useGetAgentsConfig();
|
||||||
|
|
@ -50,7 +50,7 @@ export function AgentPanelProvider({ children }: { children: React.ReactNode })
|
||||||
);
|
);
|
||||||
|
|
||||||
const { connectionStatus } = useMCPConnectionStatus({
|
const { connectionStatus } = useMCPConnectionStatus({
|
||||||
enabled: !!agent_id && mcpServerNames.length > 0,
|
enabled: !isEphemeralAgent(agent_id) && mcpServerNames.length > 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mcpServersMap = useMemo(() => {
|
const mcpServersMap = useMemo(() => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { TModelSpec, TStartupConfig } from 'librechat-data-provider';
|
import { TStartupConfig } from 'librechat-data-provider';
|
||||||
|
|
||||||
export interface Endpoint {
|
export interface Endpoint {
|
||||||
value: string;
|
value: string;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
import { FileSources, EModelEndpoint } from 'librechat-data-provider';
|
import { Constants, FileSources, EModelEndpoint } from 'librechat-data-provider';
|
||||||
import type { UseMutationResult } from '@tanstack/react-query';
|
import type { UseMutationResult } from '@tanstack/react-query';
|
||||||
import type * as InputNumberPrimitive from 'rc-input-number';
|
import type * as InputNumberPrimitive from 'rc-input-number';
|
||||||
import type { SetterOrUpdater, RecoilState } from 'recoil';
|
import type { SetterOrUpdater, RecoilState } from 'recoil';
|
||||||
|
|
@ -8,6 +8,10 @@ import type * as t from 'librechat-data-provider';
|
||||||
import type { LucideIcon } from 'lucide-react';
|
import type { LucideIcon } from 'lucide-react';
|
||||||
import type { TranslationKeys } from '~/hooks';
|
import type { TranslationKeys } from '~/hooks';
|
||||||
|
|
||||||
|
export function isEphemeralAgent(agentId: string | null | undefined): boolean {
|
||||||
|
return agentId == null || agentId === '' || agentId === Constants.EPHEMERAL_AGENT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConfigFieldDetail {
|
export interface ConfigFieldDetail {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,26 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { ChevronLeft } from 'lucide-react';
|
import { ChevronLeft } from 'lucide-react';
|
||||||
import { useForm, FormProvider } from 'react-hook-form';
|
import { useForm, FormProvider } from 'react-hook-form';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AuthTypeEnum,
|
AuthTypeEnum,
|
||||||
AuthorizationTypeEnum,
|
AuthorizationTypeEnum,
|
||||||
TokenExchangeMethodEnum,
|
TokenExchangeMethodEnum,
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
import {
|
import {
|
||||||
OGDialogTemplate,
|
|
||||||
TrashIcon,
|
|
||||||
OGDialog,
|
|
||||||
OGDialogTrigger,
|
|
||||||
Label,
|
Label,
|
||||||
|
OGDialog,
|
||||||
|
TrashIcon,
|
||||||
|
OGDialogTrigger,
|
||||||
useToastContext,
|
useToastContext,
|
||||||
|
OGDialogTemplate,
|
||||||
} from '@librechat/client';
|
} from '@librechat/client';
|
||||||
|
import type { ActionAuthForm } from '~/common';
|
||||||
import ActionsAuth from '~/components/SidePanel/Builder/ActionsAuth';
|
import ActionsAuth from '~/components/SidePanel/Builder/ActionsAuth';
|
||||||
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
||||||
import { useDeleteAgentAction } from '~/data-provider';
|
import { useDeleteAgentAction } from '~/data-provider';
|
||||||
import type { ActionAuthForm } from '~/common';
|
import { Panel, isEphemeralAgent } from '~/common';
|
||||||
import ActionsInput from './ActionsInput';
|
import ActionsInput from './ActionsInput';
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize } from '~/hooks';
|
||||||
import { Panel } from '~/common';
|
|
||||||
|
|
||||||
export default function ActionsPanel() {
|
export default function ActionsPanel() {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
|
|
@ -109,7 +108,7 @@ export default function ActionsPanel() {
|
||||||
<div className="absolute right-0 top-6">
|
<div className="absolute right-0 top-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!agent_id || !action.action_id}
|
disabled={isEphemeralAgent(agent_id) || !action.action_id}
|
||||||
className="btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium"
|
className="btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium"
|
||||||
>
|
>
|
||||||
<TrashIcon className="text-red-500" />
|
<TrashIcon className="text-red-500" />
|
||||||
|
|
@ -127,7 +126,7 @@ export default function ActionsPanel() {
|
||||||
}
|
}
|
||||||
selection={{
|
selection={{
|
||||||
selectHandler: () => {
|
selectHandler: () => {
|
||||||
if (!agent_id) {
|
if (isEphemeralAgent(agent_id)) {
|
||||||
return showToast({
|
return showToast({
|
||||||
message: localize('com_agents_no_agent_id_error'),
|
message: localize('com_agents_no_agent_id_error'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
|
|
@ -135,7 +134,7 @@ export default function ActionsPanel() {
|
||||||
}
|
}
|
||||||
deleteAgentAction.mutate({
|
deleteAgentAction.mutate({
|
||||||
action_id: action.action_id,
|
action_id: action.action_id,
|
||||||
agent_id,
|
agent_id: agent_id || '',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
selectClasses:
|
selectClasses:
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { useFileMapContext, useAgentPanelContext } from '~/Providers';
|
||||||
import AgentCategorySelector from './AgentCategorySelector';
|
import AgentCategorySelector from './AgentCategorySelector';
|
||||||
import Action from '~/components/SidePanel/Builder/Action';
|
import Action from '~/components/SidePanel/Builder/Action';
|
||||||
import { useLocalize, useVisibleTools } from '~/hooks';
|
import { useLocalize, useVisibleTools } from '~/hooks';
|
||||||
|
import { Panel, isEphemeralAgent } from '~/common';
|
||||||
import { useGetAgentFiles } from '~/data-provider';
|
import { useGetAgentFiles } from '~/data-provider';
|
||||||
import { icons } from '~/hooks/Endpoint/Icons';
|
import { icons } from '~/hooks/Endpoint/Icons';
|
||||||
import Instructions from './Instructions';
|
import Instructions from './Instructions';
|
||||||
|
|
@ -29,7 +30,6 @@ import Artifacts from './Artifacts';
|
||||||
import AgentTool from './AgentTool';
|
import AgentTool from './AgentTool';
|
||||||
import CodeForm from './Code/Form';
|
import CodeForm from './Code/Form';
|
||||||
import MCPTools from './MCPTools';
|
import MCPTools from './MCPTools';
|
||||||
import { Panel } from '~/common';
|
|
||||||
|
|
||||||
const labelClass = 'mb-2 text-token-text-primary block font-medium';
|
const labelClass = 'mb-2 text-token-text-primary block font-medium';
|
||||||
const inputClass = cn(
|
const inputClass = cn(
|
||||||
|
|
@ -149,7 +149,7 @@ export default function AgentConfig({ createMutation }: Pick<AgentPanelProps, 'c
|
||||||
}, [agent, agent_id, mergedFileMap]);
|
}, [agent, agent_id, mergedFileMap]);
|
||||||
|
|
||||||
const handleAddActions = useCallback(() => {
|
const handleAddActions = useCallback(() => {
|
||||||
if (!agent_id) {
|
if (isEphemeralAgent(agent_id)) {
|
||||||
showToast({
|
showToast({
|
||||||
message: localize('com_assistants_actions_disabled'),
|
message: localize('com_assistants_actions_disabled'),
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
|
|
@ -370,7 +370,7 @@ export default function AgentConfig({ createMutation }: Pick<AgentPanelProps, 'c
|
||||||
{(actionsEnabled ?? false) && (
|
{(actionsEnabled ?? false) && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!agent_id}
|
disabled={isEphemeralAgent(agent_id)}
|
||||||
onClick={handleAddActions}
|
onClick={handleAddActions}
|
||||||
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
||||||
aria-haspopup="dialog"
|
aria-haspopup="dialog"
|
||||||
|
|
|
||||||
|
|
@ -37,22 +37,28 @@ jest.mock('librechat-data-provider', () => {
|
||||||
dataService: {
|
dataService: {
|
||||||
updateAgent: jest.fn(),
|
updateAgent: jest.fn(),
|
||||||
},
|
},
|
||||||
Tools: {
|
Tools: actualModule.Tools || {
|
||||||
execute_code: 'execute_code',
|
execute_code: 'execute_code',
|
||||||
file_search: 'file_search',
|
file_search: 'file_search',
|
||||||
web_search: 'web_search',
|
web_search: 'web_search',
|
||||||
},
|
},
|
||||||
Constants: {
|
Constants: actualModule.Constants || {
|
||||||
EPHEMERAL_AGENT_ID: 'ephemeral',
|
EPHEMERAL_AGENT_ID: 'ephemeral',
|
||||||
},
|
},
|
||||||
SystemRoles: {
|
SystemRoles: actualModule.SystemRoles || {
|
||||||
ADMIN: 'ADMIN',
|
ADMIN: 'ADMIN',
|
||||||
},
|
},
|
||||||
EModelEndpoint: {
|
EModelEndpoint: actualModule.EModelEndpoint || {
|
||||||
agents: 'agents',
|
agents: 'agents',
|
||||||
chatGPTBrowser: 'chatGPTBrowser',
|
chatGPTBrowser: 'chatGPTBrowser',
|
||||||
gptPlugins: 'gptPlugins',
|
gptPlugins: 'gptPlugins',
|
||||||
},
|
},
|
||||||
|
ResourceType: actualModule.ResourceType || {
|
||||||
|
AGENT: 'agent',
|
||||||
|
},
|
||||||
|
PermissionBits: actualModule.PermissionBits || {
|
||||||
|
EDIT: 2,
|
||||||
|
},
|
||||||
isAssistantsEndpoint: jest.fn(() => false),
|
isAssistantsEndpoint: jest.fn(() => false),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -97,6 +103,13 @@ jest.mock('~/hooks', () => ({
|
||||||
useAuthContext: () => ({ user: { id: 'user-123', role: 'USER' } }),
|
useAuthContext: () => ({ user: { id: 'user-123', role: 'USER' } }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('~/hooks/useResourcePermissions', () => ({
|
||||||
|
useResourcePermissions: () => ({
|
||||||
|
hasPermission: jest.fn(() => true),
|
||||||
|
isLoading: false,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('~/Providers/AgentPanelContext', () => ({
|
jest.mock('~/Providers/AgentPanelContext', () => ({
|
||||||
useAgentPanelContext: () => ({
|
useAgentPanelContext: () => ({
|
||||||
activePanel: 'builder',
|
activePanel: 'builder',
|
||||||
|
|
@ -109,6 +122,9 @@ jest.mock('~/Providers/AgentPanelContext', () => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('~/common', () => ({
|
jest.mock('~/common', () => ({
|
||||||
|
isEphemeralAgent: (agentId: string | null | undefined): boolean => {
|
||||||
|
return agentId == null || agentId === '' || agentId === 'ephemeral';
|
||||||
|
},
|
||||||
Panel: {
|
Panel: {
|
||||||
model: 'model',
|
model: 'model',
|
||||||
builder: 'builder',
|
builder: 'builder',
|
||||||
|
|
@ -199,6 +215,10 @@ jest.mock('~/data-provider', () => {
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
useGetAgentByIdQuery: jest.fn(),
|
useGetAgentByIdQuery: jest.fn(),
|
||||||
|
useGetExpandedAgentByIdQuery: jest.fn(() => ({
|
||||||
|
data: null,
|
||||||
|
isInitialLoading: false,
|
||||||
|
})),
|
||||||
useUpdateAgentMutation: actual.useUpdateAgentMutation,
|
useUpdateAgentMutation: actual.useUpdateAgentMutation,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { useWatch, useForm, FormProvider } from 'react-hook-form';
|
||||||
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
|
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
|
||||||
import {
|
import {
|
||||||
Tools,
|
Tools,
|
||||||
Constants,
|
|
||||||
SystemRoles,
|
SystemRoles,
|
||||||
ResourceType,
|
ResourceType,
|
||||||
EModelEndpoint,
|
EModelEndpoint,
|
||||||
|
|
@ -25,11 +24,11 @@ import { useSelectAgent, useLocalize, useAuthContext } from '~/hooks';
|
||||||
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
||||||
import AgentPanelSkeleton from './AgentPanelSkeleton';
|
import AgentPanelSkeleton from './AgentPanelSkeleton';
|
||||||
import AdvancedPanel from './Advanced/AdvancedPanel';
|
import AdvancedPanel from './Advanced/AdvancedPanel';
|
||||||
|
import { Panel, isEphemeralAgent } from '~/common';
|
||||||
import AgentConfig from './AgentConfig';
|
import AgentConfig from './AgentConfig';
|
||||||
import AgentSelect from './AgentSelect';
|
import AgentSelect from './AgentSelect';
|
||||||
import AgentFooter from './AgentFooter';
|
import AgentFooter from './AgentFooter';
|
||||||
import ModelPanel from './ModelPanel';
|
import ModelPanel from './ModelPanel';
|
||||||
import { Panel } from '~/common';
|
|
||||||
|
|
||||||
export default function AgentPanel() {
|
export default function AgentPanel() {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
|
|
@ -57,11 +56,7 @@ export default function AgentPanel() {
|
||||||
const canEdit = hasPermission(PermissionBits.EDIT);
|
const canEdit = hasPermission(PermissionBits.EDIT);
|
||||||
|
|
||||||
const expandedAgentQuery = useGetExpandedAgentByIdQuery(current_agent_id ?? '', {
|
const expandedAgentQuery = useGetExpandedAgentByIdQuery(current_agent_id ?? '', {
|
||||||
enabled:
|
enabled: !isEphemeralAgent(current_agent_id) && canEdit && !permissionsLoading,
|
||||||
!!(current_agent_id ?? '') &&
|
|
||||||
current_agent_id !== Constants.EPHEMERAL_AGENT_ID &&
|
|
||||||
canEdit &&
|
|
||||||
!permissionsLoading,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const agentQuery = canEdit && expandedAgentQuery.data ? expandedAgentQuery : basicAgentQuery;
|
const agentQuery = canEdit && expandedAgentQuery.data ? expandedAgentQuery : basicAgentQuery;
|
||||||
|
|
@ -298,7 +293,7 @@ export default function AgentPanel() {
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="submit"
|
variant="submit"
|
||||||
disabled={!agent_id || agentQuery.isInitialLoading}
|
disabled={isEphemeralAgent(agent_id) || agentQuery.isInitialLoading}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleSelectAgent();
|
handleSelectAgent();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { AgentPanelProvider, useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
import { AgentPanelProvider, useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
||||||
|
import { Panel, isEphemeralAgent } from '~/common';
|
||||||
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';
|
||||||
import AgentPanel from './AgentPanel';
|
import AgentPanel from './AgentPanel';
|
||||||
import MCPPanel from './MCPPanel';
|
import MCPPanel from './MCPPanel';
|
||||||
import { Panel } from '~/common';
|
|
||||||
|
|
||||||
export default function AgentPanelSwitch() {
|
export default function AgentPanelSwitch() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -21,7 +21,7 @@ function AgentPanelSwitchWithContext() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const agent_id = conversation?.agent_id ?? '';
|
const agent_id = conversation?.agent_id ?? '';
|
||||||
if (agent_id) {
|
if (!isEphemeralAgent(agent_id)) {
|
||||||
setCurrentAgentId(agent_id);
|
setCurrentAgentId(agent_id);
|
||||||
}
|
}
|
||||||
}, [setCurrentAgentId, conversation?.agent_id]);
|
}, [setCurrentAgentId, conversation?.agent_id]);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { useFileHandling, useLocalize, useLazyEffect } from '~/hooks';
|
||||||
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
||||||
import { useGetFileConfig } from '~/data-provider';
|
import { useGetFileConfig } from '~/data-provider';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
const tool_resource = EToolResources.execute_code;
|
const tool_resource = EToolResources.execute_code;
|
||||||
|
|
||||||
|
|
@ -85,7 +86,7 @@ export default function Files({
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!agent_id || codeChecked === false}
|
disabled={isEphemeralAgent(agent_id) || codeChecked === false}
|
||||||
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
||||||
onClick={handleButtonClick}
|
onClick={handleButtonClick}
|
||||||
>
|
>
|
||||||
|
|
@ -96,7 +97,7 @@ export default function Files({
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
disabled={!agent_id || codeChecked === false}
|
disabled={isEphemeralAgent(agent_id) || codeChecked === false}
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { logger, getDefaultAgentFormValues } from '~/utils';
|
||||||
import { useLocalize, useSetIndexOptions } from '~/hooks';
|
import { useLocalize, useSetIndexOptions } from '~/hooks';
|
||||||
import { useDeleteAgentMutation } from '~/data-provider';
|
import { useDeleteAgentMutation } from '~/data-provider';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
export default function DeleteButton({
|
export default function DeleteButton({
|
||||||
agent_id,
|
agent_id,
|
||||||
|
|
@ -76,7 +77,7 @@ export default function DeleteButton({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!agent_id) {
|
if (isEphemeralAgent(agent_id)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { CopyIcon } from 'lucide-react';
|
import { CopyIcon } from 'lucide-react';
|
||||||
import { useToastContext, Button } from '@librechat/client';
|
import { useToastContext, Button } from '@librechat/client';
|
||||||
import { useDuplicateAgentMutation } from '~/data-provider';
|
import { useDuplicateAgentMutation } from '~/data-provider';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize } from '~/hooks';
|
||||||
|
|
||||||
export default function DuplicateAgent({ agent_id }: { agent_id: string }) {
|
export default function DuplicateAgent({ agent_id }: { agent_id: string }) {
|
||||||
|
|
@ -23,7 +24,7 @@ export default function DuplicateAgent({ agent_id }: { agent_id: string }) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!agent_id) {
|
if (isEphemeralAgent(agent_id)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import { useFileHandling, useLocalize, useLazyEffect, useSharePointFileHandling
|
||||||
import { useGetFileConfig, useGetStartupConfig } from '~/data-provider';
|
import { useGetFileConfig, useGetStartupConfig } from '~/data-provider';
|
||||||
import { SharePointPickerDialog } from '~/components/SharePoint';
|
import { SharePointPickerDialog } from '~/components/SharePoint';
|
||||||
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
||||||
|
import { ESide, isEphemeralAgent } from '~/common';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
import { ESide } from '~/common';
|
|
||||||
|
|
||||||
export default function FileContext({
|
export default function FileContext({
|
||||||
agent_id,
|
agent_id,
|
||||||
|
|
@ -156,7 +156,7 @@ export default function FileContext({
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!agent_id}
|
disabled={isEphemeralAgent(agent_id)}
|
||||||
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
className="btn btn-neutral border-token-border-light relative h-9 w-full rounded-lg font-medium"
|
||||||
onClick={handleLocalFileClick}
|
onClick={handleLocalFileClick}
|
||||||
>
|
>
|
||||||
|
|
@ -173,7 +173,7 @@ export default function FileContext({
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
disabled={!agent_id}
|
disabled={isEphemeralAgent(agent_id)}
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { SharePointPickerDialog } from '~/components/SharePoint';
|
||||||
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
||||||
import FileSearchCheckbox from './FileSearchCheckbox';
|
import FileSearchCheckbox from './FileSearchCheckbox';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
export default function FileSearch({
|
export default function FileSearch({
|
||||||
agent_id,
|
agent_id,
|
||||||
|
|
@ -69,7 +70,7 @@ export default function FileSearch({
|
||||||
const isUploadDisabled = endpointFileConfig.disabled ?? false;
|
const isUploadDisabled = endpointFileConfig.disabled ?? false;
|
||||||
|
|
||||||
const sharePointEnabled = startupConfig?.sharePointFilePickerEnabled;
|
const sharePointEnabled = startupConfig?.sharePointFilePickerEnabled;
|
||||||
const disabledUploadButton = !agent_id || fileSearchChecked === false;
|
const disabledUploadButton = isEphemeralAgent(agent_id) || fileSearchChecked === false;
|
||||||
|
|
||||||
const handleSharePointFilesSelected = async (sharePointFiles: any[]) => {
|
const handleSharePointFilesSelected = async (sharePointFiles: any[]) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,19 @@ import {
|
||||||
TokenExchangeMethodEnum,
|
TokenExchangeMethodEnum,
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
import {
|
import {
|
||||||
OGDialog,
|
|
||||||
OGDialogTrigger,
|
|
||||||
Label,
|
Label,
|
||||||
OGDialogTemplate,
|
OGDialog,
|
||||||
TrashIcon,
|
TrashIcon,
|
||||||
|
OGDialogTrigger,
|
||||||
useToastContext,
|
useToastContext,
|
||||||
|
OGDialogTemplate,
|
||||||
} from '@librechat/client';
|
} from '@librechat/client';
|
||||||
|
import type { MCPForm } from '~/common';
|
||||||
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
||||||
import { defaultMCPFormValues } from '~/common/mcp';
|
import { defaultMCPFormValues } from '~/common/mcp';
|
||||||
import type { MCPForm } from '~/common';
|
import { Panel, isEphemeralAgent } from '~/common';
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize } from '~/hooks';
|
||||||
import MCPInput from './MCPInput';
|
import MCPInput from './MCPInput';
|
||||||
import { Panel } from '~/common';
|
|
||||||
// TODO: Add MCP delete (for now mocked for ui)
|
|
||||||
// import { useDeleteAgentMCP } from '~/data-provider';
|
|
||||||
|
|
||||||
function useDeleteAgentMCP({
|
function useDeleteAgentMCP({
|
||||||
onSuccess,
|
onSuccess,
|
||||||
|
|
@ -127,7 +125,7 @@ export default function MCPPanel() {
|
||||||
<div className="absolute right-0 top-6">
|
<div className="absolute right-0 top-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!agent_id || !mcp.mcp_id}
|
disabled={isEphemeralAgent(agent_id) || !mcp.mcp_id}
|
||||||
className="btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium"
|
className="btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium"
|
||||||
>
|
>
|
||||||
<TrashIcon className="text-red-500" />
|
<TrashIcon className="text-red-500" />
|
||||||
|
|
@ -145,7 +143,7 @@ export default function MCPPanel() {
|
||||||
}
|
}
|
||||||
selection={{
|
selection={{
|
||||||
selectHandler: () => {
|
selectHandler: () => {
|
||||||
if (!agent_id) {
|
if (isEphemeralAgent(agent_id)) {
|
||||||
return showToast({
|
return showToast({
|
||||||
message: localize('com_agents_no_agent_id_error'),
|
message: localize('com_agents_no_agent_id_error'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
|
|
@ -153,7 +151,7 @@ export default function MCPPanel() {
|
||||||
}
|
}
|
||||||
deleteAgentMCP.mutate({
|
deleteAgentMCP.mutate({
|
||||||
mcp_id: mcp.mcp_id,
|
mcp_id: mcp.mcp_id,
|
||||||
agent_id,
|
agent_id: agent_id || '',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
selectClasses:
|
selectClasses:
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@ import { useLocalize } from '~/hooks';
|
||||||
import { useToastContext } from '@librechat/client';
|
import { useToastContext } from '@librechat/client';
|
||||||
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
||||||
import MCP from '~/components/SidePanel/Builder/MCP';
|
import MCP from '~/components/SidePanel/Builder/MCP';
|
||||||
import { Panel } from '~/common';
|
import { Panel, isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
export default function MCPSection() {
|
export default function MCPSection() {
|
||||||
const { showToast } = useToastContext();
|
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
|
const { showToast } = useToastContext();
|
||||||
const { mcps = [], agent_id, setMcp, setActivePanel } = useAgentPanelContext();
|
const { mcps = [], agent_id, setMcp, setActivePanel } = useAgentPanelContext();
|
||||||
|
|
||||||
const handleAddMCP = useCallback(() => {
|
const handleAddMCP = useCallback(() => {
|
||||||
if (!agent_id) {
|
if (isEphemeralAgent(agent_id)) {
|
||||||
showToast({
|
showToast({
|
||||||
message: localize('com_agents_mcps_disabled'),
|
message: localize('com_agents_mcps_disabled'),
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,12 @@
|
||||||
import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import {
|
import { QueryKeys, dataService, EModelEndpoint, PermissionBits } from 'librechat-data-provider';
|
||||||
Constants,
|
|
||||||
QueryKeys,
|
|
||||||
dataService,
|
|
||||||
EModelEndpoint,
|
|
||||||
PermissionBits,
|
|
||||||
} from 'librechat-data-provider';
|
|
||||||
import type {
|
import type {
|
||||||
QueryObserverResult,
|
QueryObserverResult,
|
||||||
UseQueryOptions,
|
UseQueryOptions,
|
||||||
UseInfiniteQueryOptions,
|
UseInfiniteQueryOptions,
|
||||||
} from '@tanstack/react-query';
|
} from '@tanstack/react-query';
|
||||||
import type t from 'librechat-data-provider';
|
import type t from 'librechat-data-provider';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AGENTS
|
* AGENTS
|
||||||
|
|
@ -73,11 +68,7 @@ export const useGetAgentByIdQuery = (
|
||||||
agent_id: string | null | undefined,
|
agent_id: string | null | undefined,
|
||||||
config?: UseQueryOptions<t.Agent>,
|
config?: UseQueryOptions<t.Agent>,
|
||||||
): QueryObserverResult<t.Agent> => {
|
): QueryObserverResult<t.Agent> => {
|
||||||
const isValidAgentId = !!(
|
const isValidAgentId = !!agent_id && !isEphemeralAgent(agent_id);
|
||||||
agent_id &&
|
|
||||||
agent_id !== '' &&
|
|
||||||
agent_id !== Constants.EPHEMERAL_AGENT_ID
|
|
||||||
);
|
|
||||||
|
|
||||||
return useQuery<t.Agent>(
|
return useQuery<t.Agent>(
|
||||||
[QueryKeys.agent, agent_id],
|
[QueryKeys.agent, agent_id],
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { QueryKeys, DynamicQueryKeys, dataService } from 'librechat-data-provider';
|
import { QueryKeys, DynamicQueryKeys, dataService } from 'librechat-data-provider';
|
||||||
import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query';
|
import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query';
|
||||||
import type t from 'librechat-data-provider';
|
import type t from 'librechat-data-provider';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
import { addFileToCache } from '~/utils';
|
import { addFileToCache } from '~/utils';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
|
|
@ -32,7 +33,7 @@ export const useGetAgentFiles = <TData = t.TFile[]>(
|
||||||
refetchOnReconnect: false,
|
refetchOnReconnect: false,
|
||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
...config,
|
...config,
|
||||||
enabled: (config?.enabled ?? true) === true && queriesEnabled && !!agentId,
|
enabled: (config?.enabled ?? true) === true && queriesEnabled && !isEphemeralAgent(agentId),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Tools, Constants, EToolResources } from 'librechat-data-provider';
|
import { Tools, EToolResources } from 'librechat-data-provider';
|
||||||
import type { TEphemeralAgent } from 'librechat-data-provider';
|
import type { TEphemeralAgent } from 'librechat-data-provider';
|
||||||
import { useGetAgentByIdQuery } from '~/data-provider';
|
import { useGetAgentByIdQuery } from '~/data-provider';
|
||||||
import { useAgentsMapContext } from '~/Providers';
|
import { useAgentsMapContext } from '~/Providers';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
interface AgentToolPermissionsResult {
|
interface AgentToolPermissionsResult {
|
||||||
fileSearchAllowedByAgent: boolean;
|
fileSearchAllowedByAgent: boolean;
|
||||||
|
|
@ -10,10 +11,6 @@ interface AgentToolPermissionsResult {
|
||||||
tools: string[] | undefined;
|
tools: string[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEphemeralAgent(agentId: string | null | undefined): boolean {
|
|
||||||
return agentId == null || agentId === '' || agentId === Constants.EPHEMERAL_AGENT_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to determine whether specific tools are allowed for a given agent.
|
* Hook to determine whether specific tools are allowed for a given agent.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import type { DropTargetMonitor } from 'react-dnd';
|
||||||
import type * as t from 'librechat-data-provider';
|
import type * as t from 'librechat-data-provider';
|
||||||
import store, { ephemeralAgentByConvoId } from '~/store';
|
import store, { ephemeralAgentByConvoId } from '~/store';
|
||||||
import useFileHandling from './useFileHandling';
|
import useFileHandling from './useFileHandling';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
export default function useDragHelpers() {
|
export default function useDragHelpers() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
@ -78,7 +79,7 @@ export default function useDragHelpers() {
|
||||||
let fileSearchAllowedByAgent = true;
|
let fileSearchAllowedByAgent = true;
|
||||||
let codeAllowedByAgent = true;
|
let codeAllowedByAgent = true;
|
||||||
|
|
||||||
if (agentId && agentId !== Constants.EPHEMERAL_AGENT_ID) {
|
if (agentId && !isEphemeralAgent(agentId)) {
|
||||||
/** Agent data from cache */
|
/** Agent data from cache */
|
||||||
const agent = queryClient.getQueryData<t.Agent>([QueryKeys.agent, agentId]);
|
const agent = queryClient.getQueryData<t.Agent>([QueryKeys.agent, agentId]);
|
||||||
if (agent) {
|
if (agent) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import {
|
import {
|
||||||
Constants,
|
|
||||||
isAgentsEndpoint,
|
isAgentsEndpoint,
|
||||||
tQueryParamsSchema,
|
tQueryParamsSchema,
|
||||||
isAssistantsEndpoint,
|
isAssistantsEndpoint,
|
||||||
} from 'librechat-data-provider';
|
} from 'librechat-data-provider';
|
||||||
import type { TConversation, TPreset } from 'librechat-data-provider';
|
import type { TConversation, TPreset } from 'librechat-data-provider';
|
||||||
|
import { isEphemeralAgent } from '~/common';
|
||||||
|
|
||||||
const allowedParams = Object.keys(tQueryParamsSchema.shape);
|
const allowedParams = Object.keys(tQueryParamsSchema.shape);
|
||||||
export default function createChatSearchParams(
|
export default function createChatSearchParams(
|
||||||
|
|
@ -33,7 +33,7 @@ export default function createChatSearchParams(
|
||||||
if (
|
if (
|
||||||
isAgentsEndpoint(endpoint) &&
|
isAgentsEndpoint(endpoint) &&
|
||||||
conversation.agent_id &&
|
conversation.agent_id &&
|
||||||
conversation.agent_id !== Constants.EPHEMERAL_AGENT_ID
|
!isEphemeralAgent(conversation.agent_id)
|
||||||
) {
|
) {
|
||||||
return new URLSearchParams({ agent_id: String(conversation.agent_id) });
|
return new URLSearchParams({ agent_id: String(conversation.agent_id) });
|
||||||
} else if (isAssistantsEndpoint(endpoint) && conversation.assistant_id) {
|
} else if (isAssistantsEndpoint(endpoint) && conversation.assistant_id) {
|
||||||
|
|
@ -53,7 +53,7 @@ export default function createChatSearchParams(
|
||||||
|
|
||||||
const paramMap: Record<string, any> = {};
|
const paramMap: Record<string, any> = {};
|
||||||
allowedParams.forEach((key) => {
|
allowedParams.forEach((key) => {
|
||||||
if (key === 'agent_id' && conversation.agent_id === Constants.EPHEMERAL_AGENT_ID) {
|
if (key === 'agent_id' && isEphemeralAgent(conversation.agent_id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (key !== 'endpoint' && key !== 'model') {
|
if (key !== 'endpoint' && key !== 'model') {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue