@mujian/js-sdk 0.1.0-beta.1 → 0.1.0-beta.3

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.
@@ -4,13 +4,11 @@ export type { Message };
4
4
  /**
5
5
  * Use chat
6
6
  * @param {object} body - The body of the chat.
7
- * @param {function} onError - The function to call when an error occurs.
8
7
  * @param {function} onFinish - The function to call when the chat is finished.
9
8
  * @returns {UseChatReturn} The chat.
10
9
  */
11
10
  export type UseChatProps = {
12
11
  body?: object;
13
- onError?: (e: unknown) => void;
14
12
  onFinish?: (message: Message) => void;
15
13
  /** 消息分页大小,不传时不分页 */
16
14
  pageSize?: number;
@@ -37,7 +35,6 @@ export type UseChatProps = {
37
35
  export type UseChatReturn = {
38
36
  messages: Message[];
39
37
  status: 'uninitialized' | 'ready' | 'streaming' | 'error';
40
- error?: unknown;
41
38
  /** 提交用户输入,让AI回答 */
42
39
  append: (query: string) => Promise<void>;
43
40
  /** 重新生成最后一条回答 */
@@ -54,6 +51,7 @@ export type UseChatReturn = {
54
51
  deleteMessage: (messageId: string) => Promise<void>;
55
52
  loadMoreMessage: () => Promise<void>;
56
53
  messagesStatus: 'all-loaded' | 'loading' | 'has-more' | 'error';
54
+ refresh: () => Promise<void>;
57
55
  };
58
56
  /**
59
57
  * Use chat common
@@ -67,7 +65,6 @@ export type UseChatCommon = {
67
65
  * Use chat
68
66
  * @param {UseChatProps} props - The props of the chat.
69
67
  * @param {object} props.body - The body of the chat.
70
- * @param {function} props.onError - The function to call when an error occurs.
71
68
  * @param {function} props.onFinish - The function to call when the chat is finished.
72
69
  * @param {number} props.pageSize - The page size of the chat.
73
70
  * @returns {UseChatReturn} The chat.
@@ -78,4 +75,4 @@ export type UseChatCommon = {
78
75
  * },
79
76
  * });
80
77
  */
81
- export declare const useChat: ({ body, onError, onFinish, pageSize, }: UseChatProps) => UseChatReturn;
78
+ export declare const useChat: ({ body, onFinish, pageSize, }: UseChatProps) => UseChatReturn;
@@ -4,7 +4,6 @@ import type { UseChatCommon } from '..';
4
4
  import type { Message } from '../message';
5
5
  interface ChatStreamingProps {
6
6
  common: UseChatCommon;
7
- setError: (error: unknown) => void;
8
7
  setMessages: Dispatch<SetStateAction<Message[]>>;
9
8
  getMessages: () => Message[];
10
9
  setInput: (input: string) => void;
@@ -12,7 +11,7 @@ interface ChatStreamingProps {
12
11
  conversationVersion: RefObject<number>;
13
12
  }
14
13
  export declare const NOT_SAVED_MSG_ID_PREFIX = "not_saved";
15
- export declare const useChatStreaming: ({ common, setError, setMessages, getMessages, setInput, mujian, conversationVersion: conversationVersionRef, }: ChatStreamingProps) => {
14
+ export declare const useChatStreaming: ({ common, setMessages, getMessages, setInput, mujian, conversationVersion: conversationVersionRef, }: ChatStreamingProps) => {
16
15
  chatStreaming: ({ query, regenerate, is_continue, conversationVersion, }: {
17
16
  /** 用户输入 */
18
17
  query?: string;
@@ -3,6 +3,7 @@ import { Message as MessageType } from '../../chat';
3
3
  type TypingOpts = {
4
4
  chunkSize: number;
5
5
  delay: number;
6
+ maxLatency: number;
6
7
  };
7
8
  export interface MessageItemProps {
8
9
  /**
package/dist/react.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { useInfiniteScroll, useLatest, useRequest, useUnmount, useUpdateEffect } from "ahooks";
2
2
  import react, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react";
3
+ import { z } from "zod";
3
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
5
  import css_tools from "@adobe/css-tools";
5
6
  import dompurify from "dompurify";
@@ -9,7 +10,6 @@ import postmate from "postmate";
9
10
  import { Virtualizer } from "virtua";
10
11
  import { get } from "lodash-es";
11
12
  import { produce } from "immer";
12
- import { z } from "zod";
13
13
  const adjustIframeHeight = (iframeId)=>`
14
14
  (function () {
15
15
  let scheduled = false;
@@ -919,7 +919,8 @@ const useChatContext = ()=>{
919
919
  const MessageItem = (props)=>{
920
920
  const { message, children, depth, index, typingOpts = {
921
921
  chunkSize: 2,
922
- delay: 40
922
+ delay: 40,
923
+ maxLatency: 100
923
924
  } } = props;
924
925
  const mujian = useMujian();
925
926
  const { updateMessageTypeWritingCount } = useChatContext();
@@ -957,6 +958,7 @@ const MessageItem = (props)=>{
957
958
  stopTypeWriting();
958
959
  return null;
959
960
  }
961
+ if (maxPos - nextPos > typingOpts.maxLatency) return maxPos;
960
962
  return nextPos;
961
963
  });
962
964
  }, typingOpts.delay);
@@ -1119,13 +1121,6 @@ const errorMessages = [
1119
1121
  function isNetworkError(message) {
1120
1122
  return errorMessages.some((m)=>message.includes(m));
1121
1123
  }
1122
- class SendMessageError extends Error {
1123
- constructor(message, cause){
1124
- super(message);
1125
- this.name = this.constructor.name;
1126
- this.cause = cause;
1127
- }
1128
- }
1129
1124
  async function* callSdk(mujian, content, type = 'generate', conversationVersion, assisMsgId) {
1130
1125
  const queue = [];
1131
1126
  let resolveWait = null;
@@ -1172,15 +1167,21 @@ async function* callSdk(mujian, content, type = 'generate', conversationVersion,
1172
1167
  resolveWait = resolve;
1173
1168
  });
1174
1169
  }
1170
+ const errorSchema = z.object({
1171
+ message: z.string()
1172
+ });
1173
+ function isError(e) {
1174
+ const { success } = errorSchema.safeParse(e);
1175
+ return success;
1176
+ }
1175
1177
  const NOT_SAVED_MSG_ID_PREFIX = 'not_saved';
1176
- const useChatStreaming = ({ common, setError, setMessages, getMessages, setInput, mujian, conversationVersion: conversationVersionRef })=>{
1178
+ const useChatStreaming = ({ common, setMessages, getMessages, setInput, mujian, conversationVersion: conversationVersionRef })=>{
1177
1179
  const { body } = common;
1178
1180
  const [isStreaming, setIsStreaming] = useState(false);
1179
1181
  const chatStreaming = useCallback(async ({ query, regenerate, is_continue, conversationVersion })=>{
1182
+ const backupMessages = getMessages();
1180
1183
  try {
1181
- setError(void 0);
1182
1184
  setIsStreaming(true);
1183
- const backupMessages = getMessages();
1184
1185
  const userMessage = {
1185
1186
  id: `${NOT_SAVED_MSG_ID_PREFIX}_user_${Date.now()}`,
1186
1187
  isUser: true,
@@ -1204,12 +1205,14 @@ const useChatStreaming = ({ common, setError, setMessages, getMessages, setInput
1204
1205
  isGenerating: true
1205
1206
  };
1206
1207
  if (regenerate) setMessages(produce((draft)=>{
1207
- const lastMessage = draft[draft.length - 1];
1208
+ const lastMessage = draft.at(-1);
1208
1209
  lastMessage.activeSwipeId = lastMessage.swipes.length;
1209
1210
  lastMessage.swipes.push({
1210
1211
  content: ''
1211
1212
  });
1212
1213
  lastMessage.isGenerating = true;
1214
+ lastMessage.finishReason = void 0;
1215
+ lastMessage.error = void 0;
1213
1216
  }));
1214
1217
  else is_continue ? setMessages((prev)=>[
1215
1218
  ...prev,
@@ -1220,50 +1223,62 @@ const useChatStreaming = ({ common, setError, setMessages, getMessages, setInput
1220
1223
  aiMessage
1221
1224
  ]);
1222
1225
  const stream = callSdk(mujian, query || '', is_continue ? 'continue' : regenerate ? 'regenerate' : 'generate', conversationVersion);
1223
- try {
1224
- for await (const chunk of stream){
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;
1231
- }
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));
1247
- }
1226
+ for await (const chunk of stream){
1227
+ if (!chunk) return;
1228
+ if ('meta' === chunk.type) {
1229
+ if (!regenerate) setMessages(produce((draft)=>{
1230
+ if (!is_continue) {
1231
+ const userMsg = draft[draft.length - 2];
1232
+ userMsg.id = chunk.userMsgId;
1233
+ }
1234
+ const lastMessage = draft[draft.length - 1];
1235
+ lastMessage.id = chunk.assisMsgId;
1236
+ }));
1237
+ conversationVersionRef.current = chunk.newConversationVersion;
1238
+ } else if ('data' === chunk.type) {
1239
+ setMessages(produce((draft)=>{
1240
+ const lastMessage = draft.at(-1);
1241
+ lastMessage.swipes[lastMessage.activeSwipeId] = {
1242
+ content: chunk.content,
1243
+ mjv: chunk.mjv
1244
+ };
1245
+ if (chunk.finishReason) lastMessage.finishReason = chunk.finishReason;
1246
+ if (chunk.error) lastMessage.error = chunk.error;
1247
+ }));
1248
+ await new Promise((resolve)=>setTimeout(resolve, 10));
1248
1249
  }
1249
- } catch (error) {
1250
- Log.e('Stream error', 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);
1256
- } finally{
1257
- setMessages(produce((draft)=>{
1258
- const lastMessage = draft[draft.length - 1];
1259
- lastMessage.isGenerating = false;
1260
- }));
1261
1250
  }
1262
1251
  } catch (error) {
1263
- console.error('Error:', error);
1264
- setError(new SendMessageError('Send message failed', error));
1265
- setMessages((prev)=>prev.slice(0, -1));
1252
+ Log.e('Stream error', error);
1253
+ setMessages(produce((draft)=>{
1254
+ const lastMessage = draft.at(-1);
1255
+ if (isError(error) && isNetworkError(error.message)) lastMessage.error = {
1256
+ code: -10000,
1257
+ message: 'E_NETWORK_ERROR'
1258
+ };
1259
+ else lastMessage.error = {
1260
+ code: 1000000005,
1261
+ message: String(error)
1262
+ };
1263
+ }));
1266
1264
  } finally{
1265
+ setMessages(produce((draft)=>{
1266
+ const lastMessage = draft[draft.length - 1];
1267
+ lastMessage.isGenerating = false;
1268
+ if (lastMessage.error?.code === 2011030009) {
1269
+ const backup = backupMessages.find((m)=>m.id === lastMessage.id);
1270
+ if (backup) {
1271
+ lastMessage.swipes = backup.swipes;
1272
+ lastMessage.activeSwipeId = backup.activeSwipeId;
1273
+ } else {
1274
+ lastMessage.activeSwipeId -= 1;
1275
+ lastMessage.swipes.length = lastMessage.swipes.length - 1;
1276
+ }
1277
+ } else if (lastMessage.error?.code === 2011030001) {
1278
+ setMessages(backupMessages);
1279
+ query && setInput(query);
1280
+ }
1281
+ }));
1267
1282
  setIsStreaming(false);
1268
1283
  }
1269
1284
  }, [
@@ -1288,8 +1303,17 @@ const useChatStreaming = ({ common, setError, setMessages, getMessages, setInput
1288
1303
  }
1289
1304
  } catch (error) {
1290
1305
  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);
1306
+ setMessages(produce((draft)=>{
1307
+ const lastMessage = draft.at(-1);
1308
+ if (isError(error) && isNetworkError(error.message)) lastMessage.error = {
1309
+ code: -10000,
1310
+ message: 'E_NETWORK_ERROR'
1311
+ };
1312
+ else lastMessage.error = {
1313
+ code: 1000000005,
1314
+ message: String(error)
1315
+ };
1316
+ }));
1293
1317
  } finally{
1294
1318
  setMessages(produce((draft)=>{
1295
1319
  const msg = draft.find((m)=>m.id === messageId);
@@ -1306,17 +1330,12 @@ const useChatStreaming = ({ common, setError, setMessages, getMessages, setInput
1306
1330
  recoverStreaming
1307
1331
  };
1308
1332
  };
1309
- const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1310
- const [error, _setError] = useState();
1333
+ const useChat = ({ body, onFinish, pageSize = 10 })=>{
1311
1334
  const [input, setInput] = useState('');
1312
1335
  const conversationVersion = useRef(0);
1313
1336
  const mujian = useMujian();
1314
- const setError = (error)=>{
1315
- _setError(error);
1316
- if (error) onError?.(error);
1317
- };
1318
1337
  const isLoadingMore = useRef(false);
1319
- const { data, loadMoreAsync, loadingMore, loading, mutate, error: messageLoadError } = useInfiniteScroll(async (current)=>{
1338
+ const { data, loadMoreAsync, loadingMore, loading, mutate, error: messageLoadError, reloadAsync } = useInfiniteScroll(async (current)=>{
1320
1339
  isLoadingMore.current = true;
1321
1340
  if (current?.noMore) return {
1322
1341
  noMore: true,
@@ -1338,6 +1357,9 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1338
1357
  }, {
1339
1358
  direction: 'top'
1340
1359
  });
1360
+ const refresh = async ()=>{
1361
+ await reloadAsync();
1362
+ };
1341
1363
  const loadMoreMessage = async ()=>{
1342
1364
  if (isLoadingMore.current) return;
1343
1365
  try {
@@ -1367,7 +1389,6 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1367
1389
  common: {
1368
1390
  body
1369
1391
  },
1370
- setError,
1371
1392
  setMessages,
1372
1393
  getMessages,
1373
1394
  setInput,
@@ -1384,15 +1405,10 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1384
1405
  ]);
1385
1406
  const append = useCallback(async (query)=>{
1386
1407
  if (isStreaming) throw new Error('useChat is streaming already.');
1387
- try {
1388
- await chatStreaming({
1389
- query,
1390
- conversationVersion: conversationVersion.current
1391
- });
1392
- } catch (e) {
1393
- setError(e);
1394
- throw e;
1395
- }
1408
+ await chatStreaming({
1409
+ query,
1410
+ conversationVersion: conversationVersion.current
1411
+ });
1396
1412
  }, [
1397
1413
  isStreaming,
1398
1414
  chatStreaming
@@ -1400,17 +1416,12 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1400
1416
  const regenerate = useCallback(async ()=>{
1401
1417
  if (isStreaming) throw new Error('useChat is streaming already.');
1402
1418
  const lastMessage = messages.findLast((message)=>message.isUser);
1403
- try {
1404
- const query = lastMessage ? lastMessage.swipes[lastMessage.activeSwipeId].content : '';
1405
- await chatStreaming({
1406
- query,
1407
- regenerate: true,
1408
- conversationVersion: conversationVersion.current
1409
- });
1410
- } catch (e) {
1411
- setError(e);
1412
- throw e;
1413
- }
1419
+ const query = lastMessage ? lastMessage.swipes[lastMessage.activeSwipeId].content : '';
1420
+ await chatStreaming({
1421
+ query,
1422
+ regenerate: true,
1423
+ conversationVersion: conversationVersion.current
1424
+ });
1414
1425
  }, [
1415
1426
  isStreaming,
1416
1427
  messages,
@@ -1418,16 +1429,11 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1418
1429
  ]);
1419
1430
  const continueGenerate = useCallback(async ()=>{
1420
1431
  if (isStreaming) throw new Error('useChat is streaming already.');
1421
- try {
1422
- await chatStreaming({
1423
- query: '',
1424
- is_continue: true,
1425
- conversationVersion: conversationVersion.current
1426
- });
1427
- } catch (e) {
1428
- setError(e);
1429
- throw e;
1430
- }
1432
+ await chatStreaming({
1433
+ query: '',
1434
+ is_continue: true,
1435
+ conversationVersion: conversationVersion.current
1436
+ });
1431
1437
  }, [
1432
1438
  isStreaming,
1433
1439
  chatStreaming
@@ -1439,10 +1445,10 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1439
1445
  };
1440
1446
  const setSwipe = async (messageId, swipeId)=>{
1441
1447
  if (swipeId < 0) return;
1442
- setMessages((prev)=>prev.map((msg)=>msg.id === messageId && swipeId < msg.swipes.length ? {
1443
- ...msg,
1444
- activeSwipeId: swipeId
1445
- } : msg));
1448
+ setMessages(produce((draft)=>{
1449
+ const msg = draft.find((m)=>m.id === messageId);
1450
+ if (msg) msg.activeSwipeId = swipeId;
1451
+ }));
1446
1452
  const resp = await mujian.ai.chat.message.swipe(messageId, swipeId);
1447
1453
  const schema = z.object({
1448
1454
  newConversationVersion: z.number()
@@ -1457,20 +1463,10 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1457
1463
  });
1458
1464
  const { data, success } = schema.safeParse(resp);
1459
1465
  if (success && data.newConversationVersion) conversationVersion.current = data.newConversationVersion;
1460
- const patchMessage = (msg)=>{
1461
- const newMsg = {
1462
- ...msg,
1463
- content
1464
- };
1465
- if (!newMsg.isUser) {
1466
- newMsg.swipes = [
1467
- ...newMsg.swipes
1468
- ];
1469
- newMsg.swipes[newMsg.activeSwipeId].content = content;
1470
- }
1471
- return newMsg;
1472
- };
1473
- setMessages((prev)=>prev.map((msg)=>msg.id === messageId ? patchMessage(msg) : msg));
1466
+ setMessages(produce((draft)=>{
1467
+ const msg = draft.find((msg)=>msg.id === messageId);
1468
+ if (msg) msg.swipes[msg.activeSwipeId].content = content;
1469
+ }));
1474
1470
  };
1475
1471
  const deleteMessage = async (messageId)=>{
1476
1472
  if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX)) try {
@@ -1492,11 +1488,9 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1492
1488
  stop,
1493
1489
  messages: messages,
1494
1490
  status: (()=>{
1495
- if (error) return 'error';
1496
1491
  if (isStreaming) return 'streaming';
1497
1492
  return 'ready';
1498
1493
  })(),
1499
- error,
1500
1494
  setMessages: setMessages,
1501
1495
  input,
1502
1496
  setInput,
@@ -1507,7 +1501,8 @@ const useChat = ({ body, onError, onFinish, pageSize = 10 })=>{
1507
1501
  editMessage,
1508
1502
  deleteMessage,
1509
1503
  loadMoreMessage,
1510
- messagesStatus: messageLoadError ? 'error' : loadingMore || loading ? 'loading' : data?.noMore ? 'all-loaded' : 'has-more'
1504
+ messagesStatus: messageLoadError ? 'error' : loadingMore || loading ? 'loading' : data?.noMore ? 'all-loaded' : 'has-more',
1505
+ refresh
1511
1506
  };
1512
1507
  };
1513
1508
  export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useChatContext, useMjEngine, useMujian };