Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. import { MessageType } from '@/constants/chat';
  2. import { fileIconMap } from '@/constants/common';
  3. import {
  4. useCreateToken,
  5. useFetchConversation,
  6. useFetchConversationList,
  7. useFetchDialog,
  8. useFetchDialogList,
  9. useFetchStats,
  10. useListToken,
  11. useRemoveConversation,
  12. useRemoveDialog,
  13. useRemoveToken,
  14. useSelectConversationList,
  15. useSelectDialogList,
  16. useSelectStats,
  17. useSelectTokenList,
  18. useSetDialog,
  19. useUpdateConversation,
  20. } from '@/hooks/chatHooks';
  21. import {
  22. useSetModalState,
  23. useShowDeleteConfirm,
  24. useTranslate,
  25. } from '@/hooks/commonHooks';
  26. import { useSendMessageWithSse } from '@/hooks/logicHooks';
  27. import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
  28. import {
  29. IAnswer,
  30. IConversation,
  31. IDialog,
  32. IStats,
  33. } from '@/interfaces/database/chat';
  34. import { IChunk } from '@/interfaces/database/knowledge';
  35. import { getFileExtension } from '@/utils';
  36. import { message } from 'antd';
  37. import dayjs, { Dayjs } from 'dayjs';
  38. import omit from 'lodash/omit';
  39. import trim from 'lodash/trim';
  40. import {
  41. ChangeEventHandler,
  42. useCallback,
  43. useEffect,
  44. useMemo,
  45. useRef,
  46. useState,
  47. } from 'react';
  48. import { useDispatch, useSearchParams, useSelector } from 'umi';
  49. import { v4 as uuid } from 'uuid';
  50. import { ChatSearchParams } from './constants';
  51. import {
  52. IClientConversation,
  53. IMessage,
  54. VariableTableDataType,
  55. } from './interface';
  56. import { ChatModelState } from './model';
  57. import { isConversationIdExist } from './utils';
  58. export const useSelectCurrentDialog = () => {
  59. const currentDialog: IDialog = useSelector(
  60. (state: any) => state.chatModel.currentDialog,
  61. );
  62. return currentDialog;
  63. };
  64. export const useFetchDialogOnMount = (
  65. dialogId: string,
  66. visible: boolean,
  67. ): IDialog => {
  68. const currentDialog: IDialog = useSelectCurrentDialog();
  69. const fetchDialog = useFetchDialog();
  70. useEffect(() => {
  71. if (dialogId && visible) {
  72. fetchDialog(dialogId);
  73. }
  74. }, [dialogId, fetchDialog, visible]);
  75. return currentDialog;
  76. };
  77. export const useSetCurrentDialog = () => {
  78. const dispatch = useDispatch();
  79. const currentDialog: IDialog = useSelector(
  80. (state: any) => state.chatModel.currentDialog,
  81. );
  82. const setCurrentDialog = useCallback(
  83. (dialogId: string) => {
  84. dispatch({
  85. type: 'chatModel/setCurrentDialog',
  86. payload: { id: dialogId },
  87. });
  88. },
  89. [dispatch],
  90. );
  91. return { currentDialog, setCurrentDialog };
  92. };
  93. export const useResetCurrentDialog = () => {
  94. const dispatch = useDispatch();
  95. const resetCurrentDialog = useCallback(() => {
  96. dispatch({
  97. type: 'chatModel/setCurrentDialog',
  98. payload: {},
  99. });
  100. }, [dispatch]);
  101. return { resetCurrentDialog };
  102. };
  103. export const useSelectPromptConfigParameters = (): VariableTableDataType[] => {
  104. const currentDialog: IDialog = useSelector(
  105. (state: any) => state.chatModel.currentDialog,
  106. );
  107. const finalParameters: VariableTableDataType[] = useMemo(() => {
  108. const parameters = currentDialog?.prompt_config?.parameters ?? [];
  109. if (!currentDialog.id) {
  110. // The newly created chat has a default parameter
  111. return [{ key: uuid(), variable: 'knowledge', optional: false }];
  112. }
  113. return parameters.map((x) => ({
  114. key: uuid(),
  115. variable: x.key,
  116. optional: x.optional,
  117. }));
  118. }, [currentDialog]);
  119. return finalParameters;
  120. };
  121. export const useDeleteDialog = () => {
  122. const showDeleteConfirm = useShowDeleteConfirm();
  123. const removeDocument = useRemoveDialog();
  124. const onRemoveDialog = (dialogIds: Array<string>) => {
  125. showDeleteConfirm({ onOk: () => removeDocument(dialogIds) });
  126. };
  127. return { onRemoveDialog };
  128. };
  129. export const useGetChatSearchParams = () => {
  130. const [currentQueryParameters] = useSearchParams();
  131. return {
  132. dialogId: currentQueryParameters.get(ChatSearchParams.DialogId) || '',
  133. conversationId:
  134. currentQueryParameters.get(ChatSearchParams.ConversationId) || '',
  135. };
  136. };
  137. export const useSetCurrentConversation = () => {
  138. const dispatch = useDispatch();
  139. const setCurrentConversation = useCallback(
  140. (currentConversation: IClientConversation) => {
  141. dispatch({
  142. type: 'chatModel/setCurrentConversation',
  143. payload: currentConversation,
  144. });
  145. },
  146. [dispatch],
  147. );
  148. return setCurrentConversation;
  149. };
  150. export const useClickDialogCard = () => {
  151. const [currentQueryParameters, setSearchParams] = useSearchParams();
  152. const newQueryParameters: URLSearchParams = useMemo(() => {
  153. return new URLSearchParams();
  154. }, []);
  155. const handleClickDialog = useCallback(
  156. (dialogId: string) => {
  157. newQueryParameters.set(ChatSearchParams.DialogId, dialogId);
  158. // newQueryParameters.set(
  159. // ChatSearchParams.ConversationId,
  160. // EmptyConversationId,
  161. // );
  162. setSearchParams(newQueryParameters);
  163. },
  164. [newQueryParameters, setSearchParams],
  165. );
  166. return { handleClickDialog };
  167. };
  168. export const useSelectFirstDialogOnMount = () => {
  169. const fetchDialogList = useFetchDialogList();
  170. const dialogList = useSelectDialogList();
  171. const { handleClickDialog } = useClickDialogCard();
  172. const fetchList = useCallback(async () => {
  173. const data = await fetchDialogList();
  174. if (data.retcode === 0 && data.data.length > 0) {
  175. handleClickDialog(data.data[0].id);
  176. }
  177. }, [fetchDialogList, handleClickDialog]);
  178. useEffect(() => {
  179. fetchList();
  180. }, [fetchList]);
  181. return dialogList;
  182. };
  183. export const useHandleItemHover = () => {
  184. const [activated, setActivated] = useState<string>('');
  185. const handleItemEnter = (id: string) => {
  186. setActivated(id);
  187. };
  188. const handleItemLeave = () => {
  189. setActivated('');
  190. };
  191. return {
  192. activated,
  193. handleItemEnter,
  194. handleItemLeave,
  195. };
  196. };
  197. export const useEditDialog = () => {
  198. const [dialog, setDialog] = useState<IDialog>({} as IDialog);
  199. const fetchDialog = useFetchDialog();
  200. const submitDialog = useSetDialog();
  201. const loading = useOneNamespaceEffectsLoading('chatModel', ['setDialog']);
  202. const {
  203. visible: dialogEditVisible,
  204. hideModal: hideDialogEditModal,
  205. showModal: showDialogEditModal,
  206. } = useSetModalState();
  207. const onDialogEditOk = useCallback(
  208. async (dialog: IDialog) => {
  209. const ret = await submitDialog(dialog);
  210. if (ret === 0) {
  211. hideDialogEditModal();
  212. }
  213. },
  214. [submitDialog, hideDialogEditModal],
  215. );
  216. const handleShowDialogEditModal = useCallback(
  217. async (dialogId?: string) => {
  218. if (dialogId) {
  219. const ret = await fetchDialog(dialogId, false);
  220. if (ret.retcode === 0) {
  221. setDialog(ret.data);
  222. }
  223. }
  224. showDialogEditModal();
  225. },
  226. [showDialogEditModal, fetchDialog],
  227. );
  228. const clearDialog = useCallback(() => {
  229. setDialog({} as IDialog);
  230. }, []);
  231. return {
  232. dialogSettingLoading: loading,
  233. initialDialog: dialog,
  234. onDialogEditOk,
  235. dialogEditVisible,
  236. hideDialogEditModal,
  237. showDialogEditModal: handleShowDialogEditModal,
  238. clearDialog,
  239. };
  240. };
  241. //#region conversation
  242. export const useFetchConversationListOnMount = () => {
  243. const conversationList = useSelectConversationList();
  244. const { dialogId } = useGetChatSearchParams();
  245. const fetchConversationList = useFetchConversationList();
  246. useEffect(() => {
  247. fetchConversationList(dialogId);
  248. }, [fetchConversationList, dialogId]);
  249. return conversationList;
  250. };
  251. export const useSelectDerivedConversationList = () => {
  252. const [list, setList] = useState<Array<IConversation>>([]);
  253. let chatModel: ChatModelState = useSelector((state: any) => state.chatModel);
  254. const { conversationList, currentDialog } = chatModel;
  255. const { dialogId } = useGetChatSearchParams();
  256. const prologue = currentDialog?.prompt_config?.prologue ?? '';
  257. const { t } = useTranslate('chat');
  258. const addTemporaryConversation = useCallback(() => {
  259. setList((pre) => {
  260. if (dialogId) {
  261. const nextList = [
  262. {
  263. id: '',
  264. name: t('newConversation'),
  265. dialog_id: dialogId,
  266. message: [
  267. {
  268. content: prologue,
  269. role: MessageType.Assistant,
  270. },
  271. ],
  272. } as IConversation,
  273. ...conversationList,
  274. ];
  275. return nextList;
  276. }
  277. return pre;
  278. });
  279. }, [conversationList, dialogId, prologue, t]);
  280. useEffect(() => {
  281. addTemporaryConversation();
  282. }, [addTemporaryConversation]);
  283. return { list, addTemporaryConversation };
  284. };
  285. export const useClickConversationCard = () => {
  286. const [currentQueryParameters, setSearchParams] = useSearchParams();
  287. const newQueryParameters: URLSearchParams = useMemo(
  288. () => new URLSearchParams(currentQueryParameters.toString()),
  289. [currentQueryParameters],
  290. );
  291. const handleClickConversation = useCallback(
  292. (conversationId: string) => {
  293. newQueryParameters.set(ChatSearchParams.ConversationId, conversationId);
  294. setSearchParams(newQueryParameters);
  295. },
  296. [newQueryParameters, setSearchParams],
  297. );
  298. return { handleClickConversation };
  299. };
  300. export const useSetConversation = () => {
  301. const { dialogId } = useGetChatSearchParams();
  302. const updateConversation = useUpdateConversation();
  303. const setConversation = useCallback(
  304. (message: string) => {
  305. return updateConversation({
  306. dialog_id: dialogId,
  307. name: message,
  308. message: [
  309. {
  310. role: MessageType.Assistant,
  311. content: message,
  312. },
  313. ],
  314. });
  315. },
  316. [updateConversation, dialogId],
  317. );
  318. return { setConversation };
  319. };
  320. export const useSelectCurrentConversation = () => {
  321. const [currentConversation, setCurrentConversation] =
  322. useState<IClientConversation>({} as IClientConversation);
  323. const conversation: IClientConversation = useSelector(
  324. (state: any) => state.chatModel.currentConversation,
  325. );
  326. const dialog = useSelectCurrentDialog();
  327. const { conversationId, dialogId } = useGetChatSearchParams();
  328. const addNewestConversation = useCallback(
  329. (message: string, answer: string = '') => {
  330. setCurrentConversation((pre) => {
  331. return {
  332. ...pre,
  333. message: [
  334. ...pre.message,
  335. {
  336. role: MessageType.User,
  337. content: message,
  338. id: uuid(),
  339. } as IMessage,
  340. {
  341. role: MessageType.Assistant,
  342. content: answer,
  343. id: uuid(),
  344. reference: {},
  345. } as IMessage,
  346. ],
  347. };
  348. });
  349. },
  350. [],
  351. );
  352. const addNewestAnswer = useCallback((answer: IAnswer) => {
  353. setCurrentConversation((pre) => {
  354. const latestMessage = pre.message?.at(-1);
  355. if (latestMessage) {
  356. return {
  357. ...pre,
  358. message: [
  359. ...pre.message.slice(0, -1),
  360. {
  361. ...latestMessage,
  362. content: answer.answer,
  363. reference: answer.reference,
  364. } as IMessage,
  365. ],
  366. };
  367. }
  368. return pre;
  369. });
  370. }, []);
  371. const removeLatestMessage = useCallback(() => {
  372. setCurrentConversation((pre) => {
  373. const nextMessages = pre.message?.slice(0, -2) ?? [];
  374. return {
  375. ...pre,
  376. message: nextMessages,
  377. };
  378. });
  379. }, []);
  380. const addPrologue = useCallback(() => {
  381. if (dialogId !== '' && conversationId === '') {
  382. const prologue = dialog.prompt_config?.prologue;
  383. const nextMessage = {
  384. role: MessageType.Assistant,
  385. content: prologue,
  386. id: uuid(),
  387. } as IMessage;
  388. setCurrentConversation({
  389. id: '',
  390. dialog_id: dialogId,
  391. reference: [],
  392. message: [nextMessage],
  393. } as any);
  394. }
  395. }, [conversationId, dialog, dialogId]);
  396. useEffect(() => {
  397. addPrologue();
  398. }, [addPrologue]);
  399. useEffect(() => {
  400. if (conversationId) {
  401. setCurrentConversation(conversation);
  402. }
  403. }, [conversation, conversationId]);
  404. return {
  405. currentConversation,
  406. addNewestConversation,
  407. removeLatestMessage,
  408. addNewestAnswer,
  409. };
  410. };
  411. export const useScrollToBottom = (currentConversation: IClientConversation) => {
  412. const ref = useRef<HTMLDivElement>(null);
  413. const scrollToBottom = useCallback(() => {
  414. if (currentConversation.id) {
  415. ref.current?.scrollIntoView({ behavior: 'instant' });
  416. }
  417. }, [currentConversation]);
  418. useEffect(() => {
  419. scrollToBottom();
  420. }, [scrollToBottom]);
  421. return ref;
  422. };
  423. export const useFetchConversationOnMount = () => {
  424. const { conversationId } = useGetChatSearchParams();
  425. const fetchConversation = useFetchConversation();
  426. const {
  427. currentConversation,
  428. addNewestConversation,
  429. removeLatestMessage,
  430. addNewestAnswer,
  431. } = useSelectCurrentConversation();
  432. const ref = useScrollToBottom(currentConversation);
  433. const fetchConversationOnMount = useCallback(() => {
  434. if (isConversationIdExist(conversationId)) {
  435. fetchConversation(conversationId);
  436. }
  437. }, [fetchConversation, conversationId]);
  438. useEffect(() => {
  439. fetchConversationOnMount();
  440. }, [fetchConversationOnMount]);
  441. return {
  442. currentConversation,
  443. addNewestConversation,
  444. ref,
  445. removeLatestMessage,
  446. addNewestAnswer,
  447. };
  448. };
  449. export const useHandleMessageInputChange = () => {
  450. const [value, setValue] = useState('');
  451. const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
  452. const value = e.target.value;
  453. const nextValue = value.replaceAll('\\n', '\n').replaceAll('\\t', '\t');
  454. setValue(nextValue);
  455. };
  456. return {
  457. handleInputChange,
  458. value,
  459. setValue,
  460. };
  461. };
  462. export const useSendMessage = (
  463. conversation: IClientConversation,
  464. addNewestConversation: (message: string, answer?: string) => void,
  465. removeLatestMessage: () => void,
  466. addNewestAnswer: (answer: IAnswer) => void,
  467. ) => {
  468. const { setConversation } = useSetConversation();
  469. const { conversationId } = useGetChatSearchParams();
  470. const { handleInputChange, value, setValue } = useHandleMessageInputChange();
  471. const { handleClickConversation } = useClickConversationCard();
  472. const { send, answer, done } = useSendMessageWithSse();
  473. const sendMessage = useCallback(
  474. async (message: string, id?: string) => {
  475. const res = await send({
  476. conversation_id: id ?? conversationId,
  477. messages: [
  478. ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')),
  479. {
  480. role: MessageType.User,
  481. content: message,
  482. },
  483. ],
  484. });
  485. if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) {
  486. // cancel loading
  487. setValue(message);
  488. console.info('removeLatestMessage111');
  489. removeLatestMessage();
  490. } else {
  491. if (id) {
  492. console.info('111');
  493. // new conversation
  494. handleClickConversation(id);
  495. } else {
  496. console.info('222');
  497. // fetchConversation(conversationId);
  498. }
  499. }
  500. },
  501. [
  502. conversation?.message,
  503. conversationId,
  504. handleClickConversation,
  505. removeLatestMessage,
  506. setValue,
  507. send,
  508. ],
  509. );
  510. const handleSendMessage = useCallback(
  511. async (message: string) => {
  512. if (conversationId !== '') {
  513. sendMessage(message);
  514. } else {
  515. const data = await setConversation(message);
  516. if (data.retcode === 0) {
  517. const id = data.data.id;
  518. sendMessage(message, id);
  519. }
  520. }
  521. },
  522. [conversationId, setConversation, sendMessage],
  523. );
  524. useEffect(() => {
  525. if (answer.answer && answer?.conversationId === conversationId) {
  526. addNewestAnswer(answer);
  527. }
  528. }, [answer, addNewestAnswer, conversationId]);
  529. const handlePressEnter = useCallback(() => {
  530. if (trim(value) === '') return;
  531. if (done) {
  532. setValue('');
  533. handleSendMessage(value.trim());
  534. }
  535. addNewestConversation(value);
  536. }, [addNewestConversation, handleSendMessage, done, setValue, value]);
  537. return {
  538. handlePressEnter,
  539. handleInputChange,
  540. value,
  541. setValue,
  542. loading: !done,
  543. };
  544. };
  545. export const useGetFileIcon = () => {
  546. const getFileIcon = (filename: string) => {
  547. const ext: string = getFileExtension(filename);
  548. const iconPath = fileIconMap[ext as keyof typeof fileIconMap];
  549. return `@/assets/svg/file-icon/${iconPath}`;
  550. };
  551. return getFileIcon;
  552. };
  553. export const useDeleteConversation = () => {
  554. const { dialogId } = useGetChatSearchParams();
  555. const { handleClickConversation } = useClickConversationCard();
  556. const showDeleteConfirm = useShowDeleteConfirm();
  557. const removeConversation = useRemoveConversation();
  558. const deleteConversation = (conversationIds: Array<string>) => async () => {
  559. const ret = await removeConversation(conversationIds, dialogId);
  560. if (ret === 0) {
  561. handleClickConversation('');
  562. }
  563. return ret;
  564. };
  565. const onRemoveConversation = (conversationIds: Array<string>) => {
  566. showDeleteConfirm({ onOk: deleteConversation(conversationIds) });
  567. };
  568. return { onRemoveConversation };
  569. };
  570. export const useRenameConversation = () => {
  571. const [conversation, setConversation] = useState<IClientConversation>(
  572. {} as IClientConversation,
  573. );
  574. const fetchConversation = useFetchConversation();
  575. const {
  576. visible: conversationRenameVisible,
  577. hideModal: hideConversationRenameModal,
  578. showModal: showConversationRenameModal,
  579. } = useSetModalState();
  580. const updateConversation = useUpdateConversation();
  581. const onConversationRenameOk = useCallback(
  582. async (name: string) => {
  583. const ret = await updateConversation({
  584. ...conversation,
  585. conversation_id: conversation.id,
  586. name,
  587. });
  588. if (ret.retcode === 0) {
  589. hideConversationRenameModal();
  590. }
  591. },
  592. [updateConversation, conversation, hideConversationRenameModal],
  593. );
  594. const loading = useOneNamespaceEffectsLoading('chatModel', [
  595. 'setConversation',
  596. ]);
  597. const handleShowConversationRenameModal = useCallback(
  598. async (conversationId: string) => {
  599. const ret = await fetchConversation(conversationId, false);
  600. if (ret.retcode === 0) {
  601. setConversation(ret.data);
  602. }
  603. showConversationRenameModal();
  604. },
  605. [showConversationRenameModal, fetchConversation],
  606. );
  607. return {
  608. conversationRenameLoading: loading,
  609. initialConversationName: conversation.name,
  610. onConversationRenameOk,
  611. conversationRenameVisible,
  612. hideConversationRenameModal,
  613. showConversationRenameModal: handleShowConversationRenameModal,
  614. };
  615. };
  616. export const useClickDrawer = () => {
  617. const { visible, showModal, hideModal } = useSetModalState();
  618. const [selectedChunk, setSelectedChunk] = useState<IChunk>({} as IChunk);
  619. const [documentId, setDocumentId] = useState<string>('');
  620. const clickDocumentButton = useCallback(
  621. (documentId: string, chunk: IChunk) => {
  622. showModal();
  623. setSelectedChunk(chunk);
  624. setDocumentId(documentId);
  625. },
  626. [showModal],
  627. );
  628. return {
  629. clickDocumentButton,
  630. visible,
  631. showModal,
  632. hideModal,
  633. selectedChunk,
  634. documentId,
  635. };
  636. };
  637. export const useSelectDialogListLoading = () => {
  638. return useOneNamespaceEffectsLoading('chatModel', ['listDialog']);
  639. };
  640. export const useSelectConversationListLoading = () => {
  641. return useOneNamespaceEffectsLoading('chatModel', ['listConversation']);
  642. };
  643. export const useSelectConversationLoading = () => {
  644. return useOneNamespaceEffectsLoading('chatModel', ['getConversation']);
  645. };
  646. export const useGetSendButtonDisabled = () => {
  647. const { dialogId, conversationId } = useGetChatSearchParams();
  648. return dialogId === '' && conversationId === '';
  649. };
  650. export const useSendButtonDisabled = (value: string) => {
  651. return trim(value) === '';
  652. };
  653. //#endregion
  654. //#region API provided for external calls
  655. type RangeValue = [Dayjs | null, Dayjs | null] | null;
  656. const getDay = (date: Dayjs) => date.format('YYYY-MM-DD');
  657. export const useFetchStatsOnMount = (visible: boolean) => {
  658. const fetchStats = useFetchStats();
  659. const [pickerValue, setPickerValue] = useState<RangeValue>([
  660. dayjs(),
  661. dayjs().subtract(7, 'day'),
  662. ]);
  663. useEffect(() => {
  664. if (visible && Array.isArray(pickerValue) && pickerValue[0]) {
  665. fetchStats({
  666. fromDate: getDay(pickerValue[0]),
  667. toDate: getDay(pickerValue[1] ?? dayjs()),
  668. });
  669. }
  670. }, [fetchStats, pickerValue, visible]);
  671. return {
  672. pickerValue,
  673. setPickerValue,
  674. };
  675. };
  676. export const useOperateApiKey = (visible: boolean, dialogId: string) => {
  677. const removeToken = useRemoveToken();
  678. const createToken = useCreateToken(dialogId);
  679. const listToken = useListToken();
  680. const tokenList = useSelectTokenList();
  681. const creatingLoading = useOneNamespaceEffectsLoading('chatModel', [
  682. 'createToken',
  683. ]);
  684. const listLoading = useOneNamespaceEffectsLoading('chatModel', ['list']);
  685. const showDeleteConfirm = useShowDeleteConfirm();
  686. const onRemoveToken = (token: string, tenantId: string) => {
  687. showDeleteConfirm({
  688. onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
  689. });
  690. };
  691. useEffect(() => {
  692. if (visible && dialogId) {
  693. listToken(dialogId);
  694. }
  695. }, [listToken, dialogId, visible]);
  696. return {
  697. removeToken: onRemoveToken,
  698. createToken,
  699. tokenList,
  700. creatingLoading,
  701. listLoading,
  702. };
  703. };
  704. type ChartStatsType = {
  705. [k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
  706. };
  707. export const useSelectChartStatsList = (): ChartStatsType => {
  708. const stats: IStats = useSelectStats();
  709. // const stats = {
  710. // pv: [
  711. // ['2024-06-01', 1],
  712. // ['2024-07-24', 3],
  713. // ['2024-09-01', 10],
  714. // ],
  715. // uv: [
  716. // ['2024-02-01', 0],
  717. // ['2024-03-01', 99],
  718. // ['2024-05-01', 3],
  719. // ],
  720. // speed: [
  721. // ['2024-09-01', 2],
  722. // ['2024-09-01', 3],
  723. // ],
  724. // tokens: [
  725. // ['2024-09-01', 1],
  726. // ['2024-09-01', 3],
  727. // ],
  728. // round: [
  729. // ['2024-09-01', 0],
  730. // ['2024-09-01', 3],
  731. // ],
  732. // thumb_up: [
  733. // ['2024-09-01', 3],
  734. // ['2024-09-01', 9],
  735. // ],
  736. // };
  737. return Object.keys(stats).reduce((pre, cur) => {
  738. const item = stats[cur as keyof IStats];
  739. if (item.length > 0) {
  740. pre[cur as keyof IStats] = item.map((x) => ({
  741. xAxis: x[0] as string,
  742. yAxis: x[1] as number,
  743. }));
  744. }
  745. return pre;
  746. }, {} as ChartStatsType);
  747. };
  748. export const useShowTokenEmptyError = () => {
  749. const [messageApi, contextHolder] = message.useMessage();
  750. const { t } = useTranslate('chat');
  751. const showTokenEmptyError = useCallback(() => {
  752. messageApi.error(t('tokenError'));
  753. }, [messageApi, t]);
  754. return { showTokenEmptyError, contextHolder };
  755. };
  756. const getUrlWithToken = (token: string) => {
  757. const { protocol, host } = window.location;
  758. return `${protocol}//${host}/chat/share?shared_id=${token}`;
  759. };
  760. const useFetchTokenListBeforeOtherStep = (dialogId: string) => {
  761. const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
  762. const listToken = useListToken();
  763. const tokenList = useSelectTokenList();
  764. const token =
  765. Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
  766. const handleOperate = useCallback(async () => {
  767. const data = await listToken(dialogId);
  768. const list = data.data;
  769. if (data.retcode === 0 && Array.isArray(list) && list.length > 0) {
  770. return list[0]?.token;
  771. } else {
  772. showTokenEmptyError();
  773. return false;
  774. }
  775. }, [dialogId, listToken, showTokenEmptyError]);
  776. return {
  777. token,
  778. contextHolder,
  779. handleOperate,
  780. };
  781. };
  782. export const useShowEmbedModal = (dialogId: string) => {
  783. const {
  784. visible: embedVisible,
  785. hideModal: hideEmbedModal,
  786. showModal: showEmbedModal,
  787. } = useSetModalState();
  788. const { handleOperate, token, contextHolder } =
  789. useFetchTokenListBeforeOtherStep(dialogId);
  790. const handleShowEmbedModal = useCallback(async () => {
  791. const succeed = await handleOperate();
  792. if (succeed) {
  793. showEmbedModal();
  794. }
  795. }, [handleOperate, showEmbedModal]);
  796. return {
  797. showEmbedModal: handleShowEmbedModal,
  798. hideEmbedModal,
  799. embedVisible,
  800. embedToken: token,
  801. errorContextHolder: contextHolder,
  802. };
  803. };
  804. export const usePreviewChat = (dialogId: string) => {
  805. const { handleOperate, contextHolder } =
  806. useFetchTokenListBeforeOtherStep(dialogId);
  807. const open = useCallback((t: string) => {
  808. window.open(getUrlWithToken(t), '_blank');
  809. }, []);
  810. const handlePreview = useCallback(async () => {
  811. const token = await handleOperate();
  812. if (token) {
  813. open(token);
  814. }
  815. }, [handleOperate, open]);
  816. return {
  817. handlePreview,
  818. contextHolder,
  819. };
  820. };
  821. //#endregion