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

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 +443 -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,46 @@ 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
+ setMessages(produce((draft)=>{
1295
+ const msg = draft.find((m)=>m.id === messageId);
1296
+ if (!msg) return;
1297
+ msg.isGenerating = false;
1298
+ }));
1299
+ }
1300
+ }, [
1301
+ mujian
1302
+ ]);
1214
1303
  return {
1215
1304
  chatStreaming,
1216
- isStreaming
1305
+ isStreaming,
1306
+ recoverStreaming
1217
1307
  };
1218
1308
  };
1219
- const useChat = ({ body, onError, onFinish, pageSize })=>{
1309
+ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1220
1310
  const [error, _setError] = useState();
1221
1311
  const [input, setInput] = useState('');
1222
- const [abortController, setAbortController] = useState();
1312
+ const conversationVersion = useRef(0);
1223
1313
  const mujian = useMujian();
1224
1314
  const setError = (error)=>{
1225
1315
  _setError(error);
@@ -1234,12 +1324,12 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1234
1324
  };
1235
1325
  try {
1236
1326
  const resp = await mujian.ai.chat.message.getPage(current?.cursor, pageSize);
1327
+ conversationVersion.current = resp.conversationVersion;
1328
+ const lastMessage = resp.messages.at(-1);
1329
+ if (lastMessage?.isGenerating) recoverStreaming(lastMessage.id, conversationVersion.current);
1237
1330
  return {
1238
1331
  cursor: resp.nextCursor,
1239
- list: resp.messages.map((m)=>({
1240
- ...m,
1241
- mjv: m.swipeInfo[m.activeSwipeId]?.mjv
1242
- })),
1332
+ list: resp.messages,
1243
1333
  noMore: !resp.nextCursor
1244
1334
  };
1245
1335
  } finally{
@@ -1270,13 +1360,19 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1270
1360
  list: newList
1271
1361
  });
1272
1362
  };
1273
- const { chatStreaming, isStreaming } = useChatStreaming({
1363
+ const getMessages = useCallback(()=>latestListRef.current, [
1364
+ latestListRef
1365
+ ]);
1366
+ const { chatStreaming, isStreaming, recoverStreaming } = useChatStreaming({
1274
1367
  common: {
1275
1368
  body
1276
1369
  },
1277
1370
  setError,
1278
1371
  setMessages,
1279
- mujian
1372
+ getMessages,
1373
+ setInput,
1374
+ mujian,
1375
+ conversationVersion
1280
1376
  });
1281
1377
  useUpdateEffect(()=>{
1282
1378
  if (!isStreaming) {
@@ -1288,18 +1384,14 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1288
1384
  ]);
1289
1385
  const append = useCallback(async (query)=>{
1290
1386
  if (isStreaming) throw new Error('useChat is streaming already.');
1291
- const controller = new AbortController();
1292
- setAbortController(controller);
1293
1387
  try {
1294
1388
  await chatStreaming({
1295
1389
  query,
1296
- signal: controller.signal
1390
+ conversationVersion: conversationVersion.current
1297
1391
  });
1298
1392
  } catch (e) {
1299
1393
  setError(e);
1300
1394
  throw e;
1301
- } finally{
1302
- setAbortController(void 0);
1303
1395
  }
1304
1396
  }, [
1305
1397
  isStreaming,
@@ -1307,20 +1399,17 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1307
1399
  ]);
1308
1400
  const regenerate = useCallback(async ()=>{
1309
1401
  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);
1402
+ const lastMessage = messages.findLast((message)=>message.isUser);
1313
1403
  try {
1404
+ const query = lastMessage ? lastMessage.swipes[lastMessage.activeSwipeId].content : '';
1314
1405
  await chatStreaming({
1315
- query: lastMessage?.content || '',
1406
+ query,
1316
1407
  regenerate: true,
1317
- signal: controller.signal
1408
+ conversationVersion: conversationVersion.current
1318
1409
  });
1319
1410
  } catch (e) {
1320
1411
  setError(e);
1321
1412
  throw e;
1322
- } finally{
1323
- setAbortController(void 0);
1324
1413
  }
1325
1414
  }, [
1326
1415
  isStreaming,
@@ -1329,49 +1418,55 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1329
1418
  ]);
1330
1419
  const continueGenerate = useCallback(async ()=>{
1331
1420
  if (isStreaming) throw new Error('useChat is streaming already.');
1332
- const controller = new AbortController();
1333
- setAbortController(controller);
1334
1421
  try {
1335
1422
  await chatStreaming({
1336
1423
  query: '',
1337
1424
  is_continue: true,
1338
- signal: controller.signal
1425
+ conversationVersion: conversationVersion.current
1339
1426
  });
1340
1427
  } catch (e) {
1341
1428
  setError(e);
1342
1429
  throw e;
1343
- } finally{
1344
- setAbortController(void 0);
1345
1430
  }
1346
1431
  }, [
1347
1432
  isStreaming,
1348
1433
  chatStreaming
1349
1434
  ]);
1350
1435
  const stop = ()=>{
1351
- abortController?.abort();
1436
+ const assisMsgId = messages.at(-1)?.id;
1437
+ if (!assisMsgId) return;
1438
+ mujian.ai.chat.stop(conversationVersion.current, assisMsgId);
1352
1439
  };
1353
1440
  const setSwipe = async (messageId, swipeId)=>{
1354
1441
  if (swipeId < 0) return;
1355
1442
  setMessages((prev)=>prev.map((msg)=>msg.id === messageId && swipeId < msg.swipes.length ? {
1356
1443
  ...msg,
1357
- activeSwipeId: swipeId,
1358
- content: msg.swipes[swipeId],
1359
- mjv: msg.swipeInfo[swipeId]?.mjv
1444
+ activeSwipeId: swipeId
1360
1445
  } : msg));
1361
- await mujian.ai.chat.message.swipe(messageId, swipeId);
1446
+ const resp = await mujian.ai.chat.message.swipe(messageId, swipeId);
1447
+ const schema = z.object({
1448
+ newConversationVersion: z.number()
1449
+ });
1450
+ const { data, success } = schema.safeParse(resp);
1451
+ if (success && data.newConversationVersion) conversationVersion.current = data.newConversationVersion;
1362
1452
  };
1363
1453
  const editMessage = async (messageId, content)=>{
1364
- await mujian.ai.chat.message.editOne(messageId, content);
1454
+ const resp = await mujian.ai.chat.message.editOne(messageId, content);
1455
+ const schema = z.object({
1456
+ newConversationVersion: z.number()
1457
+ });
1458
+ const { data, success } = schema.safeParse(resp);
1459
+ if (success && data.newConversationVersion) conversationVersion.current = data.newConversationVersion;
1365
1460
  const patchMessage = (msg)=>{
1366
1461
  const newMsg = {
1367
1462
  ...msg,
1368
1463
  content
1369
1464
  };
1370
- if ('assistant' === newMsg.role) {
1465
+ if (!newMsg.isUser) {
1371
1466
  newMsg.swipes = [
1372
1467
  ...newMsg.swipes
1373
1468
  ];
1374
- newMsg.swipes[newMsg.activeSwipeId] = content;
1469
+ newMsg.swipes[newMsg.activeSwipeId].content = content;
1375
1470
  }
1376
1471
  return newMsg;
1377
1472
  };
@@ -1379,7 +1474,12 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1379
1474
  };
1380
1475
  const deleteMessage = async (messageId)=>{
1381
1476
  if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX)) try {
1382
- await mujian.ai.chat.message.deleteOne(messageId);
1477
+ const resp = await mujian.ai.chat.message.deleteOne(messageId);
1478
+ const schema = z.object({
1479
+ newConversationVersion: z.number()
1480
+ });
1481
+ const { data, success } = schema.safeParse(resp);
1482
+ if (success && data.newConversationVersion) conversationVersion.current = data.newConversationVersion;
1383
1483
  } catch (e) {
1384
1484
  if (e?.code !== 2011030004) throw e;
1385
1485
  }
@@ -1410,4 +1510,4 @@ const useChat = ({ body, onError, onFinish, pageSize })=>{
1410
1510
  messagesStatus: messageLoadError ? 'error' : loadingMore || loading ? 'loading' : data?.noMore ? 'all-loaded' : 'has-more'
1411
1511
  };
1412
1512
  };
1413
- export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useMujian };
1513
+ export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useChatContext, useMjEngine, useMujian };