textChat.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /**
  2. * 文本|语音聊天框
  3. */
  4. import { create } from "zustand";
  5. import { generateUUID } from '@/utils/index'
  6. import { getMessageHistories, type TGetMessageHistoriesParams, } from '@/service/bot'
  7. import { EChatRole, TAnyMessage, TRobotMessage, TMessage } from "@/types/bot";
  8. type TRobotMessageWithOptionalId = Omit<TRobotMessage, "msgUk"> & {
  9. msgUk?: string; // 将 messageId 设置为可选
  10. reasoningContent?: string; // 添加 reasoningContent
  11. };
  12. const INIT_CURRENT_ROBOT_MSG_UK = ''
  13. export interface TextChat {
  14. currentRobotMsgUk: string; // 当前正在说话的 AI 机器人 id, 可用于控制是否继续输出文本至当前 message 框内
  15. scrollTop: number; // 控制聊天内容变化后滚动至最底部
  16. autoScroll: boolean
  17. list: TAnyMessage[];
  18. questions: string[]; //推荐问题
  19. sessionId: string|null
  20. // 显示聊天历史
  21. setAutoScroll: (b: boolean) => void // 是否自动滚动
  22. genSessionId: () => void // 进入聊天界面后,本次的 sessionId
  23. setQuestions: (q:string[]) => void // 设置推荐问题
  24. setScrollTop: () => void
  25. // 将机器人气泡框推入聊天框
  26. pushRobotMessage: (message: TRobotMessageWithOptionalId) => string;
  27. // 将自己发出的气泡框推入聊天框
  28. pushMessage: (content: string) => {msgUk: string, sessionId: string};
  29. // 更新自己发出的气泡框
  30. updateMessage: (content: string, msgUk: string) => string;
  31. // 更新机器人汽泡框内的内容实现 gpt 的效果
  32. updateRobotMessage: (content: string, body?: Record<string,any>, saveStatus?: number) => void;
  33. updateRobotReasoningMessage: (msgUk: string, reasoningContent: string, body?:Record<string,any>) => void;
  34. getCurrentRobotMessage:() => TRobotMessage|undefined
  35. deleteMessage: (msgUk: string) => void;
  36. // 清空
  37. destroy: () => void;
  38. fetchMessageHistories: (data: TGetMessageHistoriesParams) => void
  39. }
  40. // 新messageId 为 index 加 1
  41. const generateUk = () => {
  42. return generateUUID();
  43. }
  44. export const useTextChat = create<TextChat>((set, get) => ({
  45. currentRobotMsgUk: INIT_CURRENT_ROBOT_MSG_UK,
  46. scrollTop: 999999,
  47. autoScroll: true,
  48. list: [],
  49. sessionId: null,
  50. questions: [],
  51. setAutoScroll: (b)=> {
  52. set({autoScroll: b})
  53. },
  54. setQuestions: (q:string[])=> {
  55. set({questions: q})
  56. },
  57. genSessionId: ()=> {
  58. const sessionId = generateUUID();
  59. set({
  60. sessionId
  61. })
  62. },
  63. // 重置
  64. destroy: () => {
  65. set({
  66. list: [],
  67. questions: [],
  68. currentRobotMsgUk: INIT_CURRENT_ROBOT_MSG_UK,
  69. scrollTop: 9999,
  70. sessionId: null,
  71. });
  72. },
  73. pushRobotMessage: (message) => {
  74. const msgUk = generateUk()
  75. set((state) => {
  76. const newRobotMessage = { ...message, msgUk, role: EChatRole.Assistant } as TRobotMessage
  77. return {
  78. list: [...state.list, newRobotMessage],
  79. currentRobotMsgUk: msgUk,
  80. scrollTop: state.scrollTop + 1,
  81. };
  82. });
  83. setTimeout(()=> {
  84. set((state) => {
  85. return {
  86. scrollTop: state.scrollTop + 1,
  87. };
  88. });
  89. }, 100)
  90. return msgUk
  91. },
  92. setScrollTop: ()=> {
  93. set((state) => {
  94. return {
  95. scrollTop: state.scrollTop + 1,
  96. };
  97. });
  98. },
  99. pushMessage: (content: string) => {
  100. const msgUk = generateUk();
  101. const newMessage:TMessage ={ msgUk, content: content, role: EChatRole.User, saveStatus: 0 }
  102. set((state) => {
  103. return {
  104. list: [...state.list, newMessage],
  105. scrollTop: state.scrollTop + 1,
  106. };
  107. });
  108. setTimeout(()=> {
  109. set((state) => {
  110. return {
  111. scrollTop: state.scrollTop + 1,
  112. };
  113. });
  114. }, 100)
  115. return {msgUk, sessionId: get().sessionId ?? ''}
  116. },
  117. updateMessage: (content, msgUk) => {
  118. set((state) => {
  119. const updatedList = state.list.map((message) => {
  120. if (message.msgUk === msgUk) {
  121. return { ...message, content: message.content + content, saveStatus: 0 }; // 更新 content
  122. }
  123. return message; // 返回未修改的 message
  124. });
  125. return { list: updatedList, scrollTop: state.scrollTop + 1 }; // 返回新的状态
  126. });
  127. return msgUk
  128. },
  129. updateRobotMessage: (content, body={}, saveStatus: number = 0) => {
  130. set((state) => {
  131. const updatedList = state.list.map((message) => {
  132. if (message.msgUk === state.currentRobotMsgUk) {
  133. // 更新消息后, saveStatus 变为 0,说明又需要上报此消息
  134. return { ...message, content: message.content + content, body, saveStatus: saveStatus } as TRobotMessage // 更新 content
  135. }
  136. return message; // 返回未修改的 message
  137. });
  138. return { list: updatedList, scrollTop: state.autoScroll ? state.scrollTop + 1 : state.scrollTop}; // 返回新的状态
  139. });
  140. },
  141. getCurrentRobotMessage: ()=> {
  142. const state = get()
  143. const currentMessage = state.list.find((message)=> message.msgUk === state.currentRobotMsgUk) as TRobotMessage
  144. return currentMessage
  145. },
  146. updateRobotReasoningMessage: (msgUk, reasoningContent, body={}) => {
  147. set((state) => {
  148. // 由于 currentRobotMsgUk 可能发生变化, 所以需要传递 msgUk
  149. // console.log(state.currentRobotMsgUk, msgUk)
  150. const updatedList = state.list.map((message) => {
  151. if (message.msgUk === msgUk) {
  152. //@ts-ignore
  153. return { ...message, body, reasoningContent: message.reasoningContent + reasoningContent } as TRobotMessage // 更新 reasoningContent
  154. }
  155. return message; // 返回未修改的 message
  156. });
  157. return { list: updatedList, scrollTop: state.autoScroll ? state.scrollTop + 1 : state.scrollTop}; // 返回新的状态
  158. });
  159. },
  160. deleteMessage: (msgUk)=> {
  161. set((state) => {
  162. // 如果对话框是空的,则删除
  163. const filtered = state.list.filter((message) => {
  164. const isEmptyContent = message.content.length <= 0
  165. return (message.msgUk !== msgUk && isEmptyContent)
  166. });
  167. return { list: filtered, scrollTop: state.scrollTop + 1 }; // 返回新的状态
  168. });
  169. },
  170. // 停止当前机器人说话输出框输出
  171. stopCurrentRobotMessaging: ()=> {
  172. set({currentRobotMsgUk: INIT_CURRENT_ROBOT_MSG_UK})
  173. },
  174. fetchMessageHistories: async (data: TGetMessageHistoriesParams) => {
  175. const response = await getMessageHistories(data)
  176. console.log(response)
  177. if(response.status){
  178. }
  179. }
  180. }));