🔄 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

@ -1,6 +1,8 @@
const { FileSources } = require('librechat-data-provider');
const { const {
Balance, Balance,
getFiles, getFiles,
updateUser,
deleteFiles, deleteFiles,
deleteConvos, deleteConvos,
deletePresets, deletePresets,
@ -12,6 +14,7 @@ const User = require('~/models/User');
const { updateUserPluginAuth, deleteUserPluginAuth } = require('~/server/services/PluginService'); const { updateUserPluginAuth, deleteUserPluginAuth } = require('~/server/services/PluginService');
const { updateUserPluginsService, deleteUserKey } = require('~/server/services/UserService'); const { updateUserPluginsService, deleteUserKey } = require('~/server/services/UserService');
const { verifyEmail, resendVerificationEmail } = require('~/server/services/AuthService'); const { verifyEmail, resendVerificationEmail } = require('~/server/services/AuthService');
const { needsRefresh, getNewS3URL } = require('~/server/services/Files/S3/crud');
const { processDeleteRequest } = require('~/server/services/Files/process'); const { processDeleteRequest } = require('~/server/services/Files/process');
const { deleteAllSharedLinks } = require('~/models/Share'); const { deleteAllSharedLinks } = require('~/models/Share');
const { deleteToolCalls } = require('~/models/ToolCall'); const { deleteToolCalls } = require('~/models/ToolCall');
@ -19,8 +22,23 @@ const { Transaction } = require('~/models/Transaction');
const { logger } = require('~/config'); const { logger } = require('~/config');
const getUserController = async (req, res) => { const getUserController = async (req, res) => {
/** @type {MongoUser} */
const userData = req.user.toObject != null ? req.user.toObject() : { ...req.user }; const userData = req.user.toObject != null ? req.user.toObject() : { ...req.user };
delete userData.totpSecret; delete userData.totpSecret;
if (req.app.locals.fileStrategy === FileSources.s3 && userData.avatar) {
const avatarNeedsRefresh = needsRefresh(userData.avatar, 3600);
if (!avatarNeedsRefresh) {
return res.status(200).send(userData);
}
const originalAvatar = userData.avatar;
try {
userData.avatar = await getNewS3URL(userData.avatar);
await updateUser(userData.id, { avatar: userData.avatar });
} catch (error) {
userData.avatar = originalAvatar;
logger.error('Error getting new S3 URL for avatar:', error);
}
}
res.status(200).send(userData); res.status(200).send(userData);
}; };

View file

@ -148,6 +148,13 @@ const createAbortController = (req, res, getAbortData, getReqData) => {
return { abortController, onStart }; return { abortController, onStart };
}; };
/**
* @param {ServerResponse} res
* @param {ServerRequest} req
* @param {Error | unknown} error
* @param {Partial<TMessage> & { partialText?: string }} data
* @returns { Promise<void> }
*/
const handleAbortError = async (res, req, error, data) => { const handleAbortError = async (res, req, error, data) => {
if (error?.message?.includes('base64')) { if (error?.message?.includes('base64')) {
logger.error('[handleAbortError] Error in base64 encoding', { logger.error('[handleAbortError] Error in base64 encoding', {
@ -178,17 +185,30 @@ const handleAbortError = async (res, req, error, data) => {
errorText = `{"type":"${ErrorTypes.NO_SYSTEM_MESSAGES}"}`; errorText = `{"type":"${ErrorTypes.NO_SYSTEM_MESSAGES}"}`;
} }
/**
* @param {string} partialText
* @returns {Promise<void>}
*/
const respondWithError = async (partialText) => { const respondWithError = async (partialText) => {
const endpointOption = req.body?.endpointOption;
let options = { let options = {
sender, sender,
messageId, messageId,
conversationId, conversationId,
parentMessageId, parentMessageId,
text: errorText, text: errorText,
shouldSaveMessage: true,
user: req.user.id, user: req.user.id,
shouldSaveMessage: true,
spec: endpointOption?.spec,
iconURL: endpointOption?.iconURL,
modelLabel: endpointOption?.modelLabel,
model: endpointOption?.modelOptions?.model || req.body?.model,
}; };
if (req.body?.agent_id) {
options.agent_id = req.body.agent_id;
}
if (partialText) { if (partialText) {
options = { options = {
...options, ...options,

View file

@ -31,19 +31,16 @@ async function getCustomConfig() {
async function getBalanceConfig() { async function getBalanceConfig() {
const isLegacyEnabled = isEnabled(process.env.CHECK_BALANCE); const isLegacyEnabled = isEnabled(process.env.CHECK_BALANCE);
const startBalance = process.env.START_BALANCE; const startBalance = process.env.START_BALANCE;
if (isLegacyEnabled || (startBalance != null && startBalance)) { /** @type {TCustomConfig['balance']} */
/** @type {TCustomConfig['balance']} */ const config = {
const config = { enabled: isLegacyEnabled,
enabled: isLegacyEnabled, startBalance: startBalance != null && startBalance ? parseInt(startBalance, 10) : undefined,
startBalance: startBalance ? parseInt(startBalance, 10) : undefined, };
};
return config;
}
const customConfig = await getCustomConfig(); const customConfig = await getCustomConfig();
if (!customConfig) { if (!customConfig) {
return null; return config;
} }
return customConfig?.['balance'] ?? null; return { ...config, ...(customConfig?.['balance'] ?? {}) };
} }
/** /**

View file

@ -303,6 +303,36 @@ function needsRefresh(signedUrl, bufferSeconds) {
} }
} }
/**
* Generates a new URL for an expired S3 URL
* @param {string} currentURL - The current file URL
* @returns {Promise<string | undefined>}
*/
async function getNewS3URL(currentURL) {
try {
const s3Key = extractKeyFromS3Url(currentURL);
if (!s3Key) {
return;
}
const keyParts = s3Key.split('/');
if (keyParts.length < 3) {
return;
}
const basePath = keyParts[0];
const userId = keyParts[1];
const fileName = keyParts.slice(2).join('/');
return await getS3URL({
userId,
fileName,
basePath,
});
} catch (error) {
logger.error('Error getting new S3 URL:', error);
}
}
/** /**
* Refreshes S3 URLs for an array of files if they're expired or close to expiring * Refreshes S3 URLs for an array of files if they're expired or close to expiring
* *
@ -333,30 +363,15 @@ async function refreshS3FileUrls(files, batchUpdateFiles, bufferSeconds = 3600)
continue; continue;
} }
try { try {
const s3Key = extractKeyFromS3Url(file.filepath); const newURL = await getNewS3URL(file.filepath);
if (!s3Key) { if (!newURL) {
continue; continue;
} }
const keyParts = s3Key.split('/');
if (keyParts.length < 3) {
continue;
}
const basePath = keyParts[0];
const userId = keyParts[1];
const fileName = keyParts.slice(2).join('/');
const newUrl = await getS3URL({
userId,
fileName,
basePath,
});
filesToUpdate.push({ filesToUpdate.push({
file_id: file.file_id, file_id: file.file_id,
filepath: newUrl, filepath: newURL,
}); });
files[i].filepath = newUrl; files[i].filepath = newURL;
} catch (error) { } catch (error) {
logger.error(`Error refreshing S3 URL for file ${file.file_id}:`, error); logger.error(`Error refreshing S3 URL for file ${file.file_id}:`, error);
} }
@ -425,4 +440,6 @@ module.exports = {
getS3FileStream, getS3FileStream,
refreshS3FileUrls, refreshS3FileUrls,
refreshS3Url, refreshS3Url,
needsRefresh,
getNewS3URL,
}; };

View file

@ -69,7 +69,13 @@ async function createMCPTool({ req, toolKey, provider }) {
} }
return result; return result;
} catch (error) { } catch (error) {
return `${toolName} MCP server tool call failed.`; logger.error(
`[MCP][User: ${userId}][${serverName}] Error calling "${toolName}" MCP tool:`,
error,
);
throw new Error(
`"${toolKey}" tool call failed${error?.message ? `: ${error?.message}` : '.'}`,
);
} }
}; };

View file

@ -828,6 +828,12 @@
* @memberof typedefs * @memberof typedefs
*/ */
/**
* @exports TEndpointOption
* @typedef {import('librechat-data-provider').TEndpointOption} TEndpointOption
* @memberof typedefs
*/
/** /**
* @exports TAttachment * @exports TAttachment
* @typedef {import('librechat-data-provider').TAttachment} TAttachment * @typedef {import('librechat-data-provider').TAttachment} TAttachment

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
import { useMemo } from 'react'; 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 type { TMessageProps } from '~/common';
import MessageEndpointIcon from '../Endpoints/MessageEndpointIcon'; import MessageEndpointIcon from '../Endpoints/MessageEndpointIcon';
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL'; import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
@ -14,11 +14,6 @@ export default function MessageIcon(
) { ) {
const { message, conversation, assistant, agent } = props; 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( const messageSettings = useMemo(
() => ({ () => ({
...(conversation ?? {}), ...(conversation ?? {}),
@ -33,7 +28,27 @@ export default function MessageIcon(
const iconURL = messageSettings.iconURL ?? ''; const iconURL = messageSettings.iconURL ?? '';
let endpoint = messageSettings.endpoint; let endpoint = messageSettings.endpoint;
endpoint = getIconEndpoint({ endpointsConfig: undefined, iconURL, 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')) { if (message?.isCreatedByUser !== true && iconURL && iconURL.includes('http')) {
return ( return (
<ConvoIconURL <ConvoIconURL
@ -68,7 +83,7 @@ export default function MessageIcon(
<MessageEndpointIcon <MessageEndpointIcon
{...messageSettings} {...messageSettings}
endpoint={endpoint} endpoint={endpoint}
iconURL={assistant == null ? undefined : assistantAvatar} iconURL={avatarURL}
model={message?.model ?? conversation?.model} model={message?.model ?? conversation?.model}
assistantName={assistantName} assistantName={assistantName}
agentName={agentName} agentName={agentName}

View file

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

View file

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

View file

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

View file

@ -8,7 +8,7 @@ import {
EModelEndpoint, EModelEndpoint,
Permissions, Permissions,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import type { TConfig, TInterfaceConfig } from 'librechat-data-provider'; import type { TConfig, TInterfaceConfig, TEndpointsConfig } from 'librechat-data-provider';
import type { NavLink } from '~/common'; import type { NavLink } from '~/common';
import AgentPanelSwitch from '~/components/SidePanel/Agents/AgentPanelSwitch'; import AgentPanelSwitch from '~/components/SidePanel/Agents/AgentPanelSwitch';
import BookmarkPanel from '~/components/SidePanel/Bookmarks/BookmarkPanel'; import BookmarkPanel from '~/components/SidePanel/Bookmarks/BookmarkPanel';
@ -27,6 +27,7 @@ export default function useSideNavLinks({
endpoint, endpoint,
endpointType, endpointType,
interfaceConfig, interfaceConfig,
endpointsConfig,
}: { }: {
hidePanel: () => void; hidePanel: () => void;
assistants?: TConfig | null; assistants?: TConfig | null;
@ -35,6 +36,7 @@ export default function useSideNavLinks({
endpoint?: EModelEndpoint | null; endpoint?: EModelEndpoint | null;
endpointType?: EModelEndpoint | null; endpointType?: EModelEndpoint | null;
interfaceConfig: Partial<TInterfaceConfig>; interfaceConfig: Partial<TInterfaceConfig>;
endpointsConfig: TEndpointsConfig;
}) { }) {
const hasAccessToPrompts = useHasAccess({ const hasAccessToPrompts = useHasAccess({
permissionType: PermissionTypes.PROMPTS, permissionType: PermissionTypes.PROMPTS,
@ -70,7 +72,13 @@ export default function useSideNavLinks({
}); });
} }
if (hasAccessToAgents && hasAccessToCreateAgents && agents && agents.disableBuilder !== true) { if (
endpointsConfig?.[EModelEndpoint.agents] &&
hasAccessToAgents &&
hasAccessToCreateAgents &&
agents &&
agents.disableBuilder !== true
) {
links.push({ links.push({
title: 'com_sidepanel_agent_builder', title: 'com_sidepanel_agent_builder',
label: '', label: '',
@ -133,6 +141,7 @@ export default function useSideNavLinks({
return links; return links;
}, [ }, [
endpointsConfig?.[EModelEndpoint.agents],
interfaceConfig.parameters, interfaceConfig.parameters,
keyProvided, keyProvided,
assistants, assistants,

View file

@ -598,7 +598,7 @@ export default function useEventHandlers({
}); });
setMessages([...messages, userMessage, errorResponse]); setMessages([...messages, userMessage, errorResponse]);
if (receivedConvoId && paramId === 'new' && newConversation) { if (receivedConvoId && paramId === Constants.NEW_CONVO && newConversation) {
newConversation({ newConversation({
template: { conversationId: receivedConvoId }, template: { conversationId: receivedConvoId },
preset: tPresetSchema.parse(submission.conversation), preset: tPresetSchema.parse(submission.conversation),

View file

@ -98,7 +98,7 @@ const Balance = require('~/models/Balance');
} }
// Check the result // Check the result
if (!result?.tokenCredits) { if (result?.tokenCredits == null) {
console.red('Error: Something went wrong while updating the balance!'); console.red('Error: Something went wrong while updating the balance!');
console.error(result); console.error(result);
silentExit(1); silentExit(1);

View file

@ -18,6 +18,8 @@ export type TMessages = TMessage[];
/* TODO: Cleanup EndpointOption types */ /* TODO: Cleanup EndpointOption types */
export type TEndpointOption = { export type TEndpointOption = {
spec?: string | null;
iconURL?: string | null;
endpoint: EModelEndpoint; endpoint: EModelEndpoint;
endpointType?: EModelEndpoint; endpointType?: EModelEndpoint;
modelDisplayLabel?: string; modelDisplayLabel?: string;