use-chat.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { ChatContext } from '@/app/chat-context';
  2. import i18n from '@/app/i18n';
  3. import { getUserId } from '@/utils';
  4. import { HEADER_USER_ID_KEY } from '@/utils/constants/index';
  5. import { EventStreamContentType, fetchEventSource } from '@microsoft/fetch-event-source';
  6. import { message } from 'antd';
  7. import { useCallback, useContext, useState } from 'react';
  8. type Props = {
  9. queryAgentURL?: string;
  10. app_code?: string;
  11. };
  12. type ChatParams = {
  13. chatId: string;
  14. ctrl?: AbortController;
  15. data?: any;
  16. query?: Record<string, string>;
  17. onMessage: (message: string) => void;
  18. onClose?: () => void;
  19. onDone?: () => void;
  20. onError?: (content: string, error?: Error) => void;
  21. };
  22. const useChat = ({ queryAgentURL = '/api/v1/chat/completions', app_code }: Props) => {
  23. const [ctrl, setCtrl] = useState<AbortController>({} as AbortController);
  24. const { scene } = useContext(ChatContext);
  25. const chat = useCallback(
  26. async ({ data, chatId, onMessage, onClose, onDone, onError, ctrl }: ChatParams) => {
  27. ctrl && setCtrl(ctrl);
  28. if (!data?.user_input && !data?.doc_id) {
  29. message.warning(i18n.t('no_context_tip'));
  30. return;
  31. }
  32. const params = {
  33. ...data,
  34. conv_uid: chatId,
  35. app_code,
  36. };
  37. // if (!params.conv_uid) {
  38. // message.error('conv_uid 不存在,请刷新后重试');
  39. // return;
  40. // }
  41. try {
  42. await fetchEventSource(`${process.env.API_BASE_URL ?? ''}${queryAgentURL}`, {
  43. method: 'POST',
  44. headers: {
  45. 'Content-Type': 'application/json',
  46. [HEADER_USER_ID_KEY]: getUserId() ?? '',
  47. },
  48. body: JSON.stringify(params),
  49. signal: ctrl ? ctrl.signal : null,
  50. openWhenHidden: true,
  51. async onopen(response) {
  52. if (response.ok && response.headers.get('content-type') === EventStreamContentType) {
  53. return;
  54. }
  55. if (response.headers.get('content-type') === 'application/json') {
  56. response.json().then(data => {
  57. onMessage?.(data);
  58. onDone?.();
  59. ctrl && ctrl.abort();
  60. });
  61. }
  62. },
  63. onclose() {
  64. ctrl && ctrl.abort();
  65. onClose?.();
  66. },
  67. onerror(err) {
  68. throw new Error(err);
  69. },
  70. onmessage: event => {
  71. let message = event.data;
  72. try {
  73. if (scene === 'chat_agent') {
  74. message = JSON.parse(message).vis;
  75. } else {
  76. message = JSON.parse(message);
  77. }
  78. } catch {
  79. message.replaceAll('\\n', '\n');
  80. }
  81. if (typeof message === 'string') {
  82. if (message === '[DONE]') {
  83. onDone?.();
  84. } else if (message?.startsWith('[ERROR]')) {
  85. onError?.(message?.replace('[ERROR]', ''));
  86. } else {
  87. onMessage?.(message);
  88. }
  89. } else {
  90. onMessage?.(message);
  91. onDone?.();
  92. }
  93. },
  94. });
  95. } catch (err) {
  96. ctrl && ctrl.abort();
  97. onError?.('Sorry, We meet some error, please try agin later.', err as Error);
  98. }
  99. },
  100. [queryAgentURL, app_code, scene],
  101. );
  102. return { chat, ctrl };
  103. };
  104. export default useChat;