### What problem does this PR solve? feat: Select derived messages from backend #2088 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
281 lines
7.0 KiB
TypeScript
281 lines
7.0 KiB
TypeScript
import { MessageType } from '@/constants/chat';
|
|
import { useFetchFlow } from '@/hooks/flow-hooks';
|
|
import {
|
|
useHandleMessageInputChange,
|
|
useScrollToBottom,
|
|
useSelectDerivedMessages,
|
|
useSendMessageWithSse,
|
|
} from '@/hooks/logic-hooks';
|
|
import { IAnswer, Message } from '@/interfaces/database/chat';
|
|
import { IMessage } from '@/pages/chat/interface';
|
|
import api from '@/utils/api';
|
|
import { buildMessageUuid } from '@/utils/chat';
|
|
import { message } from 'antd';
|
|
import trim from 'lodash/trim';
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
import { useParams } from 'umi';
|
|
import { v4 as uuid } from 'uuid';
|
|
import { receiveMessageError } from '../utils';
|
|
|
|
const antMessage = message;
|
|
|
|
export const useSelectCurrentMessages = () => {
|
|
const { id: id } = useParams();
|
|
const [currentMessages, setCurrentMessages] = useState<IMessage[]>([]);
|
|
|
|
const { data: flowDetail, loading } = useFetchFlow();
|
|
const messages = flowDetail.dsl.messages;
|
|
const reference = flowDetail.dsl.reference;
|
|
|
|
const ref = useScrollToBottom(currentMessages);
|
|
|
|
const addNewestQuestion = useCallback(
|
|
(message: Message, answer: string = '') => {
|
|
setCurrentMessages((pre) => {
|
|
return [
|
|
...pre,
|
|
{
|
|
...message,
|
|
id: buildMessageUuid(message),
|
|
},
|
|
{
|
|
role: MessageType.Assistant,
|
|
content: answer,
|
|
id: buildMessageUuid({ ...message, role: MessageType.Assistant }),
|
|
},
|
|
];
|
|
});
|
|
},
|
|
[],
|
|
);
|
|
|
|
const addNewestAnswer = useCallback((answer: IAnswer) => {
|
|
setCurrentMessages((pre) => {
|
|
return [
|
|
...pre.slice(0, -1),
|
|
{
|
|
role: MessageType.Assistant,
|
|
content: answer.answer,
|
|
reference: answer.reference,
|
|
id: buildMessageUuid({
|
|
id: answer.id,
|
|
role: MessageType.Assistant,
|
|
}),
|
|
},
|
|
];
|
|
});
|
|
}, []);
|
|
|
|
const removeLatestMessage = useCallback(() => {
|
|
setCurrentMessages((pre) => {
|
|
const nextMessages = pre?.slice(0, -2) ?? [];
|
|
return nextMessages;
|
|
return [...pre, ...nextMessages];
|
|
});
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (id) {
|
|
const nextMessages = messages.map((x) => ({ ...x, id: uuid() }));
|
|
setCurrentMessages(nextMessages);
|
|
}
|
|
}, [messages, id]);
|
|
|
|
return {
|
|
currentMessages,
|
|
reference,
|
|
addNewestQuestion,
|
|
removeLatestMessage,
|
|
addNewestAnswer,
|
|
ref,
|
|
loading,
|
|
};
|
|
};
|
|
|
|
export const useSelectNextMessages = () => {
|
|
const { id: id } = useParams();
|
|
const { data: flowDetail, loading } = useFetchFlow();
|
|
const messages = flowDetail.dsl.messages;
|
|
const reference = flowDetail.dsl.reference;
|
|
const {
|
|
derivedMessages,
|
|
setDerivedMessages,
|
|
ref,
|
|
addNewestQuestion,
|
|
addNewestAnswer,
|
|
removeLatestMessage,
|
|
removeMessageById,
|
|
removeMessagesAfterCurrentMessage,
|
|
} = useSelectDerivedMessages();
|
|
|
|
useEffect(() => {
|
|
if (id) {
|
|
const nextMessages = messages.map((x) => ({ ...x, id: uuid() }));
|
|
setDerivedMessages(nextMessages);
|
|
}
|
|
}, [messages, id, setDerivedMessages]);
|
|
|
|
return {
|
|
reference,
|
|
loading,
|
|
derivedMessages,
|
|
ref,
|
|
addNewestQuestion,
|
|
addNewestAnswer,
|
|
removeLatestMessage,
|
|
removeMessageById,
|
|
removeMessagesAfterCurrentMessage,
|
|
};
|
|
};
|
|
|
|
export const useSendMessage = (
|
|
addNewestQuestion: (message: Message, answer?: string) => void,
|
|
removeLatestMessage: () => void,
|
|
addNewestAnswer: (answer: IAnswer) => void,
|
|
) => {
|
|
const { id: flowId } = useParams();
|
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
|
const { refetch } = useFetchFlow();
|
|
|
|
const { send, answer, done } = useSendMessageWithSse(api.runCanvas);
|
|
|
|
const sendMessage = useCallback(
|
|
async (message: Message) => {
|
|
const params: Record<string, unknown> = {
|
|
id: flowId,
|
|
};
|
|
if (message.content) {
|
|
params.message = message.content;
|
|
params.message_id = message.id;
|
|
}
|
|
const res = await send(params);
|
|
|
|
if (receiveMessageError(res)) {
|
|
antMessage.error(res?.data?.retmsg);
|
|
|
|
// cancel loading
|
|
setValue(message.content);
|
|
removeLatestMessage();
|
|
} else {
|
|
refetch(); // pull the message list after sending the message successfully
|
|
}
|
|
},
|
|
[flowId, removeLatestMessage, setValue, send, refetch],
|
|
);
|
|
|
|
const handleSendMessage = useCallback(
|
|
async (message: Message) => {
|
|
sendMessage(message);
|
|
},
|
|
[sendMessage],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (answer.answer) {
|
|
addNewestAnswer(answer);
|
|
}
|
|
}, [answer, addNewestAnswer]);
|
|
|
|
const handlePressEnter = useCallback(() => {
|
|
if (trim(value) === '') return;
|
|
const id = uuid();
|
|
if (done) {
|
|
setValue('');
|
|
handleSendMessage({ id, content: value.trim(), role: MessageType.User });
|
|
}
|
|
addNewestQuestion({
|
|
content: value,
|
|
id,
|
|
role: MessageType.User,
|
|
});
|
|
}, [addNewestQuestion, handleSendMessage, done, setValue, value]);
|
|
|
|
return {
|
|
handlePressEnter,
|
|
handleInputChange,
|
|
value,
|
|
loading: !done,
|
|
};
|
|
};
|
|
|
|
export const useSendNextMessage = () => {
|
|
const {
|
|
reference,
|
|
loading,
|
|
derivedMessages,
|
|
ref,
|
|
addNewestQuestion,
|
|
addNewestAnswer,
|
|
removeLatestMessage,
|
|
removeMessageById,
|
|
} = useSelectNextMessages();
|
|
const { id: flowId } = useParams();
|
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
|
const { refetch } = useFetchFlow();
|
|
|
|
const { send, answer, done } = useSendMessageWithSse(api.runCanvas);
|
|
|
|
const sendMessage = useCallback(
|
|
async ({ message }: { message: Message; messages?: Message[] }) => {
|
|
const params: Record<string, unknown> = {
|
|
id: flowId,
|
|
};
|
|
if (message.content) {
|
|
params.message = message.content;
|
|
params.message_id = message.id;
|
|
}
|
|
const res = await send(params);
|
|
|
|
if (receiveMessageError(res)) {
|
|
antMessage.error(res?.data?.retmsg);
|
|
|
|
// cancel loading
|
|
setValue(message.content);
|
|
removeLatestMessage();
|
|
} else {
|
|
refetch(); // pull the message list after sending the message successfully
|
|
}
|
|
},
|
|
[flowId, removeLatestMessage, setValue, send, refetch],
|
|
);
|
|
|
|
const handleSendMessage = useCallback(
|
|
async (message: Message) => {
|
|
sendMessage({ message });
|
|
},
|
|
[sendMessage],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (answer.answer) {
|
|
addNewestAnswer(answer);
|
|
}
|
|
}, [answer, addNewestAnswer]);
|
|
|
|
const handlePressEnter = useCallback(() => {
|
|
if (trim(value) === '') return;
|
|
const id = uuid();
|
|
if (done) {
|
|
setValue('');
|
|
handleSendMessage({ id, content: value.trim(), role: MessageType.User });
|
|
}
|
|
addNewestQuestion({
|
|
content: value,
|
|
id,
|
|
role: MessageType.User,
|
|
});
|
|
}, [addNewestQuestion, handleSendMessage, done, setValue, value]);
|
|
|
|
return {
|
|
handlePressEnter,
|
|
handleInputChange,
|
|
value,
|
|
sendLoading: !done,
|
|
reference,
|
|
loading,
|
|
derivedMessages,
|
|
ref,
|
|
removeMessageById,
|
|
};
|
|
};
|