chatInput.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import { useEffect, useState } from "react";
  2. import TextInputBar from "./TextInputBar";
  3. import VoiceInputBar from "./VoiceInputBar";
  4. import { textChat } from "@/service/bot";
  5. import { useTextChat } from "@/store/textChat";
  6. import { TAgentDetail } from "@/types/agent";
  7. import { delay, getLoginId, isSuccess } from "@/utils";
  8. import { EAI_MODEL } from "@/consts/enum";
  9. import { useUnload } from "@tarojs/taro";
  10. import { EChatRole, EContentType, TRobotMessage } from "@/types/bot";
  11. import { usePostMessage } from './message'
  12. import { getRecommendPrompt } from "@/service/bot"
  13. interface Props {
  14. agent: TAgentDetail | null;
  15. setShowWelcome?: (b: boolean) => void;
  16. setIsVoice?: (b: boolean) => void;
  17. setDisabled?: (b: boolean) => void;
  18. }
  19. let stopReceiveChunk: (() => void) | undefined;
  20. export const useChatInput = ({ agent, setShowWelcome, setDisabled, }: Props) => {
  21. const {
  22. pushRobotMessage,
  23. updateRobotMessage,
  24. getCurrentRobotMessage,
  25. updateRobotReasoningMessage,
  26. pushMessage,
  27. updateMessage,
  28. deleteMessage,
  29. setQuestions,
  30. questions,
  31. } = useTextChat();
  32. const { startTimedMessage, stopTimedMessage, saveMessageToServer } = usePostMessage(getCurrentRobotMessage);
  33. let myMsgUk = '';
  34. let mySessionId = '';
  35. const chatWithGpt = async (message: string, sessionId: string, msgUk: string) => {
  36. setShowWelcome?.(false)
  37. setQuestions([])
  38. let currentRobotMsgUk = "";
  39. await delay(300);
  40. setDisabled?.(true);
  41. if (!agent?.agentId) {
  42. return;
  43. }
  44. const loginId = getLoginId();
  45. if (!loginId) {
  46. return;
  47. }
  48. // const greeting = "欢迎光临我的智能体,你想问什么?";
  49. // {
  50. // content: greeting,
  51. // contentType: EContentType.TextPlain,
  52. // role: EChatRole.System,
  53. // },
  54. const newMsg = {
  55. content: message,
  56. contentType: EContentType.TextPlain,
  57. role: EChatRole.User,
  58. }
  59. // 等发送的消息上报完成
  60. const myMsgResponse = await saveMessageToServer({
  61. loginId,
  62. messages: [{
  63. ...newMsg,
  64. saveStatus: 2,
  65. isStreaming: false,
  66. msgUk,
  67. }],
  68. agentId: agent.agentId,
  69. sessionId,
  70. })
  71. if(!isSuccess(myMsgResponse.status)){
  72. return setDisabled?.(false);
  73. }
  74. // 发起文本聊天
  75. stopReceiveChunk = textChat({
  76. params: {
  77. agentId: agent.agentId,
  78. isEnableOutputAudioStream: false,
  79. isEnableSearch: false,
  80. isEnableThinking: false,
  81. loginId,
  82. messages: [newMsg],
  83. sessionId,
  84. },
  85. onStart: () => {
  86. // 推一个空回复,用于展示 ui
  87. const blankMessage = {
  88. role: EChatRole.Assistant,
  89. contentType: EContentType.TextPlain,
  90. saveStatus: 0,
  91. content: "",
  92. reasoningContent: "",
  93. isStreaming: false,
  94. msgUk: currentRobotMsgUk,
  95. dislikeReason: '',
  96. robot: {
  97. avatar: agent?.avatarUrl ?? "",
  98. name: agent?.name ?? "",
  99. agentId: agent?.agentId ?? "",
  100. },
  101. }
  102. currentRobotMsgUk = pushRobotMessage(blankMessage);
  103. // 先暂停
  104. stopTimedMessage()
  105. // 开始定时发送,每5秒发送一次
  106. startTimedMessage({
  107. loginId,
  108. messages: [{
  109. ...blankMessage,
  110. }],
  111. agentId: agent.agentId ?? '',
  112. sessionId,
  113. }, 5000);
  114. },
  115. onReceived: (m) => {
  116. console.log("received:", m);
  117. if (m.reasoningContent) {
  118. updateRobotReasoningMessage(
  119. currentRobotMsgUk,
  120. m.reasoningContent,
  121. m.body,
  122. );
  123. } else {
  124. updateRobotMessage(m.content, m.body);
  125. }
  126. },
  127. onFinished: async () => {
  128. const currentRobotMessage = getCurrentRobotMessage();
  129. console.log("回复完毕 ok, 当前robotmessage: ", currentRobotMessage);
  130. stopTimedMessage()
  131. if(!agent.agentId){
  132. return
  133. }
  134. setDisabled?.(false);
  135. // 如果没有任何回答,则显示
  136. if (!currentRobotMessage?.content?.length) {
  137. updateRobotMessage("服务器繁忙...");
  138. return
  139. }
  140. // 将智能体的回答保存至服务器
  141. // currentRobotMessage.content 保存的是当前完整的智能体回复信息文本
  142. const content = currentRobotMessage.content as string
  143. updateRobotMessage(content, currentRobotMessage?.body, 2, true)
  144. await saveMessageToServer({
  145. loginId,
  146. messages: [{
  147. saveStatus: 2,
  148. content: content,
  149. contentType: currentRobotMessage?.body?.contentType ?? EContentType.TextPlain,
  150. isStreaming: false,
  151. role: currentRobotMessage.role,
  152. msgUk: currentRobotMessage.msgUk,
  153. }],
  154. agentId: agent.agentId,
  155. sessionId,
  156. })
  157. const response = await getRecommendPrompt({
  158. agentId: agent.agentId,
  159. sessionId,
  160. })
  161. // todo: 如果用户快速输入需要将前面的问题答案丢弃,根据 currentRobotMessage.msgUk 来设置 questions
  162. if(isSuccess(response.status)){
  163. setQuestions(response.data.questions)
  164. }
  165. },
  166. onComplete: async ()=> {
  167. stopTimedMessage()
  168. console.log('回复 onComplete')
  169. // 为防止服务端没有终止消息,当接口请求结束时强制再保存一次消息体,以防定时保存的消息体漏掉最后一部分
  170. const currentRobotMessage = getCurrentRobotMessage();
  171. if(currentRobotMessage && agent.agentId){
  172. const content = currentRobotMessage.content as string
  173. await saveMessageToServer({
  174. loginId,
  175. messages: [{
  176. saveStatus: 2,
  177. content: content,
  178. contentType: currentRobotMessage?.body?.contentType ?? EContentType.TextPlain,
  179. isStreaming: false,
  180. role: currentRobotMessage.role,
  181. msgUk: currentRobotMessage.msgUk,
  182. }],
  183. agentId: agent.agentId,
  184. sessionId,
  185. })
  186. }
  187. setDisabled?.(false);
  188. },
  189. onError: () => {
  190. deleteMessage(currentRobotMsgUk);
  191. },
  192. });
  193. };
  194. const handleVoiceSend = (message: string) => {
  195. updateMessage(message, myMsgUk);
  196. chatWithGpt(message, mySessionId, myMsgUk);
  197. };
  198. const handleOnSend = async (message: string) => {
  199. if(!agent?.agentId){
  200. return
  201. }
  202. const {sessionId, msgUk} = pushMessage(message);
  203. chatWithGpt(message, sessionId, msgUk);
  204. };
  205. // 推一个自己的空气泡框
  206. const handleBeforeSend = () => {
  207. if(!agent?.agentId){
  208. return
  209. }
  210. const {sessionId, msgUk} = pushMessage("");
  211. myMsgUk = msgUk
  212. mySessionId = sessionId
  213. };
  214. // 发生主意识别错误时,删除当前自己发出的气泡框
  215. const handleVoiceError = () => {
  216. deleteMessage(myMsgUk);
  217. };
  218. useEffect(()=> {
  219. return ()=> {
  220. stopTimedMessage();
  221. }
  222. }, [])
  223. useUnload(() => {
  224. if (stopReceiveChunk) {
  225. stopReceiveChunk();
  226. stopTimedMessage();
  227. }
  228. });
  229. return {
  230. setQuestions,
  231. handleVoiceSend,
  232. handleOnSend,
  233. questions,
  234. handleBeforeSend,
  235. handleVoiceError,
  236. }
  237. }