You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import { MessageType } from '@/constants/chat';
  2. import { useFetchFlow } from '@/hooks/flow-hooks';
  3. import {
  4. useHandleMessageInputChange,
  5. useScrollToBottom,
  6. useSelectDerivedMessages,
  7. useSendMessageWithSse,
  8. } from '@/hooks/logic-hooks';
  9. import { IAnswer, Message } from '@/interfaces/database/chat';
  10. import { IMessage } from '@/pages/chat/interface';
  11. import api from '@/utils/api';
  12. import { buildMessageUuid } from '@/utils/chat';
  13. import { message } from 'antd';
  14. import trim from 'lodash/trim';
  15. import { useCallback, useEffect, useState } from 'react';
  16. import { useParams } from 'umi';
  17. import { v4 as uuid } from 'uuid';
  18. import { receiveMessageError } from '../utils';
  19. const antMessage = message;
  20. export const useSelectCurrentMessages = () => {
  21. const { id: id } = useParams();
  22. const [currentMessages, setCurrentMessages] = useState<IMessage[]>([]);
  23. const { data: flowDetail, loading } = useFetchFlow();
  24. const messages = flowDetail.dsl.messages;
  25. const reference = flowDetail.dsl.reference;
  26. const ref = useScrollToBottom(currentMessages);
  27. const addNewestQuestion = useCallback(
  28. (message: Message, answer: string = '') => {
  29. setCurrentMessages((pre) => {
  30. return [
  31. ...pre,
  32. {
  33. ...message,
  34. id: buildMessageUuid(message),
  35. },
  36. {
  37. role: MessageType.Assistant,
  38. content: answer,
  39. id: buildMessageUuid({ ...message, role: MessageType.Assistant }),
  40. },
  41. ];
  42. });
  43. },
  44. [],
  45. );
  46. const addNewestAnswer = useCallback((answer: IAnswer) => {
  47. setCurrentMessages((pre) => {
  48. return [
  49. ...pre.slice(0, -1),
  50. {
  51. role: MessageType.Assistant,
  52. content: answer.answer,
  53. reference: answer.reference,
  54. id: buildMessageUuid({
  55. id: answer.id,
  56. role: MessageType.Assistant,
  57. }),
  58. },
  59. ];
  60. });
  61. }, []);
  62. const removeLatestMessage = useCallback(() => {
  63. setCurrentMessages((pre) => {
  64. const nextMessages = pre?.slice(0, -2) ?? [];
  65. return nextMessages;
  66. return [...pre, ...nextMessages];
  67. });
  68. }, []);
  69. useEffect(() => {
  70. if (id) {
  71. const nextMessages = messages.map((x) => ({ ...x, id: uuid() }));
  72. setCurrentMessages(nextMessages);
  73. }
  74. }, [messages, id]);
  75. return {
  76. currentMessages,
  77. reference,
  78. addNewestQuestion,
  79. removeLatestMessage,
  80. addNewestAnswer,
  81. ref,
  82. loading,
  83. };
  84. };
  85. export const useSelectNextMessages = () => {
  86. const { id: id } = useParams();
  87. const { data: flowDetail, loading } = useFetchFlow();
  88. const messages = flowDetail.dsl.messages;
  89. const reference = flowDetail.dsl.reference;
  90. const {
  91. derivedMessages,
  92. setDerivedMessages,
  93. ref,
  94. addNewestQuestion,
  95. addNewestAnswer,
  96. removeLatestMessage,
  97. removeMessageById,
  98. removeMessagesAfterCurrentMessage,
  99. } = useSelectDerivedMessages();
  100. useEffect(() => {
  101. if (id) {
  102. const nextMessages = messages.map((x) => ({ ...x, id: uuid() }));
  103. setDerivedMessages(nextMessages);
  104. }
  105. }, [messages, id, setDerivedMessages]);
  106. return {
  107. reference,
  108. loading,
  109. derivedMessages,
  110. ref,
  111. addNewestQuestion,
  112. addNewestAnswer,
  113. removeLatestMessage,
  114. removeMessageById,
  115. removeMessagesAfterCurrentMessage,
  116. };
  117. };
  118. export const useSendMessage = (
  119. addNewestQuestion: (message: Message, answer?: string) => void,
  120. removeLatestMessage: () => void,
  121. addNewestAnswer: (answer: IAnswer) => void,
  122. ) => {
  123. const { id: flowId } = useParams();
  124. const { handleInputChange, value, setValue } = useHandleMessageInputChange();
  125. const { refetch } = useFetchFlow();
  126. const { send, answer, done } = useSendMessageWithSse(api.runCanvas);
  127. const sendMessage = useCallback(
  128. async (message: Message) => {
  129. const params: Record<string, unknown> = {
  130. id: flowId,
  131. };
  132. if (message.content) {
  133. params.message = message.content;
  134. params.message_id = message.id;
  135. }
  136. const res = await send(params);
  137. if (receiveMessageError(res)) {
  138. antMessage.error(res?.data?.message);
  139. // cancel loading
  140. setValue(message.content);
  141. removeLatestMessage();
  142. } else {
  143. refetch(); // pull the message list after sending the message successfully
  144. }
  145. },
  146. [flowId, removeLatestMessage, setValue, send, refetch],
  147. );
  148. const handleSendMessage = useCallback(
  149. async (message: Message) => {
  150. sendMessage(message);
  151. },
  152. [sendMessage],
  153. );
  154. useEffect(() => {
  155. if (answer.answer) {
  156. addNewestAnswer(answer);
  157. }
  158. }, [answer, addNewestAnswer]);
  159. const handlePressEnter = useCallback(() => {
  160. if (trim(value) === '') return;
  161. const id = uuid();
  162. if (done) {
  163. setValue('');
  164. handleSendMessage({ id, content: value.trim(), role: MessageType.User });
  165. }
  166. addNewestQuestion({
  167. content: value,
  168. id,
  169. role: MessageType.User,
  170. });
  171. }, [addNewestQuestion, handleSendMessage, done, setValue, value]);
  172. return {
  173. handlePressEnter,
  174. handleInputChange,
  175. value,
  176. loading: !done,
  177. };
  178. };
  179. export const useSendNextMessage = () => {
  180. const {
  181. reference,
  182. loading,
  183. derivedMessages,
  184. ref,
  185. addNewestQuestion,
  186. addNewestAnswer,
  187. removeLatestMessage,
  188. removeMessageById,
  189. } = useSelectNextMessages();
  190. const { id: flowId } = useParams();
  191. const { handleInputChange, value, setValue } = useHandleMessageInputChange();
  192. const { refetch } = useFetchFlow();
  193. const { send, answer, done } = useSendMessageWithSse(api.runCanvas);
  194. const sendMessage = useCallback(
  195. async ({ message }: { message: Message; messages?: Message[] }) => {
  196. const params: Record<string, unknown> = {
  197. id: flowId,
  198. };
  199. if (message.content) {
  200. params.message = message.content;
  201. params.message_id = message.id;
  202. }
  203. const res = await send(params);
  204. if (receiveMessageError(res)) {
  205. antMessage.error(res?.data?.message);
  206. // cancel loading
  207. setValue(message.content);
  208. removeLatestMessage();
  209. } else {
  210. refetch(); // pull the message list after sending the message successfully
  211. }
  212. },
  213. [flowId, removeLatestMessage, setValue, send, refetch],
  214. );
  215. const handleSendMessage = useCallback(
  216. async (message: Message) => {
  217. sendMessage({ message });
  218. },
  219. [sendMessage],
  220. );
  221. useEffect(() => {
  222. if (answer.answer) {
  223. addNewestAnswer(answer);
  224. }
  225. }, [answer, addNewestAnswer]);
  226. const handlePressEnter = useCallback(() => {
  227. if (trim(value) === '') return;
  228. const id = uuid();
  229. if (done) {
  230. setValue('');
  231. handleSendMessage({ id, content: value.trim(), role: MessageType.User });
  232. }
  233. addNewestQuestion({
  234. content: value,
  235. id,
  236. role: MessageType.User,
  237. });
  238. }, [addNewestQuestion, handleSendMessage, done, setValue, value]);
  239. return {
  240. handlePressEnter,
  241. handleInputChange,
  242. value,
  243. sendLoading: !done,
  244. reference,
  245. loading,
  246. derivedMessages,
  247. ref,
  248. removeMessageById,
  249. };
  250. };