import throttle from 'lodash/throttle'; import { useEffect, useRef, useCallback, useMemo } from 'react'; import { Constants, isAssistantsEndpoint, isAgentsEndpoint } from 'librechat-data-provider'; import type { TMessageProps } from '~/common'; import { useChatContext, useAssistantsMapContext, useAgentsMapContext } from '~/Providers'; import useCopyToClipboard from './useCopyToClipboard'; import { getTextKey, logger } from '~/utils'; export default function useMessageHelpers(props: TMessageProps) { const latestText = useRef(''); const { message, currentEditId, setCurrentEditId } = props; const { ask, index, regenerate, isSubmitting, conversation, latestMessage, setAbortScroll, handleContinue, setLatestMessage, } = useChatContext(); const assistantMap = useAssistantsMapContext(); const agentsMap = useAgentsMapContext(); const { text, content, children, messageId = null, isCreatedByUser } = message ?? {}; const edit = messageId === currentEditId; const isLast = children?.length === 0 || children?.length === undefined; useEffect(() => { const convoId = conversation?.conversationId; if (convoId === Constants.NEW_CONVO) { return; } if (!message) { return; } if (!isLast) { return; } const textKey = getTextKey(message, convoId); // Check for text/conversation change const logInfo = { textKey, 'latestText.current': latestText.current, messageId: message.messageId, convoId, }; if ( textKey !== latestText.current || (latestText.current && convoId !== latestText.current.split(Constants.COMMON_DIVIDER)[2]) ) { logger.log('[useMessageHelpers] Setting latest message: ', logInfo); latestText.current = textKey; setLatestMessage({ ...message }); } else { logger.log('No change in latest message', logInfo); } }, [isLast, message, setLatestMessage, conversation?.conversationId]); const enterEdit = useCallback( (cancel?: boolean) => setCurrentEditId && setCurrentEditId(cancel === true ? -1 : messageId), [messageId, setCurrentEditId], ); const handleScroll = useCallback( (event: unknown) => { throttle(() => { logger.log( 'message_scrolling', `useMessageHelpers: setting abort scroll to ${isSubmitting}, handleScroll event`, event, ); if (isSubmitting) { setAbortScroll(true); } else { setAbortScroll(false); } }, 500)(); }, [isSubmitting, setAbortScroll], ); const assistant = useMemo(() => { if (!isAssistantsEndpoint(conversation?.endpoint)) { return undefined; } const endpointKey = conversation?.endpoint ?? ''; const modelKey = message?.model ?? ''; return assistantMap?.[endpointKey] ? assistantMap[endpointKey][modelKey] : undefined; }, [conversation?.endpoint, message?.model, assistantMap]); const agent = useMemo(() => { if (!isAgentsEndpoint(conversation?.endpoint)) { return undefined; } const modelKey = message?.model ?? ''; return agentsMap ? agentsMap[modelKey] : undefined; }, [agentsMap, conversation?.endpoint, message?.model]); const regenerateMessage = () => { if ((isSubmitting && isCreatedByUser === true) || !message) { return; } regenerate(message); }; const copyToClipboard = useCopyToClipboard({ text, content }); return { ask, edit, agent, index, isLast, assistant, enterEdit, conversation, isSubmitting, handleScroll, latestMessage, handleContinue, copyToClipboard, regenerateMessage, }; }