🔄 fix: Avatar & Error Handling Enhancements (#6687)

* fix: Ensure safe access to agent capabilities in AgentConfig

* fix: don't show agent builder if agents endpoint is not enabled

* fix: Improve error logging for MCP tool calls

* fix: Enhance error message for MCP tool failures

* feat: Add optional spec and iconURL properties to TEndpointOption type

* chore: Update condition to use constant for new conversation parameter

* feat: Enhance abort error handling with additional endpoint options to properly render error message fields

* fix: Throw error instead of returning message for failed MCP tool calls

* refactor: separate logic to generate new S3 URLs for expired links

* feat: Implement S3 URL refresh for user avatars with error handling

* fix: authcontext error in chats where agent chain is used

* refactor: streamline balance configuration logic in getBalanceConfig function

* fix: enhance icon resolution logic in SpecIcon component

* fix: allow null values for spec and iconURL in TEndpointOption type

* fix: update balance check to allow null tokenCredits
This commit is contained in:
Danny Avila 2025-04-02 18:44:13 -04:00 committed by GitHub
parent cfa44de1c9
commit c4f1da26b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 184 additions and 75 deletions

View file

@ -20,7 +20,7 @@ const SpecIcon: React.FC<SpecIconProps> = ({ currentSpec, endpointsConfig }) =>
let Icon: IconType;
if (!iconURL.includes('http')) {
Icon = (icons[iconKey] ?? icons.unknown) as IconType;
Icon = (icons[iconURL] ?? icons[iconKey] ?? icons.unknown) as IconType;
} else if (iconURL) {
return (
<URLIcon
@ -32,7 +32,7 @@ const SpecIcon: React.FC<SpecIconProps> = ({ currentSpec, endpointsConfig }) =>
/>
);
} else {
Icon = (icons[endpoint ?? ''] ?? icons.unknown) as IconType;
Icon = (icons[endpoint ?? ''] ?? icons[iconKey] ?? icons.unknown) as IconType;
}
return (

View file

@ -1,13 +1,16 @@
import React, { useMemo } from 'react';
import { EModelEndpoint } from 'librechat-data-provider';
import type { TMessage } from 'librechat-data-provider';
import MessageIcon from '~/components/Share/MessageIcon';
import { useAgentsMapContext } from '~/Providers';
import Icon from '~/components/Endpoints/Icon';
import { useLocalize } from '~/hooks';
interface AgentUpdateProps {
currentAgentId: string;
}
const AgentUpdate: React.FC<AgentUpdateProps> = ({ currentAgentId }) => {
const localize = useLocalize();
const agentsMap = useAgentsMapContext() || {};
const currentAgent = useMemo(() => agentsMap[currentAgentId], [agentsMap, currentAgentId]);
if (!currentAgentId) {
@ -23,14 +26,19 @@ const AgentUpdate: React.FC<AgentUpdateProps> = ({ currentAgentId }) => {
</div>
<div className="my-4 flex items-center gap-2">
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
<Icon
endpoint={EModelEndpoint.agents}
agentName={currentAgent?.name ?? ''}
iconURL={currentAgent?.avatar?.filepath}
isCreatedByUser={false}
<MessageIcon
message={
{
endpoint: EModelEndpoint.agents,
isCreatedByUser: false,
} as TMessage
}
agent={currentAgent}
/>
</div>
<div className="font-medium text-text-primary">{currentAgent?.name}</div>
<div className="text-base font-medium text-text-primary">
{currentAgent?.name || localize('com_ui_agent')}
</div>
</div>
</div>
);

View file

@ -66,7 +66,7 @@ const ConvoIconURL: React.FC<ConvoIconURLProps> = ({
agentName={agentName}
iconURL={endpointIconURL}
assistantName={assistantName}
avatar={assistantAvatar ?? agentAvatar}
avatar={assistantAvatar || agentAvatar}
/>
)}
</div>

View file

@ -1,5 +1,5 @@
import { useMemo } from 'react';
import type { TMessage, TPreset, Assistant, Agent } from 'librechat-data-provider';
import type { TMessage, Assistant, Agent } from 'librechat-data-provider';
import type { TMessageProps } from '~/common';
import MessageEndpointIcon from '../Endpoints/MessageEndpointIcon';
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
@ -14,11 +14,6 @@ export default function MessageIcon(
) {
const { message, conversation, assistant, agent } = props;
const assistantName = assistant ? (assistant.name as string | undefined) : '';
const assistantAvatar = assistant ? (assistant.metadata?.avatar as string | undefined) : '';
const agentName = agent ? (agent.name as string | undefined) : '';
const agentAvatar = agent ? (agent.metadata?.avatar as string | undefined) : '';
const messageSettings = useMemo(
() => ({
...(conversation ?? {}),
@ -33,7 +28,27 @@ export default function MessageIcon(
const iconURL = messageSettings.iconURL ?? '';
let endpoint = messageSettings.endpoint;
endpoint = getIconEndpoint({ endpointsConfig: undefined, iconURL, endpoint });
const assistantName = (assistant ? assistant.name : '') ?? '';
const assistantAvatar = (assistant ? assistant.metadata?.avatar : '') ?? '';
const agentName = (agent ? agent.name : '') ?? '';
const agentAvatar = (agent ? agent?.avatar?.filepath : '') ?? '';
const avatarURL = useMemo(() => {
let result = '';
if (assistant) {
result = assistantAvatar;
} else if (agent) {
result = agentAvatar;
}
return result;
}, [assistant, agent, assistantAvatar, agentAvatar]);
console.log('MessageIcon', {
endpoint,
iconURL,
assistantName,
assistantAvatar,
agentName,
agentAvatar,
});
if (message?.isCreatedByUser !== true && iconURL && iconURL.includes('http')) {
return (
<ConvoIconURL
@ -68,7 +83,7 @@ export default function MessageIcon(
<MessageEndpointIcon
{...messageSettings}
endpoint={endpoint}
iconURL={assistant == null ? undefined : assistantAvatar}
iconURL={avatarURL}
model={message?.model ?? conversation?.model}
assistantName={assistantName}
agentName={agentName}

View file

@ -2,12 +2,13 @@ import { X, Link2, PlusCircle } from 'lucide-react';
import { EModelEndpoint } from 'librechat-data-provider';
import React, { useState, useMemo, useCallback, useEffect } from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import type { TMessage } from 'librechat-data-provider';
import type { AgentForm, OptionWithIcon } from '~/common';
import ControlCombobox from '~/components/ui/ControlCombobox';
import { HoverCard, HoverCardPortal, HoverCardContent, HoverCardTrigger } from '~/components/ui';
import MessageIcon from '~/components/Share/MessageIcon';
import { CircleHelpIcon } from '~/components/svg';
import { useAgentsMapContext } from '~/Providers';
import Icon from '~/components/Endpoints/Icon';
import { useLocalize } from '~/hooks';
import { ESide } from '~/common';
@ -37,11 +38,14 @@ const AgentChain: React.FC<AgentChainProps> = ({ field, currentAgentId }) => {
label: agent?.name || '',
value: agent?.id,
icon: (
<Icon
endpoint={EModelEndpoint.agents}
agentName={agent?.name ?? ''}
iconURL={agent?.avatar?.filepath}
isCreatedByUser={false}
<MessageIcon
message={
{
endpoint: EModelEndpoint.agents,
isCreatedByUser: false,
} as TMessage
}
agent={agent}
/>
),
}) as OptionWithIcon,
@ -88,11 +92,14 @@ const AgentChain: React.FC<AgentChainProps> = ({ field, currentAgentId }) => {
<div className="flex h-10 items-center justify-between rounded-md border border-border-medium bg-surface-primary-contrast px-3 py-2">
<div className="flex items-center gap-2">
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
<Icon
endpoint={EModelEndpoint.agents}
agentName={getAgentDetails(currentAgentId)?.name ?? ''}
iconURL={getAgentDetails(currentAgentId)?.avatar?.filepath}
isCreatedByUser={false}
<MessageIcon
message={
{
endpoint: EModelEndpoint.agents,
isCreatedByUser: false,
} as TMessage
}
agent={currentAgentId ? agentsMap[currentAgentId] : undefined}
/>
</div>
<div className="font-medium text-text-primary">
@ -114,11 +121,14 @@ const AgentChain: React.FC<AgentChainProps> = ({ field, currentAgentId }) => {
items={selectableAgents}
displayValue={getAgentDetails(agentId)?.name ?? ''}
SelectIcon={
<Icon
endpoint={EModelEndpoint.agents}
isCreatedByUser={false}
agentName={getAgentDetails(agentId)?.name ?? ''}
iconURL={getAgentDetails(agentId)?.avatar?.filepath}
<MessageIcon
message={
{
endpoint: EModelEndpoint.agents,
isCreatedByUser: false,
} as TMessage
}
agent={agentId ? agentsMap[agentId] : undefined}
/>
}
className="flex-1 border-border-heavy"

View file

@ -53,27 +53,27 @@ export default function AgentConfig({
const agent_id = useWatch({ control, name: 'id' });
const toolsEnabled = useMemo(
() => agentsConfig?.capabilities.includes(AgentCapabilities.tools),
() => agentsConfig?.capabilities?.includes(AgentCapabilities.tools) ?? false,
[agentsConfig],
);
const actionsEnabled = useMemo(
() => agentsConfig?.capabilities.includes(AgentCapabilities.actions),
() => agentsConfig?.capabilities?.includes(AgentCapabilities.actions) ?? false,
[agentsConfig],
);
const artifactsEnabled = useMemo(
() => agentsConfig?.capabilities.includes(AgentCapabilities.artifacts) ?? false,
() => agentsConfig?.capabilities?.includes(AgentCapabilities.artifacts) ?? false,
[agentsConfig],
);
const ocrEnabled = useMemo(
() => agentsConfig?.capabilities.includes(AgentCapabilities.ocr) ?? false,
() => agentsConfig?.capabilities?.includes(AgentCapabilities.ocr) ?? false,
[agentsConfig],
);
const fileSearchEnabled = useMemo(
() => agentsConfig?.capabilities.includes(AgentCapabilities.file_search) ?? false,
() => agentsConfig?.capabilities?.includes(AgentCapabilities.file_search) ?? false,
[agentsConfig],
);
const codeEnabled = useMemo(
() => agentsConfig?.capabilities.includes(AgentCapabilities.execute_code) ?? false,
() => agentsConfig?.capabilities?.includes(AgentCapabilities.execute_code) ?? false,
[agentsConfig],
);

View file

@ -91,6 +91,7 @@ const SidePanel = ({
keyProvided,
endpointType,
interfaceConfig,
endpointsConfig,
});
const toggleNavVisible = useCallback(() => {