@mujian/js-sdk 0.0.6-beta.mjv.68 → 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/events/index.d.ts +25 -5
  2. package/dist/index.d.ts +41 -2
  3. package/dist/index.js +143 -184
  4. package/dist/lite.d.ts +73 -0
  5. package/dist/lite.js +124 -0
  6. package/dist/modules/ai/chat/chat.d.ts +18 -26
  7. package/dist/modules/ai/chat/index.d.ts +1 -1
  8. package/dist/modules/ai/chat/message/index.d.ts +7 -16
  9. package/dist/modules/ai/chat/settings/index.d.ts +1 -2
  10. package/dist/modules/ai/chat/settings/persona.d.ts +0 -8
  11. package/dist/modules/ai/openai/chat.d.ts +0 -3
  12. package/dist/modules/ai/openai/completions.d.ts +0 -2
  13. package/dist/modules/ai/openai/images.d.ts +0 -2
  14. package/dist/modules/ai/openai/responses.d.ts +0 -2
  15. package/dist/modules/config.d.ts +17 -0
  16. package/dist/react/chat/useChat/index.d.ts +27 -5
  17. package/dist/react/chat/useChat/inner/chatStreaming.d.ts +8 -4
  18. package/dist/react/chat/useChat/inner/stream.d.ts +25 -2
  19. package/dist/react/chat/useChat/message.d.ts +27 -38
  20. package/dist/react/components/MdRenderer/index.d.ts +3 -2
  21. package/dist/react/components/MujianProvider/index.d.ts +4 -0
  22. package/dist/react/components/Thread/MessageItem.d.ts +6 -0
  23. package/dist/react/components/index.d.ts +1 -1
  24. package/dist/react/index.d.ts +1 -0
  25. package/dist/react/mjEngine/index.d.ts +48 -0
  26. package/dist/react.js +446 -343
  27. package/dist/types/index.d.ts +1 -0
  28. package/dist/umd/index.js +1 -791
  29. package/dist/umd/lite.js +2 -0
  30. package/dist/umd/lite.js.LICENSE.txt +7 -0
  31. package/dist/umd/react.css +1 -343
  32. package/dist/umd/react.js +95 -8807
  33. package/dist/umd/react.js.LICENSE.txt +1 -1
  34. package/dist/utils/is-network-error.d.ts +1 -0
  35. package/package.json +14 -6
  36. package/dist/modules/ai/chat/settings/model.d.ts +0 -24
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useInfiniteScroll, useLatest, useRequest, useUpdateEffect } from "ahooks";
1
+ import { useInfiniteScroll, useLatest, useRequest, useUnmount, useUpdateEffect } from "ahooks";
2
2
  import react, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
  import css_tools from "@adobe/css-tools";
@@ -7,6 +7,9 @@ import showdown from "showdown";
7
7
  import { v4 } from "uuid";
8
8
  import postmate from "postmate";
9
9
  import { Virtualizer } from "virtua";
10
+ import { get } from "lodash-es";
11
+ import { produce } from "immer";
12
+ import { z } from "zod";
10
13
  const adjustIframeHeight = (iframeId)=>`
11
14
  (function () {
12
15
  let scheduled = false;
@@ -404,7 +407,14 @@ function messageFormatting(mes, sanitizerOverrides = {}) {
404
407
  });
405
408
  return mes;
406
409
  }
407
- const MdRendererBase = ({ content, unsafe = false, extra = {} })=>{
410
+ const loadingTranslates = {
411
+ 'zh-hans': '加载中...',
412
+ 'zh-hant': '載入中...',
413
+ en: 'Loading...',
414
+ ja: '読み込み中...',
415
+ th: 'กำลังโหลด...'
416
+ };
417
+ const MdRendererBase = ({ content, unsafe = false, extra = {}, lang })=>{
408
418
  const containerRef = useRef(null);
409
419
  const [iframeIdList, setIframeIdList] = useState([]);
410
420
  useEffect(()=>{
@@ -421,16 +431,12 @@ const MdRendererBase = ({ content, unsafe = false, extra = {} })=>{
421
431
  id
422
432
  ]);
423
433
  const escapedSrcdoc = escapeHtmlAttribute(srcdoc);
434
+ const loadingText = loadingTranslates[lang ?? 'zh-hans'] || loadingTranslates['zh-hans'];
424
435
  return `
425
436
  <div class="MJ-iframe-container" id="${containerId}" style="display:flex;width:100%;height:100%;position:relative;">
426
437
  <div class="spin-overlay" id="MJ-spin-${id}" style="width:100%;height:100%;position:absolute;top:0;left:0;z-index:1000;background-color:rgba(0,0,0,0.5);display:flex;justify-content:center;align-items:center;">
427
438
  <div class="spin-inner wave-text" style="font-weight:500;font-size:16px;color:white;display:flex;justify-content:center;align-items:center;">
428
- <span>加</span>
429
- <span>载</span>
430
- <span>中</span>
431
- <span>.</span>
432
- <span>.</span>
433
- <span>.</span>
439
+ ${loadingText.split('').map((t)=>`<span>${t}</span>`).join('')}
434
440
  </div>
435
441
  </div>
436
442
  <iframe id="${iframeId}" class="w-full" sandbox="${unsafe ? "allow-scripts" : "allow-scripts allow-same-origin"}" loading="lazy"
@@ -509,6 +515,11 @@ var events_EVENT = /*#__PURE__*/ function(EVENT) {
509
515
  EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_ALL"] = "mujian:ai:chat:message:getAll";
510
516
  EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_PAGE"] = "mujian:ai:chat:message:getPage";
511
517
  EVENT["MUJIAN_AI_CHAT_PROJECT_GET_INFO"] = "mujian:ai:chat:project:getInfo";
518
+ EVENT["MUJIAN_AI_CHAT_V2_COMPLETE"] = "mujian:ai:chat_v2:complete";
519
+ EVENT["MUJIAN_AI_CHAT_V2_RECOVER"] = "mujian:ai:chat_v2:recover";
520
+ EVENT["MUJIAN_AI_CHAT_V2_STOP"] = "mujian:ai:chat_v2:stop";
521
+ EVENT["MUJIAN_AI_CHAT_V2_MESSAGE_GET_PAGE"] = "mujian:ai:chat_v2:message:getPage";
522
+ EVENT["MUJIAN_AI_CHAT_V2_RENDER_MESSAGE"] = "mujian:ai:chat_v2:renderMessage";
512
523
  EVENT["MUJIAN_AI_SETTINGS_PERSONA_GET_ACTIVE"] = "mujian:ai:settings:persona:getActive";
513
524
  EVENT["MUJIAN_AI_SETTINGS_PERSONA_SET_ACTIVE"] = "mujian:ai:settings:persona:setActive";
514
525
  EVENT["MUJIAN_AI_SETTINGS_MODEL_GET_ALL"] = "mujian:ai:settings:model:getAll";
@@ -517,87 +528,51 @@ var events_EVENT = /*#__PURE__*/ function(EVENT) {
517
528
  EVENT["MUJIAN_AI_CHAT_MESSAGE_DELETE_ONE"] = "mujian:ai:chat:message:deleteOne";
518
529
  EVENT["MUJIAN_AI_CHAT_MESSAGE_EDIT_ONE"] = "mujian:ai:chat:message:editOne";
519
530
  EVENT["MUJIAN_AI_CHAT_MESSAGE_SWIPE"] = "mujian:ai:chat:message:swipe";
520
- EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_PROMPT"] = "mujian:ai:chat:message:getPrompt";
521
531
  EVENT["MUJIAN_AI_OPENAI_IMAGES_GENERATE"] = "mujian:ai:openai:images:generate";
522
532
  EVENT["MUJIAN_UTILS_CLIPBOARD_WRITE_TEXT"] = "mujian:utils:clipboard:writeText";
533
+ EVENT["MUJIAN_GET_CONFIG"] = "mujian:getConfig";
523
534
  return EVENT;
524
535
  }({});
525
- function wrapOnData(onData) {
526
- let fullContent = '';
527
- let buffer = '';
528
- let questionId;
529
- let replyId;
530
- return function(data) {
531
- buffer += data;
532
- const lines = buffer.split('\n');
533
- buffer = lines.pop() || '';
534
- for (const line of lines)if (line.startsWith('data: ')) try {
535
- const parsedData = JSON.parse(line.slice(6));
536
- if (parsedData.question_id) questionId = parsedData.question_id;
537
- if (parsedData.reply_id) replyId = parsedData.reply_id;
538
- if (parsedData.isFinished) return void onData({
539
- isFinished: true,
540
- deltaContent: '',
541
- fullContent,
542
- questionId,
543
- replyId
544
- });
545
- const deltaContent = parsedData?.choices?.[0]?.delta?.content;
546
- if (deltaContent?.length > 0) {
547
- fullContent += deltaContent;
548
- onData({
549
- isFinished: false,
550
- deltaContent: deltaContent,
551
- fullContent,
552
- questionId,
553
- replyId
554
- });
555
- }
556
- } catch (e) {
557
- onData({
558
- isFinished: true,
559
- error: e,
560
- deltaContent: '',
561
- fullContent,
562
- questionId,
563
- replyId
564
- });
565
- return;
566
- }
567
- };
568
- }
569
- const chat_complete = async function(message, onData, signal, option = {}) {
570
- await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
571
- content: message
536
+ const chat_complete = async function(message, conversationVersion, onData) {
537
+ await this.call(events_EVENT.MUJIAN_AI_CHAT_V2_COMPLETE, {
538
+ content: message,
539
+ conversationVersion
572
540
  }, {
573
- onData: option.parseContent ? wrapOnData(onData) : onData,
574
- signal
541
+ onData
575
542
  });
576
543
  };
577
- const applyRegex = async function(props) {
578
- return await this.call(events_EVENT.MUJIAN_AI_CHAT_APPLY_REGEX, props);
579
- };
580
544
  const renderMessage = async function(props) {
581
- return await this.call(events_EVENT.MUJIAN_AI_CHAT_RENDER_MESSAGE, props);
545
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_V2_RENDER_MESSAGE, props);
582
546
  };
583
- const continueComplete = async function(onData, signal) {
584
- return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
585
- isContinue: true
547
+ const continueComplete = async function(conversationVersion, onData) {
548
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_V2_COMPLETE, {
549
+ isContinue: true,
550
+ conversationVersion
586
551
  }, {
587
- onData,
588
- signal
552
+ onData
589
553
  });
590
554
  };
591
- const chat_regenerate = async function(onData, signal) {
592
- return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
593
- isRegenerate: true
555
+ const chat_regenerate = async function(conversationVersion, onData) {
556
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_V2_COMPLETE, {
557
+ isRegenerate: true,
558
+ conversationVersion
594
559
  }, {
595
- onData,
596
- signal
560
+ onData
561
+ });
562
+ };
563
+ const chat_stop = async function(conversationVersion, assisMsgId) {
564
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_V2_STOP, {
565
+ conversationVersion,
566
+ assisMsgId
597
567
  });
598
568
  };
599
- const getAll = async function() {
600
- return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_ALL);
569
+ const recover = async function(conversationVersion, assisMsgId, onData) {
570
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_V2_RECOVER, {
571
+ conversationVersion,
572
+ assisMsgId
573
+ }, {
574
+ onData
575
+ });
601
576
  };
602
577
  const messageDeleteOne = async function(messageId) {
603
578
  return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_DELETE_ONE, {
@@ -616,13 +591,8 @@ const messageSwipe = async function(messageId, swipeId) {
616
591
  swipeId
617
592
  });
618
593
  };
619
- const getPrompt = async function(messageId) {
620
- return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_PROMPT, {
621
- messageId
622
- });
623
- };
624
594
  async function getPage(fromCursor, pageSize) {
625
- return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_PAGE, {
595
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_V2_MESSAGE_GET_PAGE, {
626
596
  fromCursor,
627
597
  pageSize
628
598
  });
@@ -633,22 +603,6 @@ const getInfo = async function() {
633
603
  const getActive = async function() {
634
604
  return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_GET_ACTIVE);
635
605
  };
636
- const setActive = async function(personaId) {
637
- return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_SET_ACTIVE, {
638
- personaId
639
- });
640
- };
641
- const model_getActive = async function() {
642
- return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ACTIVE);
643
- };
644
- const model_setActive = async function(modelId) {
645
- return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_SET_ACTIVE, {
646
- modelId
647
- });
648
- };
649
- const model_getAll = async function() {
650
- return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ALL);
651
- };
652
606
  const generate = async function(content) {
653
607
  return await this.call(MujianSdk.EVENT.MUJIAN_AI_TEXT_GENERATE, {
654
608
  content
@@ -700,6 +654,9 @@ const images_images = {
700
654
  });
701
655
  }
702
656
  };
657
+ async function getConfig() {
658
+ return await this.call(events_EVENT.MUJIAN_GET_CONFIG);
659
+ }
703
660
  const saveGame = async function() {};
704
661
  const loadGame = async function() {};
705
662
  async function writeText(text) {
@@ -718,48 +675,59 @@ class MujianSdk {
718
675
  return this.ready;
719
676
  }
720
677
  pendingRequests = new Map();
678
+ _config;
679
+ get openapi() {
680
+ return this._config?.openapi;
681
+ }
682
+ initPromise = null;
721
683
  async init() {
722
- const handshake = new postmate.Model({
723
- reply: ({ id, complete, data, error })=>{
724
- const call = this.pendingRequests.get(id);
725
- if (!call) return;
726
- if (error) {
727
- call.reject(error);
728
- this.pendingRequests.delete(id);
729
- return;
684
+ if (this.initPromise) return this.initPromise;
685
+ this.initPromise = (async ()=>{
686
+ const handshake = new postmate.Model({
687
+ reply: ({ id, complete, data, error })=>{
688
+ const call = this.pendingRequests.get(id);
689
+ if (!call) return;
690
+ if (error) {
691
+ call.reject(error);
692
+ this.pendingRequests.delete(id);
693
+ return;
694
+ }
695
+ call.onData?.(data);
696
+ if (complete) {
697
+ call.onComplete?.();
698
+ call.resolve(data);
699
+ this.pendingRequests.delete(id);
700
+ }
730
701
  }
731
- call.onData?.(data);
732
- if (complete) {
733
- call.onComplete?.();
734
- call.resolve(data);
735
- this.pendingRequests.delete(id);
702
+ });
703
+ try {
704
+ const parent = await handshake;
705
+ this.ready = true;
706
+ this.parent = parent;
707
+ Log.i('mujian sdk client init');
708
+ await this.call(events_EVENT.MUJIAN_INIT);
709
+ const projectInfo = await this.ai.chat.project.getInfo();
710
+ if (projectInfo.config?.customCss) {
711
+ const style = document.createElement('style');
712
+ style.setAttribute('type', 'text/css');
713
+ style.setAttribute('id', 'mujian-custom-css');
714
+ style.textContent = projectInfo.config.customCss;
715
+ document.head.appendChild(style);
736
716
  }
717
+ if (projectInfo.config?.customJs) {
718
+ const script = document.createElement("script");
719
+ script.setAttribute('type', "text/javascript");
720
+ script.setAttribute('id', 'mujian-custom-js');
721
+ script.textContent = projectInfo.config.customJs;
722
+ document.head.appendChild(script);
723
+ }
724
+ const config = await getConfig.call(this);
725
+ if (config) this._config = config;
726
+ } catch (error) {
727
+ Log.e('init error', error);
737
728
  }
738
- });
739
- try {
740
- const parent = await handshake;
741
- this.ready = true;
742
- this.parent = parent;
743
- Log.i('mujian sdk client init');
744
- await this.call(events_EVENT.MUJIAN_INIT);
745
- const projectInfo = await this.ai.chat.project.getInfo();
746
- if (projectInfo.config?.customCss) {
747
- const style = document.createElement('style');
748
- style.setAttribute('type', 'text/css');
749
- style.setAttribute('id', 'mujian-custom-css');
750
- style.textContent = projectInfo.config.customCss;
751
- document.head.appendChild(style);
752
- }
753
- if (projectInfo.config?.customJs) {
754
- const script = document.createElement("script");
755
- script.setAttribute('type', "text/javascript");
756
- script.setAttribute('id', 'mujian-custom-js');
757
- script.textContent = projectInfo.config.customJs;
758
- document.head.appendChild(script);
759
- }
760
- } catch (error) {
761
- Log.e('init error', error);
762
- }
729
+ })();
730
+ return this.initPromise;
763
731
  }
764
732
  emit(event, data) {
765
733
  if (!this.ready) throw new Error('Mujian is not initialized');
@@ -797,27 +765,20 @@ class MujianSdk {
797
765
  getInfo: getInfo.bind(this)
798
766
  },
799
767
  settings: {
800
- model: {
801
- getAll: model_getAll.bind(this),
802
- getActive: model_getActive.bind(this),
803
- setActive: model_setActive.bind(this)
804
- },
805
768
  persona: {
806
- getActive: getActive.bind(this),
807
- setActive: setActive.bind(this)
769
+ getActive: getActive.bind(this)
808
770
  }
809
771
  },
810
772
  complete: chat_complete.bind(this),
811
- applyRegex: applyRegex.bind(this),
812
773
  renderMessage: renderMessage.bind(this),
813
774
  continue: continueComplete.bind(this),
814
775
  regenerate: chat_regenerate.bind(this),
776
+ stop: chat_stop.bind(this),
777
+ recover: recover.bind(this),
815
778
  message: {
816
- getAll: getAll.bind(this),
817
779
  deleteOne: messageDeleteOne.bind(this),
818
780
  editOne: messageEditOne.bind(this),
819
781
  swipe: messageSwipe.bind(this),
820
- getPrompt: getPrompt.bind(this),
821
782
  getPage: getPage.bind(this)
822
783
  }
823
784
  },
@@ -900,8 +861,10 @@ const MujianSpinner = ({ className, style })=>/*#__PURE__*/ jsxs(Fragment, {
900
861
  ]
901
862
  });
902
863
  const MujianContext = /*#__PURE__*/ createContext(null);
864
+ const ChatContext = /*#__PURE__*/ createContext(null);
903
865
  const MujianProvider = ({ children, loadingComponent })=>{
904
866
  const [mujian, setMujian] = useState(null);
867
+ const [messageTypeWritingCount, setMessageTypeWritingCount] = useState(0);
905
868
  useEffect(()=>{
906
869
  (async ()=>{
907
870
  const mujian = MujianSdk.getInstance();
@@ -911,18 +874,26 @@ const MujianProvider = ({ children, loadingComponent })=>{
911
874
  }, []);
912
875
  return /*#__PURE__*/ jsx(MujianContext.Provider, {
913
876
  value: mujian,
914
- children: mujian ? children : loadingComponent ?? /*#__PURE__*/ jsx("div", {
915
- style: {
916
- height: '100%',
917
- width: '100%',
918
- display: 'flex',
919
- justifyContent: 'center',
920
- alignItems: 'center'
877
+ children: /*#__PURE__*/ jsx(ChatContext.Provider, {
878
+ value: {
879
+ messageTypeWritingCount,
880
+ updateMessageTypeWritingCount (dir) {
881
+ setMessageTypeWritingCount((p)=>Math.max(p + ('add' === dir ? 1 : -1), 0));
882
+ }
921
883
  },
922
- children: /*#__PURE__*/ jsx(MujianSpinner, {
884
+ children: mujian ? children : loadingComponent ?? /*#__PURE__*/ jsx("div", {
923
885
  style: {
924
- color: '#EC4342'
925
- }
886
+ height: '100%',
887
+ width: '100%',
888
+ display: 'flex',
889
+ justifyContent: 'center',
890
+ alignItems: 'center'
891
+ },
892
+ children: /*#__PURE__*/ jsx(MujianSpinner, {
893
+ style: {
894
+ color: '#EC4342'
895
+ }
896
+ })
926
897
  })
927
898
  })
928
899
  });
@@ -936,12 +907,86 @@ const useMujian = ()=>{
936
907
  }
937
908
  return mujian;
938
909
  };
910
+ const useChatContext = ()=>{
911
+ const mujian = useContext(ChatContext);
912
+ if (!mujian) {
913
+ const message = 'Context is not initialized. Please check that useChatContext is called inside MujianProvider';
914
+ console.error(message);
915
+ throw new Error(message);
916
+ }
917
+ return mujian;
918
+ };
939
919
  const MessageItem = (props)=>{
940
- const { message, children, depth, index } = props;
920
+ const { message, children, depth, index, typingOpts = {
921
+ chunkSize: 2,
922
+ delay: 40
923
+ } } = props;
941
924
  const mujian = useMujian();
925
+ const { updateMessageTypeWritingCount } = useChatContext();
926
+ const lastMessage = useLatest(message);
927
+ const [typeWritingPos, setTypeWritingPos] = useState(null);
928
+ const typeWritingIntervalId = useRef(null);
929
+ function stopTypeWriting() {
930
+ if (typeWritingIntervalId.current) {
931
+ clearTimeout(typeWritingIntervalId.current);
932
+ typeWritingIntervalId.current = null;
933
+ setTimeout(()=>{
934
+ updateMessageTypeWritingCount('sub');
935
+ });
936
+ }
937
+ }
938
+ function startTypeWriting() {
939
+ updateMessageTypeWritingCount('add');
940
+ stopTypeWriting();
941
+ let lastTimestamp = Date.now();
942
+ typeWritingIntervalId.current = setInterval(()=>{
943
+ function calcTimeFrames() {
944
+ const now = Date.now();
945
+ const deltaTime = now - lastTimestamp;
946
+ lastTimestamp = now;
947
+ return Math.round(deltaTime / typingOpts.delay);
948
+ }
949
+ setTypeWritingPos((p)=>{
950
+ const frames = calcTimeFrames();
951
+ const posIncrement = frames * typingOpts.chunkSize;
952
+ const message = lastMessage.current;
953
+ const content = message.swipes[message.activeSwipeId].content;
954
+ const maxPos = content.length;
955
+ const nextPos = Math.min((p || 0) + posIncrement, maxPos);
956
+ if (!message.isGenerating && maxPos === nextPos) {
957
+ stopTypeWriting();
958
+ return null;
959
+ }
960
+ return nextPos;
961
+ });
962
+ }, typingOpts.delay);
963
+ }
964
+ useUnmount(()=>{
965
+ stopTypeWriting();
966
+ });
967
+ useEffect(()=>{
968
+ if (message.isGenerating) {
969
+ const content = message.swipes[message.activeSwipeId].content;
970
+ setTypeWritingPos(content.length);
971
+ startTypeWriting();
972
+ }
973
+ }, [
974
+ message.isGenerating
975
+ ]);
942
976
  const { data: renderedMessage } = useRequest(async ()=>{
943
- const { isStreaming } = message;
944
- if (isStreaming) return message;
977
+ if (null !== typeWritingPos) {
978
+ const pos = typeWritingPos;
979
+ return {
980
+ ...message,
981
+ swipes: message.swipes.map((s, i)=>{
982
+ if (i !== message.activeSwipeId) return s;
983
+ return {
984
+ ...s,
985
+ content: s.content.slice(0, pos)
986
+ };
987
+ })
988
+ };
989
+ }
945
990
  return await mujian.ai.chat.renderMessage({
946
991
  message,
947
992
  depth,
@@ -949,7 +994,8 @@ const MessageItem = (props)=>{
949
994
  });
950
995
  }, {
951
996
  refreshDeps: [
952
- message
997
+ message,
998
+ typeWritingPos
953
999
  ]
954
1000
  });
955
1001
  return children(renderedMessage || message, message);
@@ -971,28 +1017,116 @@ const Thread = {
971
1017
  MessageItem: MessageItem,
972
1018
  MessageList: MessageList
973
1019
  };
974
- class SendMessageError extends Error {
975
- constructor(message, cause){
976
- super(message);
977
- this.name = this.constructor.name;
978
- this.cause = cause;
1020
+ let $mj_ai_chat_complete;
1021
+ let $mj_ai_chat_project;
1022
+ let $mj_ai_chat_settings_persona;
1023
+ let $mj_ai_chat_setFirstMesIndex;
1024
+ let $mj_ai_chat_mjv_get;
1025
+ let $mj_ai_chat_mjv_getAll;
1026
+ const $mj_engine = {
1027
+ ai: {
1028
+ chat: {
1029
+ get complete () {
1030
+ return $mj_ai_chat_complete;
1031
+ },
1032
+ get project () {
1033
+ return $mj_ai_chat_project;
1034
+ },
1035
+ settings: {
1036
+ get persona () {
1037
+ return $mj_ai_chat_settings_persona;
1038
+ }
1039
+ },
1040
+ get setFirstMesIndex () {
1041
+ return $mj_ai_chat_setFirstMesIndex;
1042
+ }
1043
+ }
979
1044
  }
980
- }
981
- class InvalidDeltaContentError extends Error {
982
- constructor(message, cause){
983
- super(message);
984
- this.name = this.constructor.name;
985
- this.cause = cause;
1045
+ };
1046
+ window.$mj_engine = {
1047
+ ...$mj_engine,
1048
+ bind ({ messageId, swipeId }) {
1049
+ return {
1050
+ ai: {
1051
+ chat: {
1052
+ mjv: {
1053
+ get (path) {
1054
+ return $mj_ai_chat_mjv_get?.(messageId, swipeId, path);
1055
+ },
1056
+ getAll () {
1057
+ return $mj_ai_chat_mjv_getAll?.(messageId, swipeId) ?? {};
1058
+ }
1059
+ },
1060
+ get complete () {
1061
+ return $mj_ai_chat_complete;
1062
+ },
1063
+ get project () {
1064
+ return $mj_ai_chat_project;
1065
+ },
1066
+ settings: {
1067
+ get persona () {
1068
+ return $mj_ai_chat_settings_persona;
1069
+ }
1070
+ },
1071
+ get setFirstMesIndex () {
1072
+ return $mj_ai_chat_setFirstMesIndex;
1073
+ }
1074
+ }
1075
+ }
1076
+ };
986
1077
  }
1078
+ };
1079
+ function useMjEngine({ projectInfo, activePersona, onSend, messages, setSwipe }) {
1080
+ useEffect(()=>{
1081
+ $mj_ai_chat_project = projectInfo ?? void 0;
1082
+ $mj_ai_chat_settings_persona = activePersona ?? void 0;
1083
+ }, [
1084
+ projectInfo,
1085
+ activePersona
1086
+ ]);
1087
+ useEffect(()=>{
1088
+ $mj_ai_chat_complete = async (message)=>{
1089
+ await onSend(message);
1090
+ };
1091
+ $mj_ai_chat_setFirstMesIndex = async (oneBasedIndex)=>{
1092
+ if (messages.length > 1) throw new Error('已有新消息,不允许切换开场白');
1093
+ if (0 === messages.length) throw new Error('断言失败:没有开场消息,无法切换开场白');
1094
+ const zeroBasedIndex = Math.floor(oneBasedIndex) - 1;
1095
+ const swipeLength = messages[0].swipes.length;
1096
+ if (zeroBasedIndex < 0 || zeroBasedIndex >= swipeLength) return;
1097
+ setSwipe(messages[0].id, zeroBasedIndex);
1098
+ };
1099
+ $mj_ai_chat_mjv_getAll = (messageId, swipeId)=>messages.find((m)=>m.id === messageId)?.swipes[swipeId]?.mjv?.value;
1100
+ $mj_ai_chat_mjv_get = (messageId, swipeId, path)=>{
1101
+ const mjv = $mj_ai_chat_mjv_getAll?.(messageId, swipeId);
1102
+ if (!mjv) return;
1103
+ return get(mjv, path);
1104
+ };
1105
+ }, [
1106
+ onSend,
1107
+ messages,
1108
+ setSwipe
1109
+ ]);
1110
+ }
1111
+ const errorMessages = [
1112
+ 'network error',
1113
+ 'NetworkError when attempting to fetch resource.',
1114
+ 'The Internet connection appears to be offline.',
1115
+ 'Network request failed',
1116
+ 'Load failed',
1117
+ 'Failed to fetch'
1118
+ ];
1119
+ function isNetworkError(message) {
1120
+ return errorMessages.some((m)=>message.includes(m));
987
1121
  }
988
- class UnexpectedSseEndingError extends Error {
1122
+ class SendMessageError extends Error {
989
1123
  constructor(message, cause){
990
1124
  super(message);
991
1125
  this.name = this.constructor.name;
992
1126
  this.cause = cause;
993
1127
  }
994
1128
  }
995
- async function* callSdk(mujian, content, type = 'generate', signal) {
1129
+ async function* callSdk(mujian, content, type = 'generate', conversationVersion, assisMsgId) {
996
1130
  const queue = [];
997
1131
  let resolveWait = null;
998
1132
  let done = false;
@@ -1000,7 +1134,6 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
1000
1134
  try {
1001
1135
  const onData = (data)=>{
1002
1136
  if (error) return;
1003
- if ('string' != typeof data) return;
1004
1137
  queue.push(data);
1005
1138
  if (resolveWait) {
1006
1139
  resolveWait();
@@ -1021,9 +1154,13 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
1021
1154
  resolveWait = null;
1022
1155
  }
1023
1156
  };
1024
- if ('generate' === type) mujian.ai.chat.complete(content, onData, signal).then(then).catch(catcher);
1025
- else if ('regenerate' === type) mujian.ai.chat.regenerate(onData, signal).then(then).catch(catcher);
1026
- else mujian.ai.chat.continue(onData, signal).then(then).catch(catcher);
1157
+ if ('generate' === type) mujian.ai.chat.complete(content, conversationVersion, onData).then(then).catch(catcher);
1158
+ else if ('regenerate' === type) mujian.ai.chat.regenerate(conversationVersion, onData).then(then).catch(catcher);
1159
+ else if ('continue' === type) mujian.ai.chat.continue(conversationVersion, onData).then(then).catch(catcher);
1160
+ else {
1161
+ if (!assisMsgId) throw new Error('call recover without assisMsgId');
1162
+ mujian.ai.chat.recover(conversationVersion, assisMsgId, onData).then(then).catch(catcher);
1163
+ }
1027
1164
  } catch (err) {
1028
1165
  error = err;
1029
1166
  done = true;
@@ -1036,56 +1173,44 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
1036
1173
  });
1037
1174
  }
1038
1175
  const NOT_SAVED_MSG_ID_PREFIX = 'not_saved';
1039
- const FALLBACK_MESSAGE = `<!-- 此条HTML消息为系统提示,请勿复读 -->
1040
- 哎呀,AI线路好像抽风了,重说一下试试吧~`;
1041
- const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
1176
+ const useChatStreaming = ({ common, setError, setMessages, getMessages, setInput, mujian, conversationVersion: conversationVersionRef })=>{
1042
1177
  const { body } = common;
1043
1178
  const [isStreaming, setIsStreaming] = useState(false);
1044
- const chatStreaming = useCallback(async ({ query, regenerate, is_continue, signal })=>{
1179
+ const chatStreaming = useCallback(async ({ query, regenerate, is_continue, conversationVersion })=>{
1045
1180
  try {
1046
1181
  setError(void 0);
1047
1182
  setIsStreaming(true);
1183
+ const backupMessages = getMessages();
1048
1184
  const userMessage = {
1049
1185
  id: `${NOT_SAVED_MSG_ID_PREFIX}_user_${Date.now()}`,
1050
- content: query || '',
1051
- role: 'user',
1052
- swipes: [],
1186
+ isUser: true,
1187
+ swipes: [
1188
+ {
1189
+ content: query ?? ''
1190
+ }
1191
+ ],
1053
1192
  activeSwipeId: 0,
1054
- isStreaming: false,
1055
- sendAt: new Date(),
1056
- swipeInfo: []
1193
+ isGenerating: false
1057
1194
  };
1058
1195
  const aiMessage = {
1059
1196
  id: `${NOT_SAVED_MSG_ID_PREFIX}_assistant_${Date.now()}`,
1060
- content: '',
1061
- role: 'assistant',
1197
+ isUser: false,
1062
1198
  swipes: [
1063
- ''
1199
+ {
1200
+ content: ''
1201
+ }
1064
1202
  ],
1065
1203
  activeSwipeId: 0,
1066
- isStreaming: true,
1067
- sendAt: new Date(),
1068
- swipeInfo: [
1069
- {}
1070
- ]
1204
+ isGenerating: true
1071
1205
  };
1072
- if (regenerate) setMessages((prev)=>{
1073
- const newMessages = [
1074
- ...prev
1075
- ];
1076
- const lastMessage = newMessages[newMessages.length - 1];
1077
- newMessages[newMessages.length - 1] = {
1078
- ...lastMessage,
1079
- activeSwipeId: lastMessage.swipes.length,
1080
- swipes: [
1081
- ...lastMessage.swipes,
1082
- ''
1083
- ],
1084
- content: '',
1085
- isStreaming: true
1086
- };
1087
- return newMessages;
1088
- });
1206
+ if (regenerate) setMessages(produce((draft)=>{
1207
+ const lastMessage = draft[draft.length - 1];
1208
+ lastMessage.activeSwipeId = lastMessage.swipes.length;
1209
+ lastMessage.swipes.push({
1210
+ content: ''
1211
+ });
1212
+ lastMessage.isGenerating = true;
1213
+ }));
1089
1214
  else is_continue ? setMessages((prev)=>[
1090
1215
  ...prev,
1091
1216
  aiMessage
@@ -1094,111 +1219,45 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
1094
1219
  userMessage,
1095
1220
  aiMessage
1096
1221
  ]);
1097
- const stream = callSdk(mujian, query || '', is_continue ? 'continue' : regenerate ? 'regenerate' : 'generate', signal);
1098
- let buffer = '';
1099
- let content = '';
1222
+ const stream = callSdk(mujian, query || '', is_continue ? 'continue' : regenerate ? 'regenerate' : 'generate', conversationVersion);
1100
1223
  try {
1101
1224
  for await (const chunk of stream){
1102
- buffer += chunk;
1103
- const lines = buffer.split('\n');
1104
- buffer = lines.pop() || '';
1105
- for (const line of lines)if (line.startsWith('data: ')) {
1106
- const data = line.slice(6);
1107
- try {
1108
- const parsedData = JSON.parse(data);
1109
- if (regenerate || 'meta' !== parsedData.type) {
1110
- if ('stream' === parsedData.type) {
1111
- const partialContent = parsedData.choices[0].delta.content;
1112
- parsedData.choices[0].delta.reasoning;
1113
- content += partialContent;
1114
- if (content || '' === content) {
1115
- setMessages((prev)=>{
1116
- const newMessages = [
1117
- ...prev
1118
- ];
1119
- const lastMessage = newMessages[newMessages.length - 1];
1120
- lastMessage.swipes[lastMessage.activeSwipeId] = content;
1121
- newMessages[newMessages.length - 1] = {
1122
- ...lastMessage,
1123
- content: content,
1124
- isStreaming: true
1125
- };
1126
- return newMessages;
1127
- });
1128
- await new Promise((resolve)=>setTimeout(resolve, 10));
1129
- } else {
1130
- console.error('data', data);
1131
- setError(new InvalidDeltaContentError(data));
1132
- }
1133
- } else if ('mjv' === parsedData.type) {
1134
- const { value, delta, schema } = parsedData;
1135
- setMessages((prev)=>{
1136
- const newMessages = [
1137
- ...prev
1138
- ];
1139
- const lastMessage = newMessages[newMessages.length - 1];
1140
- const mjv = {
1141
- value,
1142
- delta,
1143
- schema
1144
- };
1145
- const newSwipeInfo = lastMessage.swipeInfo;
1146
- if (newSwipeInfo[lastMessage.activeSwipeId]) newSwipeInfo[lastMessage.activeSwipeId].mjv = mjv;
1147
- else newSwipeInfo[lastMessage.activeSwipeId] = {
1148
- mjv
1149
- };
1150
- newMessages[newMessages.length - 1] = {
1151
- ...lastMessage,
1152
- swipeInfo: newSwipeInfo,
1153
- mjv
1154
- };
1155
- return newMessages;
1156
- });
1157
- }
1158
- } else {
1159
- const { question_id, reply_id } = parsedData;
1160
- setMessages((prev)=>{
1161
- const newMessages = [
1162
- ...prev
1163
- ];
1164
- if (question_id && !is_continue) newMessages[newMessages.length - 2].id = question_id;
1165
- const lastMessage = newMessages[newMessages.length - 1];
1166
- newMessages[newMessages.length - 1] = {
1167
- ...lastMessage,
1168
- id: reply_id
1169
- };
1170
- return newMessages;
1171
- });
1172
- continue;
1225
+ if (!chunk) return;
1226
+ if ('meta' === chunk.type) {
1227
+ if (!regenerate) setMessages(produce((draft)=>{
1228
+ if (!is_continue) {
1229
+ const userMsg = draft[draft.length - 2];
1230
+ userMsg.id = chunk.userMsgId;
1173
1231
  }
1174
- } catch {
1175
- if ('[DONE]' !== data) {
1176
- console.error('Received plain SSE message:', data);
1177
- setError(new UnexpectedSseEndingError(data));
1178
- }
1179
- }
1232
+ const lastMessage = draft[draft.length - 1];
1233
+ lastMessage.id = chunk.assisMsgId;
1234
+ }));
1235
+ conversationVersionRef.current = chunk.newConversationVersion;
1236
+ } else if ('data' === chunk.type) {
1237
+ setMessages(produce((draft)=>{
1238
+ const lastMessage = draft[draft.length - 1];
1239
+ lastMessage.swipes[lastMessage.activeSwipeId] = {
1240
+ content: chunk.content,
1241
+ mjv: chunk.mjv
1242
+ };
1243
+ if (chunk.finishReason) lastMessage.finishReason = chunk.finishReason;
1244
+ if (chunk.error) lastMessage.error = chunk.error;
1245
+ }));
1246
+ await new Promise((resolve)=>setTimeout(resolve, 10));
1180
1247
  }
1181
1248
  }
1182
1249
  } catch (error) {
1183
1250
  Log.e('Stream error', error);
1184
- 'object' == typeof error && error && 'message' in error && 'string' == typeof error.message ? setError(new SendMessageError(error.message)) : setError(error);
1251
+ if ('object' == typeof error && error && 'message' in error && 'string' == typeof error.message) if ('code' in error && 2011030001 === error.code) {
1252
+ setMessages(backupMessages);
1253
+ if (!regenerate && !is_continue && query) setInput(query);
1254
+ } else isNetworkError(error.message) ? setError(new SendMessageError('E_NETWORK_ERROR')) : setError(new SendMessageError(error.message));
1255
+ else setError(error);
1185
1256
  } finally{
1186
- Log.i('stream end finally');
1187
- setMessages((prev)=>{
1188
- const newMessages = [
1189
- ...prev
1190
- ];
1191
- const lastMessage = {
1192
- ...newMessages[newMessages.length - 1],
1193
- isStreaming: false
1194
- };
1195
- if (!lastMessage.swipes[lastMessage.activeSwipeId]) {
1196
- lastMessage.swipes[lastMessage.activeSwipeId] = FALLBACK_MESSAGE;
1197
- lastMessage.content = FALLBACK_MESSAGE;
1198
- }
1199
- newMessages[newMessages.length - 1] = lastMessage;
1200
- return newMessages;
1201
- });
1257
+ setMessages(produce((draft)=>{
1258
+ const lastMessage = draft[draft.length - 1];
1259
+ lastMessage.isGenerating = false;
1260
+ }));
1202
1261
  }
1203
1262
  } catch (error) {
1204
1263
  console.error('Error:', error);
@@ -1211,15 +1270,48 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
1211
1270
  body,
1212
1271
  mujian
1213
1272
  ]);
1273
+ const recoverStreaming = useCallback(async (messageId, conversationVersion)=>{
1274
+ const stream = callSdk(mujian, '', 'recover', conversationVersion, messageId);
1275
+ try {
1276
+ for await (const chunk of stream){
1277
+ if (!chunk) return;
1278
+ if ('data' === chunk.type) setMessages(produce((draft)=>{
1279
+ const msg = draft.find((m)=>m.id === messageId);
1280
+ if (!msg) return;
1281
+ msg.swipes[msg.activeSwipeId] = {
1282
+ content: chunk.content,
1283
+ mjv: chunk.mjv
1284
+ };
1285
+ if (chunk.finishReason) msg.finishReason = chunk.finishReason;
1286
+ if (chunk.error) msg.error = chunk.error;
1287
+ }));
1288
+ }
1289
+ } catch (error) {
1290
+ Log.e('Stream error', error);
1291
+ if ('object' == typeof error && error && 'message' in error && 'string' == typeof error.message) isNetworkError(error.message) ? setError(new SendMessageError('E_NETWORK_ERROR')) : setError(new SendMessageError(error.message));
1292
+ else setError(error);
1293
+ } finally{
1294
+ console.log('finally');
1295
+ setMessages(produce((draft)=>{
1296
+ const msg = draft.find((m)=>m.id === messageId);
1297
+ console.log('finally', msg);
1298
+ if (!msg) return;
1299
+ msg.isGenerating = false;
1300
+ }));
1301
+ }
1302
+ }, [
1303
+ mujian
1304
+ ]);
1214
1305
  return {
1215
1306
  chatStreaming,
1216
- isStreaming
1307
+ isStreaming,
1308
+ recoverStreaming
1217
1309
  };
1218
1310
  };
1219
- const useChat = ({ body, onError, onFinish, pageSize })=>{
1311
+ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1220
1312
  const [error, _setError] = useState();
1221
1313
  const [input, setInput] = useState('');
1222
- const [abortController, setAbortController] = useState();
1314
+ const conversationVersion = useRef(0);
1223
1315
  const mujian = useMujian();
1224
1316
  const setError = (error)=>{
1225
1317
  _setError(error);
@@ -1234,12 +1326,13 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1234
1326
  };
1235
1327
  try {
1236
1328
  const resp = await mujian.ai.chat.message.getPage(current?.cursor, pageSize);
1329
+ conversationVersion.current = resp.conversationVersion;
1330
+ const lastMessage = resp.messages.at(-1);
1331
+ console.log(lastMessage);
1332
+ if (lastMessage?.isGenerating) recoverStreaming(lastMessage.id, conversationVersion.current);
1237
1333
  return {
1238
1334
  cursor: resp.nextCursor,
1239
- list: resp.messages.map((m)=>({
1240
- ...m,
1241
- mjv: m.swipeInfo[m.activeSwipeId]?.mjv
1242
- })),
1335
+ list: resp.messages,
1243
1336
  noMore: !resp.nextCursor
1244
1337
  };
1245
1338
  } finally{
@@ -1270,13 +1363,19 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1270
1363
  list: newList
1271
1364
  });
1272
1365
  };
1273
- const { chatStreaming, isStreaming } = useChatStreaming({
1366
+ const getMessages = useCallback(()=>latestListRef.current, [
1367
+ latestListRef
1368
+ ]);
1369
+ const { chatStreaming, isStreaming, recoverStreaming } = useChatStreaming({
1274
1370
  common: {
1275
1371
  body
1276
1372
  },
1277
1373
  setError,
1278
1374
  setMessages,
1279
- mujian
1375
+ getMessages,
1376
+ setInput,
1377
+ mujian,
1378
+ conversationVersion
1280
1379
  });
1281
1380
  useUpdateEffect(()=>{
1282
1381
  if (!isStreaming) {
@@ -1288,18 +1387,14 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1288
1387
  ]);
1289
1388
  const append = useCallback(async (query)=>{
1290
1389
  if (isStreaming) throw new Error('useChat is streaming already.');
1291
- const controller = new AbortController();
1292
- setAbortController(controller);
1293
1390
  try {
1294
1391
  await chatStreaming({
1295
1392
  query,
1296
- signal: controller.signal
1393
+ conversationVersion: conversationVersion.current
1297
1394
  });
1298
1395
  } catch (e) {
1299
1396
  setError(e);
1300
1397
  throw e;
1301
- } finally{
1302
- setAbortController(void 0);
1303
1398
  }
1304
1399
  }, [
1305
1400
  isStreaming,
@@ -1307,20 +1402,17 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1307
1402
  ]);
1308
1403
  const regenerate = useCallback(async ()=>{
1309
1404
  if (isStreaming) throw new Error('useChat is streaming already.');
1310
- const controller = new AbortController();
1311
- setAbortController(controller);
1312
- const lastMessage = messages.findLast((message)=>'user' === message.role);
1405
+ const lastMessage = messages.findLast((message)=>message.isUser);
1313
1406
  try {
1407
+ const query = lastMessage ? lastMessage.swipes[lastMessage.activeSwipeId].content : '';
1314
1408
  await chatStreaming({
1315
- query: lastMessage?.content || '',
1409
+ query,
1316
1410
  regenerate: true,
1317
- signal: controller.signal
1411
+ conversationVersion: conversationVersion.current
1318
1412
  });
1319
1413
  } catch (e) {
1320
1414
  setError(e);
1321
1415
  throw e;
1322
- } finally{
1323
- setAbortController(void 0);
1324
1416
  }
1325
1417
  }, [
1326
1418
  isStreaming,
@@ -1329,49 +1421,55 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1329
1421
  ]);
1330
1422
  const continueGenerate = useCallback(async ()=>{
1331
1423
  if (isStreaming) throw new Error('useChat is streaming already.');
1332
- const controller = new AbortController();
1333
- setAbortController(controller);
1334
1424
  try {
1335
1425
  await chatStreaming({
1336
1426
  query: '',
1337
1427
  is_continue: true,
1338
- signal: controller.signal
1428
+ conversationVersion: conversationVersion.current
1339
1429
  });
1340
1430
  } catch (e) {
1341
1431
  setError(e);
1342
1432
  throw e;
1343
- } finally{
1344
- setAbortController(void 0);
1345
1433
  }
1346
1434
  }, [
1347
1435
  isStreaming,
1348
1436
  chatStreaming
1349
1437
  ]);
1350
1438
  const stop = ()=>{
1351
- abortController?.abort();
1439
+ const assisMsgId = messages.at(-1)?.id;
1440
+ if (!assisMsgId) return;
1441
+ mujian.ai.chat.stop(conversationVersion.current, assisMsgId);
1352
1442
  };
1353
1443
  const setSwipe = async (messageId, swipeId)=>{
1354
1444
  if (swipeId < 0) return;
1355
1445
  setMessages((prev)=>prev.map((msg)=>msg.id === messageId && swipeId < msg.swipes.length ? {
1356
1446
  ...msg,
1357
- activeSwipeId: swipeId,
1358
- content: msg.swipes[swipeId],
1359
- mjv: msg.swipeInfo[swipeId]?.mjv
1447
+ activeSwipeId: swipeId
1360
1448
  } : msg));
1361
- await mujian.ai.chat.message.swipe(messageId, swipeId);
1449
+ const resp = await mujian.ai.chat.message.swipe(messageId, swipeId);
1450
+ const schema = z.object({
1451
+ newConversationVersion: z.number()
1452
+ });
1453
+ const { data, success } = schema.safeParse(resp);
1454
+ if (success && data.newConversationVersion) conversationVersion.current = data.newConversationVersion;
1362
1455
  };
1363
1456
  const editMessage = async (messageId, content)=>{
1364
- await mujian.ai.chat.message.editOne(messageId, content);
1457
+ const resp = await mujian.ai.chat.message.editOne(messageId, content);
1458
+ const schema = z.object({
1459
+ newConversationVersion: z.number()
1460
+ });
1461
+ const { data, success } = schema.safeParse(resp);
1462
+ if (success && data.newConversationVersion) conversationVersion.current = data.newConversationVersion;
1365
1463
  const patchMessage = (msg)=>{
1366
1464
  const newMsg = {
1367
1465
  ...msg,
1368
1466
  content
1369
1467
  };
1370
- if ('assistant' === newMsg.role) {
1468
+ if (!newMsg.isUser) {
1371
1469
  newMsg.swipes = [
1372
1470
  ...newMsg.swipes
1373
1471
  ];
1374
- newMsg.swipes[newMsg.activeSwipeId] = content;
1472
+ newMsg.swipes[newMsg.activeSwipeId].content = content;
1375
1473
  }
1376
1474
  return newMsg;
1377
1475
  };
@@ -1379,7 +1477,12 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1379
1477
  };
1380
1478
  const deleteMessage = async (messageId)=>{
1381
1479
  if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX)) try {
1382
- await mujian.ai.chat.message.deleteOne(messageId);
1480
+ const resp = await mujian.ai.chat.message.deleteOne(messageId);
1481
+ const schema = z.object({
1482
+ newConversationVersion: z.number()
1483
+ });
1484
+ const { data, success } = schema.safeParse(resp);
1485
+ if (success && data.newConversationVersion) conversationVersion.current = data.newConversationVersion;
1383
1486
  } catch (e) {
1384
1487
  if (e?.code !== 2011030004) throw e;
1385
1488
  }
@@ -1410,4 +1513,4 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1410
1513
  messagesStatus: messageLoadError ? 'error' : loadingMore || loading ? 'loading' : data?.noMore ? 'all-loaded' : 'has-more'
1411
1514
  };
1412
1515
  };
1413
- export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useMujian };
1516
+ export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useChatContext, useMjEngine, useMujian };