| import { useRecoilValue } from 'recoil'; | |
| import { useCallback, useMemo, useState } from 'react'; | |
| import { useUpdateFeedbackMutation } from 'librechat-data-provider/react-query'; | |
| import { | |
| isAssistantsEndpoint, | |
| isAgentsEndpoint, | |
| TUpdateFeedbackRequest, | |
| getTagByKey, | |
| TFeedback, | |
| toMinimalFeedback, | |
| SearchResultData, | |
| } from 'librechat-data-provider'; | |
| import type { TMessageProps } from '~/common'; | |
| import { | |
| useChatContext, | |
| useAddedChatContext, | |
| useAssistantsMapContext, | |
| useAgentsMapContext, | |
| } from '~/Providers'; | |
| import useCopyToClipboard from './useCopyToClipboard'; | |
| import { useAuthContext } from '~/hooks/AuthContext'; | |
| import { useLocalize } from '~/hooks'; | |
| import store from '~/store'; | |
| export type TMessageActions = Pick< | |
| TMessageProps, | |
| 'message' | 'currentEditId' | 'setCurrentEditId' | |
| > & { | |
| isMultiMessage?: boolean; | |
| searchResults?: { [key: string]: SearchResultData }; | |
| }; | |
| export default function useMessageActions(props: TMessageActions) { | |
| const localize = useLocalize(); | |
| const { user } = useAuthContext(); | |
| const UsernameDisplay = useRecoilValue<boolean>(store.UsernameDisplay); | |
| const { message, currentEditId, setCurrentEditId, isMultiMessage, searchResults } = props; | |
| const { | |
| ask, | |
| index, | |
| regenerate, | |
| latestMessage, | |
| handleContinue, | |
| setLatestMessage, | |
| conversation: rootConvo, | |
| isSubmitting: isSubmittingRoot, | |
| } = useChatContext(); | |
| const { conversation: addedConvo, isSubmitting: isSubmittingAdditional } = useAddedChatContext(); | |
| const conversation = useMemo( | |
| () => (isMultiMessage === true ? addedConvo : rootConvo), | |
| [isMultiMessage, addedConvo, rootConvo], | |
| ); | |
| const agentsMap = useAgentsMapContext(); | |
| const assistantMap = useAssistantsMapContext(); | |
| const { text, content, messageId = null, isCreatedByUser } = message ?? {}; | |
| const edit = useMemo(() => messageId === currentEditId, [messageId, currentEditId]); | |
| const [feedback, setFeedback] = useState<TFeedback | undefined>(() => { | |
| if (message?.feedback) { | |
| const tag = getTagByKey(message.feedback?.tag?.key); | |
| return { | |
| rating: message.feedback.rating, | |
| tag, | |
| text: message.feedback.text, | |
| }; | |
| } | |
| return undefined; | |
| }); | |
| const enterEdit = useCallback( | |
| (cancel?: boolean) => setCurrentEditId && setCurrentEditId(cancel === true ? -1 : messageId), | |
| [messageId, setCurrentEditId], | |
| ); | |
| 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; | |
| } | |
| if (!agentsMap) { | |
| return undefined; | |
| } | |
| const modelKey = message?.model ?? ''; | |
| if (modelKey) { | |
| return agentsMap[modelKey]; | |
| } | |
| const agentId = conversation?.agent_id ?? ''; | |
| if (agentId) { | |
| return agentsMap[agentId]; | |
| } | |
| }, [agentsMap, conversation?.agent_id, conversation?.endpoint, message?.model]); | |
| const isSubmitting = useMemo( | |
| () => (isMultiMessage === true ? isSubmittingAdditional : isSubmittingRoot), | |
| [isMultiMessage, isSubmittingAdditional, isSubmittingRoot], | |
| ); | |
| const regenerateMessage = useCallback(() => { | |
| if ((isSubmitting && isCreatedByUser === true) || !message) { | |
| return; | |
| } | |
| regenerate(message); | |
| }, [isSubmitting, isCreatedByUser, message, regenerate]); | |
| const copyToClipboard = useCopyToClipboard({ text, content, searchResults }); | |
| const messageLabel = useMemo(() => { | |
| if (message?.isCreatedByUser === true) { | |
| return UsernameDisplay ? (user?.name ?? '') || user?.username : localize('com_user_message'); | |
| } else if (agent) { | |
| return agent.name ?? 'Assistant'; | |
| } else if (assistant) { | |
| return assistant.name ?? 'Assistant'; | |
| } else { | |
| return message?.sender; | |
| } | |
| }, [message, agent, assistant, UsernameDisplay, user, localize]); | |
| const feedbackMutation = useUpdateFeedbackMutation( | |
| conversation?.conversationId || '', | |
| message?.messageId || '', | |
| ); | |
| const handleFeedback = useCallback( | |
| ({ feedback: newFeedback }: { feedback: TFeedback | undefined }) => { | |
| const payload: TUpdateFeedbackRequest = { | |
| feedback: newFeedback ? toMinimalFeedback(newFeedback) : undefined, | |
| }; | |
| feedbackMutation.mutate(payload, { | |
| onSuccess: (data) => { | |
| if (!data.feedback) { | |
| setFeedback(undefined); | |
| } else { | |
| const tag = getTagByKey(data.feedback?.tag ?? undefined); | |
| setFeedback({ | |
| rating: data.feedback.rating, | |
| tag, | |
| text: data.feedback.text, | |
| }); | |
| } | |
| }, | |
| onError: (error) => { | |
| console.error('Failed to update feedback:', error); | |
| }, | |
| }); | |
| }, | |
| [feedbackMutation], | |
| ); | |
| return { | |
| ask, | |
| edit, | |
| index, | |
| agent, | |
| assistant, | |
| enterEdit, | |
| conversation, | |
| messageLabel, | |
| isSubmitting, | |
| latestMessage, | |
| handleContinue, | |
| copyToClipboard, | |
| setLatestMessage, | |
| regenerateMessage, | |
| handleFeedback, | |
| feedback, | |
| }; | |
| } | |