@ai-group/chat-sdk 3.6.6 → 3.6.8

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.
@@ -45,14 +45,51 @@ var combineTextParts = function combineTextParts(parts) {
45
45
  return result;
46
46
  };
47
47
  var DEFAULT_SESSION_EVENTS_POLLING_INTERVAL = 10000;
48
+
49
+ /**
50
+ * 从 event + part 构建非文本 IMessage(functionCall / functionResponse / executableCode 等)。
51
+ */
52
+ var buildNonTextMessage = function buildNonTextMessage(event, part) {
53
+ var role = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "bot";
54
+ var msg = {
55
+ id: v4(),
56
+ author: event.author,
57
+ invocationId: event.invocationId,
58
+ eventId: event.id,
59
+ timestamp: event.timestamp,
60
+ isLike: event.isLike || 0,
61
+ role: role,
62
+ modelCode: event.modelCode,
63
+ usageMetadata: event.usageMetadata,
64
+ finishReason: event.finishReason,
65
+ raw: event
66
+ };
67
+ if (part.inlineData) {
68
+ msg.inlineData = {
69
+ displayName: part.inlineData.displayName,
70
+ data: part.inlineData.data,
71
+ mimeType: part.inlineData.mimeType
72
+ };
73
+ } else if (part.fileData) {
74
+ msg.fileData = [part.fileData];
75
+ } else if (part.functionCall) {
76
+ msg.functionCall = part.functionCall;
77
+ } else if (part.functionResponse) {
78
+ msg.functionResponse = part.functionResponse;
79
+ } else if (part.executableCode) {
80
+ msg.executableCode = part.executableCode;
81
+ } else if (part.codeExecutionResult) {
82
+ msg.codeExecutionResult = part.codeExecutionResult;
83
+ } else if (part.text) {
84
+ msg.text = part.text;
85
+ msg.thought = part.thought;
86
+ }
87
+ return msg;
88
+ };
48
89
  var getMessageEventId = function getMessageEventId(messageItem) {
49
90
  var _messageItem$raw;
50
91
  return messageItem.eventId || ((_messageItem$raw = messageItem.raw) === null || _messageItem$raw === void 0 ? void 0 : _messageItem$raw.id);
51
92
  };
52
- var getMessageInvocationId = function getMessageInvocationId(messageItem) {
53
- var _messageItem$raw2;
54
- return messageItem.invocationId || ((_messageItem$raw2 = messageItem.raw) === null || _messageItem$raw2 === void 0 ? void 0 : _messageItem$raw2.invocationId);
55
- };
56
93
  var deduplicateMessages = function deduplicateMessages(messageItems) {
57
94
  var seenMessageIds = new Set();
58
95
  var hasDuplicate = false;
@@ -64,16 +101,18 @@ var deduplicateMessages = function deduplicateMessages(messageItems) {
64
101
  seenMessageIds.add(messageItem.id);
65
102
  return true;
66
103
  });
104
+
105
+ // 机器人文本消息去重:同一事件、同一思考类型、同一文件列表的文本消息,如果文本内容包含关系,则保留最长的文本消息,去掉被包含的短文本消息。
67
106
  var botTextGroups = new Map();
68
107
  uniqueIdMessages.forEach(function (messageItem) {
69
- var invocationId = getMessageInvocationId(messageItem);
70
- if (messageItem.role !== "bot" || !invocationId || typeof messageItem.text !== "string") return;
108
+ var eventId = getMessageEventId(messageItem);
109
+ if (messageItem.role !== "bot" || !eventId || typeof messageItem.text !== "string") return;
71
110
  var normalizedText = messageItem.text.replace(/\s+/g, " ").trim();
72
111
  if (!normalizedText) return;
73
112
  var fileKey = (messageItem.fileData || []).map(function (file) {
74
113
  return file.fileUri;
75
114
  }).join("|");
76
- var groupKey = "".concat(invocationId, "\0").concat(Boolean(messageItem.thought), "\0").concat(fileKey);
115
+ var groupKey = "".concat(eventId, "\0").concat(Boolean(messageItem.thought), "\0").concat(fileKey);
77
116
  var group = botTextGroups.get(groupKey) || [];
78
117
  group.push({
79
118
  messageItem: messageItem,
@@ -111,6 +150,10 @@ var mapSessionMessages = function mapSessionMessages(sessionEvents) {
111
150
  if (item.author === "user" && item.content.parts.some(function (part) {
112
151
  return part.task;
113
152
  })) return;
153
+ // functionResponse 的 user 事件仅用于配对 functionCall,不作为独立消息展示
154
+ if (item.content.role === "user" && item.content.parts.every(function (part) {
155
+ return !part || part.functionResponse;
156
+ })) return;
114
157
  var role = (item.content.role || "").toLowerCase() === "user" ? "user" : "bot";
115
158
  var parts = item.content.parts.filter(function (part) {
116
159
  if (!part) return false;
@@ -126,9 +169,13 @@ var mapSessionMessages = function mapSessionMessages(sessionEvents) {
126
169
  var otherParts = parts.filter(function (part) {
127
170
  return !part.text && !part.errorMessage && !part.inlineData && !part.fileData;
128
171
  });
172
+
173
+ // 使用确定性 ID:同一服务端事件 + 同一 part 类型始终生成相同 ID,
174
+ // 确保多次 mapSessionMessages 调用产生的消息可以被 id 去重正确识别。
175
+ var partCounter = 0;
129
176
  if (textParts.length > 0 || fileDataParts.length > 0) {
130
177
  var messageItem = {
131
- id: v4(),
178
+ id: "session-event:".concat(item.id, ":").concat(partCounter++),
132
179
  author: item.author,
133
180
  invocationId: item.invocationId,
134
181
  eventId: item.id,
@@ -169,7 +216,7 @@ var mapSessionMessages = function mapSessionMessages(sessionEvents) {
169
216
  }
170
217
  otherParts.forEach(function (part) {
171
218
  var messageItem = {
172
- id: v4(),
219
+ id: "session-event:".concat(item.id, ":").concat(partCounter++),
173
220
  author: item.author,
174
221
  invocationId: item.invocationId,
175
222
  eventId: item.id,
@@ -199,70 +246,62 @@ var mapSessionMessages = function mapSessionMessages(sessionEvents) {
199
246
  });
200
247
  return mapped;
201
248
  };
202
- var getComparableMessageContent = function getComparableMessageContent(messageItem) {
203
- var _messageItem$text;
204
- return JSON.stringify({
205
- role: messageItem.role,
206
- text: ((_messageItem$text = messageItem.text) === null || _messageItem$text === void 0 ? void 0 : _messageItem$text.replace(/\r\n/g, "\n").trim()) || "",
207
- thought: Boolean(messageItem.thought),
208
- inlineData: messageItem.inlineData || null,
209
- fileData: messageItem.fileData || null,
210
- functionCall: messageItem.functionCall || null,
211
- functionResponse: messageItem.functionResponse || null,
212
- executableCode: messageItem.executableCode || null,
213
- codeExecutionResult: messageItem.codeExecutionResult || null
214
- });
215
- };
216
- var isStreamMessageWithoutServerEventId = function isStreamMessageWithoutServerEventId(messageItem) {
217
- var eventId = getMessageEventId(messageItem);
218
- return Boolean(messageItem.invocationId) && (!eventId || eventId === messageItem.invocationId);
219
- };
220
- var insertMissingSessionEvents = function insertMissingSessionEvents(currentMessages, sessionEvents) {
249
+ var insertMissingSessionEvents = function insertMissingSessionEvents(currentMessages, sessionEvents, removedInvocationIds) {
221
250
  var renderedEventIds = new Set(currentMessages.map(getMessageEventId).filter(Boolean));
222
- var unmatchedStreamMessageIndexes = new Set(currentMessages.map(function (messageItem, index) {
223
- return isStreamMessageWithoutServerEventId(messageItem) ? index : -1;
224
- }).filter(function (index) {
225
- return index >= 0;
226
- }));
227
- var missingEvents = sessionEvents.filter(function (event) {
251
+
252
+ // 从所有 sessionEvents 中提取 functionResponse,配对到 currentMessages 中缺少响应的 functionCall
253
+ // (functionResponse 事件的 role 为 user,不应作为独立消息展示,仅用于补齐工具调用状态)
254
+ var nextMessages = _toConsumableArray(currentMessages);
255
+ var fnResMap = new Map();
256
+ sessionEvents.forEach(function (event) {
228
257
  var _event$content, _event$content2;
229
- var parts = (_event$content = event.content) === null || _event$content === void 0 ? void 0 : _event$content.parts;
230
- var canRender = Boolean(event.id) && ((_event$content2 = event.content) === null || _event$content2 === void 0 ? void 0 : _event$content2.role) === "model" && Array.isArray(parts) && parts.length > 0;
258
+ if (((_event$content = event.content) === null || _event$content === void 0 ? void 0 : _event$content.role) !== "user" || !Array.isArray((_event$content2 = event.content) === null || _event$content2 === void 0 ? void 0 : _event$content2.parts)) return;
259
+ event.content.parts.forEach(function (p) {
260
+ var _p$functionResponse;
261
+ if ((_p$functionResponse = p.functionResponse) !== null && _p$functionResponse !== void 0 && _p$functionResponse.id) {
262
+ fnResMap.set(p.functionResponse.id, p.functionResponse);
263
+ }
264
+ });
265
+ });
266
+ if (fnResMap.size > 0) {
267
+ var paired = false;
268
+ nextMessages = nextMessages.map(function (msg) {
269
+ if (msg.functionCall && !msg.functionResponse) {
270
+ var callId = msg.functionCall.id;
271
+ if (callId && fnResMap.has(callId)) {
272
+ paired = true;
273
+ return _objectSpread(_objectSpread({}, msg), {}, {
274
+ functionResponse: fnResMap.get(callId)
275
+ });
276
+ }
277
+ }
278
+ return msg;
279
+ });
280
+ if (!paired) nextMessages = currentMessages;
281
+ }
282
+ var missingEvents = sessionEvents.filter(function (event) {
283
+ var _event$content3, _event$content4;
284
+ var parts = (_event$content3 = event.content) === null || _event$content3 === void 0 ? void 0 : _event$content3.parts;
285
+ var canRender = Boolean(event.id) && ((_event$content4 = event.content) === null || _event$content4 === void 0 ? void 0 : _event$content4.role) === "model" && Array.isArray(parts) && parts.length > 0;
231
286
  if (!canRender || renderedEventIds.has(event.id)) return false;
232
287
 
233
- // 新版 SSE 包装数据没有事件 id,只能临时使用 invocationId。
234
- // invocationId + 完整渲染内容做一次性身份对齐,避免会话轮询重复插入;
235
- // 同 invocation 下内容不同或后续出现的事件,仍然按独立 event.id 补入。
236
- var eventMessages = mapSessionMessages([event]);
237
- var matchedIndexes = [];
238
- var isRenderedByStream = eventMessages.length > 0 && eventMessages.every(function (eventMessage) {
239
- var comparableContent = getComparableMessageContent(eventMessage);
240
- var matchedIndex = currentMessages.findIndex(function (currentMessage, index) {
241
- return unmatchedStreamMessageIndexes.has(index) && !matchedIndexes.includes(index) && currentMessage.invocationId === eventMessage.invocationId && getComparableMessageContent(currentMessage) === comparableContent;
242
- });
243
- if (matchedIndex < 0) return false;
244
- matchedIndexes.push(matchedIndex);
245
- return true;
246
- });
247
- if (isRenderedByStream) {
248
- matchedIndexes.forEach(function (index) {
249
- return unmatchedStreamMessageIndexes.delete(index);
250
- });
288
+ // reChat 截断后,忽略被删除的 invocationId 对应的旧 bot 消息
289
+ if (removedInvocationIds && removedInvocationIds.size > 0 && event.invocationId && removedInvocationIds.has(event.invocationId)) {
251
290
  return false;
252
291
  }
253
292
  return true;
254
293
  });
255
- if (missingEvents.length === 0) return currentMessages;
294
+ if (missingEvents.length === 0) return nextMessages;
256
295
  var missingMessages = mapSessionMessages(missingEvents);
257
- if (missingMessages.length === 0) return currentMessages;
296
+ if (missingMessages.length === 0) return nextMessages;
258
297
  var eventOrder = new Map(sessionEvents.map(function (event, index) {
259
298
  return [event.id, index];
260
299
  }));
261
300
  var getMessageOrder = function getMessageOrder(messageItem) {
262
- var _messageItem$timestam, _messageItem$raw3;
301
+ var _messageItem$timestam, _messageItem$raw2;
263
302
  var eventId = getMessageEventId(messageItem);
264
303
  if (eventId && eventOrder.has(eventId)) return eventOrder.get(eventId);
265
- var timestamp = (_messageItem$timestam = messageItem.timestamp) !== null && _messageItem$timestam !== void 0 ? _messageItem$timestam : (_messageItem$raw3 = messageItem.raw) === null || _messageItem$raw3 === void 0 ? void 0 : _messageItem$raw3.timestamp;
304
+ var timestamp = (_messageItem$timestam = messageItem.timestamp) !== null && _messageItem$timestam !== void 0 ? _messageItem$timestam : (_messageItem$raw2 = messageItem.raw) === null || _messageItem$raw2 === void 0 ? void 0 : _messageItem$raw2.timestamp;
266
305
  if (timestamp !== undefined) {
267
306
  var nextEventIndex = sessionEvents.findIndex(function (event) {
268
307
  return (event.timestamp || 0) > timestamp;
@@ -271,7 +310,8 @@ var insertMissingSessionEvents = function insertMissingSessionEvents(currentMess
271
310
  }
272
311
  return Number.POSITIVE_INFINITY;
273
312
  };
274
- var nextMessages = _toConsumableArray(currentMessages);
313
+
314
+ // nextMessages 已在上方 functionResponse 配对阶段创建,此处直接复用
275
315
  missingMessages.forEach(function (messageItem) {
276
316
  var targetOrder = getMessageOrder(messageItem);
277
317
  var insertIndex = nextMessages.findIndex(function (currentMessage) {
@@ -294,7 +334,7 @@ function useADKChat(_ref2) {
294
334
  _ref2$config = _ref2.config,
295
335
  config = _ref2$config === void 0 ? {} : _ref2$config,
296
336
  _ref2$type = _ref2.type,
297
- type = _ref2$type === void 0 ? "agentDebug" : _ref2$type,
337
+ _type = _ref2$type === void 0 ? "agentDebug" : _ref2$type,
298
338
  _ref2$enabled = _ref2.enabled,
299
339
  enabled = _ref2$enabled === void 0 ? true : _ref2$enabled,
300
340
  strategies = _ref2.strategies,
@@ -370,11 +410,11 @@ function useADKChat(_ref2) {
370
410
  messages = _useState20[0],
371
411
  setMessages = _useState20[1];
372
412
  var pendingSessionEventsRef = useRef(null);
373
- var sessionEventsFlushTimerRef = useRef(null);
374
- var sessionEventsSyncingRef = useRef(false);
375
413
  var sessionDetailLoadingRef = useRef(false);
376
414
  // 用户自定义业务参数 (stateDelta),每次发送请求时携带
377
415
  var stateDeltaRef = useRef(undefined);
416
+ // reChat 截断消息时记录被删除的 invocationId,防止轮询把旧 bot 回复补回
417
+ var reChatRemovedInvocationIdsRef = useRef(new Set());
378
418
 
379
419
  // ===== 多会话后台连接管理 =====
380
420
  var backgroundRef = useRef(new Map());
@@ -382,6 +422,7 @@ function useADKChat(_ref2) {
382
422
  messagesRef.current = messages;
383
423
  var currentSessionIdRef = useRef(currentSessionId);
384
424
  currentSessionIdRef.current = currentSessionId;
425
+ // 合并消息
385
426
  var mergedMessages = useMemo(function () {
386
427
  var fnResMap = {};
387
428
  var uniqueMessages = deduplicateMessages(messages);
@@ -403,33 +444,23 @@ function useADKChat(_ref2) {
403
444
  }, [messages]);
404
445
  var textMsgRef = useRef(null);
405
446
  var eventDataRef = useRef(new Map());
406
- var cancelSessionEventsFlush = useCallback(function () {
407
- if (sessionEventsFlushTimerRef.current === null) return;
408
- clearTimeout(sessionEventsFlushTimerRef.current);
409
- sessionEventsFlushTimerRef.current = null;
410
- }, []);
411
447
  var flushPendingSessionEvents = useCallback(function () {
412
448
  var pending = pendingSessionEventsRef.current;
413
- var replyInProgress = loadingRef.current || sendingRef.current || textMsgRef.current !== null;
449
+ // 仅在用户正在发送或流式文本拼接时跳过 flush,
450
+ // loading 状态不应阻塞轮询补入(异步任务完成的消息需要在 loading 期间也能补入)
451
+ var replyInProgress = sendingRef.current || textMsgRef.current !== null;
414
452
  if (!pending || replyInProgress || pending.sessionId !== currentSessionIdRef.current) return;
415
- var previewMessages = insertMissingSessionEvents(messagesRef.current, pending.events);
416
453
  pendingSessionEventsRef.current = null;
417
- if (previewMessages === messagesRef.current) return;
418
- sessionEventsSyncingRef.current = true;
419
- setLoading(true);
420
- sessionEventsFlushTimerRef.current = setTimeout(function () {
421
- sessionEventsFlushTimerRef.current = null;
422
- if (pending.sessionId === currentSessionIdRef.current) {
423
- setMessages(function (prev) {
424
- var nextMessages = insertMissingSessionEvents(prev, pending.events);
425
- messagesRef.current = nextMessages;
426
- return nextMessages;
427
- });
428
- }
429
- sessionEventsSyncingRef.current = false;
430
- setLoading(false);
431
- }, 0);
432
- }, [setLoading]);
454
+
455
+ // 使用 functional update 在同一个 setMessages 回调中完成 diff + 写入,
456
+ // 避免双重调用和 loading 闪烁。仅在有实际新消息时才触发 loading 状态。
457
+ setMessages(function (prev) {
458
+ var nextMessages = insertMissingSessionEvents(prev, pending.events, reChatRemovedInvocationIdsRef.current);
459
+ if (nextMessages === prev) return prev; // 无变化,不触发 re-render
460
+ messagesRef.current = nextMessages;
461
+ return nextMessages;
462
+ });
463
+ }, []);
433
464
  var syncSessionEvents = useCallback(function (events, targetSessionId) {
434
465
  if (targetSessionId !== currentSessionIdRef.current) return;
435
466
  pendingSessionEventsRef.current = {
@@ -440,12 +471,7 @@ function useADKChat(_ref2) {
440
471
  }, [flushPendingSessionEvents]);
441
472
  useEffect(function () {
442
473
  pendingSessionEventsRef.current = null;
443
- cancelSessionEventsFlush();
444
- if (sessionEventsSyncingRef.current) {
445
- sessionEventsSyncingRef.current = false;
446
- setLoading(false);
447
- }
448
- }, [cancelSessionEventsFlush, currentSessionId, setLoading]);
474
+ }, [currentSessionId]);
449
475
  useEffect(function () {
450
476
  if (!loading) flushPendingSessionEvents();
451
477
  }, [flushPendingSessionEvents, loading]);
@@ -462,21 +488,21 @@ function useADKChat(_ref2) {
462
488
  }, []);
463
489
 
464
490
  // 初始化开场白预设置问题
465
- var insertSuggestedQuestions = function insertSuggestedQuestions() {
491
+ var insertSuggestedQuestions = useCallback(function () {
466
492
  var suggested_questions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
467
493
  if (!(suggested_questions !== null && suggested_questions !== void 0 && suggested_questions.length)) return;
468
494
  // 无消息内容 且 有建议问题时
469
- if (messages.length === 0 && suggested_questions.length) {
470
- setSuggestedQuestions(suggested_questions);
471
- }
472
- };
495
+ setSuggestedQuestions(function (prev) {
496
+ return messages.length === 0 ? suggested_questions : prev;
497
+ });
498
+ }, [messages.length]);
473
499
 
474
500
  // 初始化开场白
475
- var insertPrologue = function insertPrologue(prologue) {
476
- if (prologue) {
477
- setPrologue(prologue);
501
+ var insertPrologue = useCallback(function (prologueText) {
502
+ if (prologueText) {
503
+ setPrologue(prologueText);
478
504
  }
479
- };
505
+ }, []);
480
506
 
481
507
  // 更新消息
482
508
  var updateMessage = useCallback(function (msg) {
@@ -543,48 +569,15 @@ function useADKChat(_ref2) {
543
569
 
544
570
  // 存储消息
545
571
  var storeMessage = useCallback(function (part, event, role) {
546
- var msg = {
547
- id: v4(),
548
- author: event.author,
549
- invocationId: event.invocationId,
550
- eventId: event.id,
551
- timestamp: event.timestamp,
552
- isLike: event.isLike || 0,
553
- role: role,
554
- modelCode: event.modelCode,
555
- usageMetadata: event.usageMetadata,
556
- finishReason: event.finishReason,
557
- raw: event
558
- };
559
- if (part) {
560
- if (part.inlineData) {
561
- msg.inlineData = {
562
- displayName: part.inlineData.displayName,
563
- data: part.inlineData.data,
564
- mimeType: part.inlineData.mimeType
565
- };
566
- } else if (part.text) {
567
- var _event$groundingMetad;
568
- msg.text = part.text;
569
- msg.thought = part.thought;
570
- if (event !== null && event !== void 0 && (_event$groundingMetad = event.groundingMetadata) !== null && _event$groundingMetad !== void 0 && (_event$groundingMetad = _event$groundingMetad.searchEntryPoint) !== null && _event$groundingMetad !== void 0 && _event$groundingMetad.renderedContent) {
571
- msg.renderedContent = event.groundingMetadata.searchEntryPoint.renderedContent;
572
- }
573
- } else if (part.fileData) {
574
- msg.fileData = [part.fileData];
575
- } else if (part.functionCall) {
576
- msg.functionCall = part.functionCall;
577
- } else if (part.functionResponse) {
578
- msg.functionResponse = part.functionResponse;
579
- } else if (part.executableCode) {
580
- msg.executableCode = part.executableCode;
581
- } else if (part.codeExecutionResult) {
582
- msg.codeExecutionResult = part.codeExecutionResult;
583
- }
584
- }
585
- if (part && Object.keys(part).length > 0) {
586
- insertMessage(msg);
572
+ var _event$groundingMetad;
573
+ if (!part || Object.keys(part).length === 0) return;
574
+ var msg = buildNonTextMessage(event, part, role);
575
+
576
+ // groundingMetadata 仅在前台 SSE 场景需要
577
+ if (part.text && event !== null && event !== void 0 && (_event$groundingMetad = event.groundingMetadata) !== null && _event$groundingMetad !== void 0 && (_event$groundingMetad = _event$groundingMetad.searchEntryPoint) !== null && _event$groundingMetad !== void 0 && _event$groundingMetad.renderedContent) {
578
+ msg.renderedContent = event.groundingMetadata.searchEntryPoint.renderedContent;
587
579
  }
580
+ insertMessage(msg);
588
581
  }, [insertMessage]);
589
582
 
590
583
  // 处理消息部分(关键更新)
@@ -643,9 +636,9 @@ function useADKChat(_ref2) {
643
636
 
644
637
  // 处理建议类消息
645
638
  var processFollowupPart = useCallback(function (event) {
646
- var _event$content3;
639
+ var _event$content5;
647
640
  // 获取建议类消息的文本内容
648
- var part = (event === null || event === void 0 || (_event$content3 = event.content) === null || _event$content3 === void 0 || (_event$content3 = _event$content3.parts) === null || _event$content3 === void 0 ? void 0 : _event$content3[0]) || null;
641
+ var part = (event === null || event === void 0 || (_event$content5 = event.content) === null || _event$content5 === void 0 || (_event$content5 = _event$content5.parts) === null || _event$content5 === void 0 ? void 0 : _event$content5[0]) || null;
649
642
  var text = (part === null || part === void 0 ? void 0 : part.text) || "";
650
643
  storeEvents(part, event);
651
644
  if (text) {
@@ -653,7 +646,7 @@ function useADKChat(_ref2) {
653
646
  return [].concat(_toConsumableArray(prev), [text]);
654
647
  });
655
648
  }
656
- }, [storeEvents, insertMessage]);
649
+ }, [storeEvents]);
657
650
 
658
651
  // 处理错误消息
659
652
  var processErrorMessage = useCallback(function (chunkJson) {
@@ -799,20 +792,7 @@ function useADKChat(_ref2) {
799
792
  }
800
793
  } else {
801
794
  bgTextMsg = null;
802
- var msg = {
803
- id: v4(),
804
- author: chunkJson.author,
805
- invocationId: chunkJson.invocationId,
806
- eventId: chunkJson.id,
807
- timestamp: chunkJson.timestamp,
808
- role: "bot"
809
- };
810
- if (part.functionCall) msg.functionCall = part.functionCall;else if (part.functionResponse) msg.functionResponse = part.functionResponse;else if (part.executableCode) msg.executableCode = part.executableCode;else if (part.codeExecutionResult) msg.codeExecutionResult = part.codeExecutionResult;else {
811
- msg.text = part.text;
812
- msg.thought = part.thought;
813
- if (part.fileData) msg.fileData = [part.fileData];
814
- }
815
- bg.messages = [].concat(_toConsumableArray(bg.messages), [msg]);
795
+ bg.messages = [].concat(_toConsumableArray(bg.messages), [buildNonTextMessage(chunkJson, part)]);
816
796
  }
817
797
  }
818
798
  } catch (err) {
@@ -847,7 +827,7 @@ function useADKChat(_ref2) {
847
827
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
848
828
  var _chunkJson2;
849
829
  var _part = _step3.value;
850
- // partial为 true 时忽略该条消息文本
830
+ // partial为 false 时忽略该条消息文本
851
831
  processPart(chunkJson, _part, ((_chunkJson2 = chunkJson) === null || _chunkJson2 === void 0 ? void 0 : _chunkJson2.partial) === false);
852
832
  }
853
833
  } catch (err) {
@@ -869,6 +849,12 @@ function useADKChat(_ref2) {
869
849
  bg.loading = false;
870
850
  bg.textMsgRef = null;
871
851
  }
852
+ // 如果该后台会话已被恢复到前台(backgroundRef 中已删除),
853
+ // 需要更新前台 loading 状态
854
+ if (!backgroundRef.current.has(targetSessionId) && currentSessionIdRef.current === targetSessionId) {
855
+ setLoading(false);
856
+ textMsgRef.current = null;
857
+ }
872
858
  } else {
873
859
  setLoading(false);
874
860
  if (textMsgRef.current) {
@@ -876,6 +862,7 @@ function useADKChat(_ref2) {
876
862
  onMessage === null || onMessage === void 0 || onMessage(((_textMsgRef$current = textMsgRef.current) === null || _textMsgRef$current === void 0 ? void 0 : _textMsgRef$current.text) || "", textMsgRef.current);
877
863
  }
878
864
  textMsgRef.current = null;
865
+ ctrl.current = null;
879
866
  }
880
867
  resolve();
881
868
  },
@@ -884,8 +871,14 @@ function useADKChat(_ref2) {
884
871
  if (isBackground) {
885
872
  var bg = backgroundRef.current.get(targetSessionId);
886
873
  if (bg) bg.loading = false;
874
+ // 同上:后台会话已恢复到前台时同步 loading 状态
875
+ if (!backgroundRef.current.has(targetSessionId) && currentSessionIdRef.current === targetSessionId) {
876
+ setLoading(false);
877
+ ctrl.current = null;
878
+ }
887
879
  } else {
888
880
  setLoading(false);
881
+ ctrl.current = null;
889
882
  }
890
883
  resolve();
891
884
  console.error("EventSource failed:", error);
@@ -893,7 +886,7 @@ function useADKChat(_ref2) {
893
886
  }
894
887
  });
895
888
  });
896
- }, [type, currentSessionId, processActionArtifact, processErrorMessage, processPart, updateLastBotEventMeta]);
889
+ }, [processActionArtifact, processErrorMessage, processPart, updateLastBotEventMeta]);
897
890
 
898
891
  // 发送会话
899
892
  var startChat = /*#__PURE__*/function () {
@@ -918,6 +911,8 @@ function useADKChat(_ref2) {
918
911
  sendingRef.current = true;
919
912
  // 清空建议问题
920
913
  setSuggestedQuestions([]);
914
+ // 正常发送时重置 reChat 删除标记
915
+ reChatRemovedInvocationIdsRef.current = new Set();
921
916
  // 发送消息
922
917
  setMessages(function (prev) {
923
918
  var temp = _toConsumableArray(prev);
@@ -954,8 +949,8 @@ function useADKChat(_ref2) {
954
949
  }
955
950
  return temp;
956
951
  });
957
- _context2.prev = 8;
958
- _context2.next = 11;
952
+ _context2.prev = 9;
953
+ _context2.next = 12;
959
954
  return sendMessageRequest(_objectSpread({
960
955
  stream: true,
961
956
  sessionId: currentSessionId,
@@ -980,18 +975,18 @@ function useADKChat(_ref2) {
980
975
  }, stateDeltaRef.current !== undefined && {
981
976
  stateDelta: stateDeltaRef.current
982
977
  }));
983
- case 11:
984
- _context2.prev = 11;
978
+ case 12:
979
+ _context2.prev = 12;
985
980
  sendingRef.current = false;
986
- return _context2.finish(11);
987
- case 14:
981
+ return _context2.finish(12);
982
+ case 15:
988
983
  // 获取聊天记录
989
984
  initAppConversations();
990
- case 15:
985
+ case 16:
991
986
  case "end":
992
987
  return _context2.stop();
993
988
  }
994
- }, _callee2, null, [[8,, 11, 14]]);
989
+ }, _callee2, null, [[9,, 12, 15]]);
995
990
  }));
996
991
  return function startChat(_x) {
997
992
  return _ref5.apply(this, arguments);
@@ -1001,7 +996,7 @@ function useADKChat(_ref2) {
1001
996
  // 重新发送会话
1002
997
  var reChat = /*#__PURE__*/function () {
1003
998
  var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
1004
- var lastUserIndex, newMessages, lastUserMessage, _lastUserMessage$text, text, _lastUserMessage$file, fileData;
999
+ var lastUserIndex, newMessages, removedInvocationIds, i, msg, lastUserMessage, _lastUserMessage$text, text, _lastUserMessage$file, fileData;
1005
1000
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
1006
1001
  while (1) switch (_context3.prev = _context3.next) {
1007
1002
  case 0:
@@ -1034,10 +1029,18 @@ function useADKChat(_ref2) {
1034
1029
  case 10:
1035
1030
  newMessages = messages.slice(0, lastUserIndex + 1);
1036
1031
  setMessages(newMessages);
1032
+
1033
+ // 收集被截断的 bot 消息的 invocationId,轮询时忽略这些旧回复
1034
+ removedInvocationIds = new Set();
1035
+ for (i = lastUserIndex + 1; i < messages.length; i++) {
1036
+ msg = messages[i];
1037
+ if (msg.invocationId) removedInvocationIds.add(msg.invocationId);
1038
+ }
1039
+ reChatRemovedInvocationIdsRef.current = removedInvocationIds;
1037
1040
  lastUserMessage = newMessages[lastUserIndex];
1038
1041
  _lastUserMessage$text = lastUserMessage.text, text = _lastUserMessage$text === void 0 ? "" : _lastUserMessage$text, _lastUserMessage$file = lastUserMessage.fileData, fileData = _lastUserMessage$file === void 0 ? [] : _lastUserMessage$file;
1039
- _context3.prev = 14;
1040
- _context3.next = 17;
1042
+ _context3.prev = 17;
1043
+ _context3.next = 20;
1041
1044
  return sendMessageRequest(_objectSpread({
1042
1045
  stream: true,
1043
1046
  sessionId: currentSessionId,
@@ -1057,15 +1060,15 @@ function useADKChat(_ref2) {
1057
1060
  }, stateDeltaRef.current !== undefined && {
1058
1061
  stateDelta: stateDeltaRef.current
1059
1062
  }));
1060
- case 17:
1061
- _context3.prev = 17;
1062
- sendingRef.current = false;
1063
- return _context3.finish(17);
1064
1063
  case 20:
1064
+ _context3.prev = 20;
1065
+ sendingRef.current = false;
1066
+ return _context3.finish(20);
1067
+ case 23:
1065
1068
  case "end":
1066
1069
  return _context3.stop();
1067
1070
  }
1068
- }, _callee3, null, [[14,, 17, 20]]);
1071
+ }, _callee3, null, [[17,, 20, 23]]);
1069
1072
  }));
1070
1073
  return function reChat() {
1071
1074
  return _ref6.apply(this, arguments);
@@ -1101,19 +1104,19 @@ function useADKChat(_ref2) {
1101
1104
  var stopChat = useCallback(function () {
1102
1105
  var _ctrl$current;
1103
1106
  pendingSessionEventsRef.current = null;
1104
- cancelSessionEventsFlush();
1105
- sessionEventsSyncingRef.current = false;
1106
1107
  (_ctrl$current = ctrl.current) === null || _ctrl$current === void 0 || _ctrl$current.abort();
1107
1108
  ctrl.current = null;
1108
1109
  setLoading(false);
1109
1110
  textMsgRef.current = null;
1110
- }, [cancelSessionEventsFlush, setLoading]);
1111
+ }, [setLoading]);
1111
1112
 
1112
1113
  // 清除/重置会话
1113
1114
  var clearChat = function clearChat() {
1114
1115
  var _appInfo$onboardingIn, _appInfo$onboardingIn2;
1115
1116
  var newSessionId = v4();
1116
1117
  setCurrentSessionId(newSessionId);
1118
+ // 清除 reChat 删除标记
1119
+ reChatRemovedInvocationIdsRef.current = new Set();
1117
1120
  // 重置聊天内容
1118
1121
  stopChat();
1119
1122
  setPrologue((appInfo === null || appInfo === void 0 || (_appInfo$onboardingIn = appInfo.onboardingInfo) === null || _appInfo$onboardingIn === void 0 ? void 0 : _appInfo$onboardingIn.prologue) || "");
@@ -1133,12 +1136,12 @@ function useADKChat(_ref2) {
1133
1136
  // 设置当前会话详情
1134
1137
  var setCurrentSession = /*#__PURE__*/function () {
1135
1138
  var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(sessionId) {
1136
- var prevSessionId, bg, _yield$fetchSessionDe, data, result, _appInfo$onboardingIn3, _appInfo$onboardingIn4;
1139
+ var prevSessionId, bg, isSseStillActive, _yield$fetchSessionDe, data, result, _appInfo$onboardingIn3, _appInfo$onboardingIn4;
1137
1140
  return _regeneratorRuntime().wrap(function _callee4$(_context4) {
1138
1141
  while (1) switch (_context4.prev = _context4.next) {
1139
1142
  case 0:
1140
1143
  if (!sessionId) {
1141
- _context4.next = 34;
1144
+ _context4.next = 35;
1142
1145
  break;
1143
1146
  }
1144
1147
  if (!(sessionId === currentSessionId)) {
@@ -1148,11 +1151,8 @@ function useADKChat(_ref2) {
1148
1151
  return _context4.abrupt("return");
1149
1152
  case 3:
1150
1153
  pendingSessionEventsRef.current = null;
1151
- cancelSessionEventsFlush();
1152
- if (sessionEventsSyncingRef.current) {
1153
- sessionEventsSyncingRef.current = false;
1154
- setLoading(false);
1155
- }
1154
+ // 切换会话时重置 reChat 删除标记
1155
+ reChatRemovedInvocationIdsRef.current = new Set();
1156
1156
 
1157
1157
  // 1. 将当前会话挂起到后台(不中断 SSE 连接)
1158
1158
  prevSessionId = currentSessionId;
@@ -1180,31 +1180,35 @@ function useADKChat(_ref2) {
1180
1180
  // 2. 尝试从后台恢复目标会话
1181
1181
  bg = backgroundRef.current.get(sessionId);
1182
1182
  if (!bg) {
1183
- _context4.next = 18;
1183
+ _context4.next = 19;
1184
1184
  break;
1185
1185
  }
1186
1186
  setMessages(bg.messages);
1187
- setLoading(bg.loading);
1187
+ messagesRef.current = bg.messages;
1188
1188
  textMsgRef.current = bg.textMsgRef;
1189
- ctrl.current = bg.loading ? bg.ctrl : null;
1189
+ // 仅当 SSE 连接还在运行时才恢复 loading 状态,
1190
+ // 避免 SSE 已关闭但 bg 快照中 loading 仍为 true 导致 sender 永久卡住
1191
+ isSseStillActive = bg.loading && !bg.ctrl.signal.aborted;
1192
+ setLoading(isSseStillActive);
1193
+ ctrl.current = isSseStillActive ? bg.ctrl : null;
1190
1194
  setCurrentSessionId(sessionId);
1191
1195
  setInitialized(true);
1192
1196
  backgroundRef.current.delete(sessionId);
1193
1197
  return _context4.abrupt("return");
1194
- case 18:
1198
+ case 19:
1195
1199
  // 3. 后台没有目标 session,走 API 加载(原有逻辑)
1196
1200
  setInitialized(false);
1197
1201
  setCurrentSessionId(sessionId);
1198
1202
  sessionDetailLoadingRef.current = true;
1199
- _context4.prev = 21;
1200
- _context4.next = 24;
1203
+ _context4.prev = 22;
1204
+ _context4.next = 25;
1201
1205
  return fetchSessionDetail({
1202
1206
  url: url,
1203
1207
  appNo: appNo,
1204
1208
  sessionId: sessionId,
1205
1209
  token: tokenRef.current
1206
1210
  });
1207
- case 24:
1211
+ case 25:
1208
1212
  _yield$fetchSessionDe = _context4.sent;
1209
1213
  data = _yield$fetchSessionDe.data;
1210
1214
  result = _yield$fetchSessionDe.result;
@@ -1216,21 +1220,21 @@ function useADKChat(_ref2) {
1216
1220
  setSuggestedQuestions((appInfo === null || appInfo === void 0 || (_appInfo$onboardingIn4 = appInfo.onboardingInfo) === null || _appInfo$onboardingIn4 === void 0 ? void 0 : _appInfo$onboardingIn4.suggested_questions) || []);
1217
1221
  }
1218
1222
  }
1219
- case 28:
1220
- _context4.prev = 28;
1223
+ case 29:
1224
+ _context4.prev = 29;
1221
1225
  sessionDetailLoadingRef.current = false;
1222
1226
  setInitialized(true);
1223
- return _context4.finish(28);
1224
- case 32:
1225
- _context4.next = 35;
1227
+ return _context4.finish(29);
1228
+ case 33:
1229
+ _context4.next = 36;
1226
1230
  break;
1227
- case 34:
1228
- setCurrentSessionId(v4());
1229
1231
  case 35:
1232
+ setCurrentSessionId(v4());
1233
+ case 36:
1230
1234
  case "end":
1231
1235
  return _context4.stop();
1232
1236
  }
1233
- }, _callee4, null, [[21,, 28, 32]]);
1237
+ }, _callee4, null, [[22,, 29, 33]]);
1234
1238
  }));
1235
1239
  return function setCurrentSession(_x2) {
1236
1240
  return _ref7.apply(this, arguments);
@@ -1565,7 +1569,7 @@ function useADKChat(_ref2) {
1565
1569
  return _regeneratorRuntime().wrap(function _callee10$(_context10) {
1566
1570
  while (1) switch (_context10.prev = _context10.next) {
1567
1571
  case 0:
1568
- if (!(requesting || sessionDetailLoadingRef.current)) {
1572
+ if (!(disposed || requesting || sessionDetailLoadingRef.current)) {
1569
1573
  _context10.next = 2;
1570
1574
  break;
1571
1575
  }
@@ -1606,7 +1610,8 @@ function useADKChat(_ref2) {
1606
1610
  return _ref13.apply(this, arguments);
1607
1611
  };
1608
1612
  }();
1609
- void poll();
1613
+
1614
+ // 不立即执行首次 poll,避免与 initAppConversations / setCurrentSession / fetchSessionDetail 竞争。首次数据由初始化流程加载,轮询仅负责后续增量同步。
1610
1615
  var timer = window.setInterval(poll, sessionEventsPollingInterval);
1611
1616
  return function () {
1612
1617
  disposed = true;
@@ -1618,7 +1623,6 @@ function useADKChat(_ref2) {
1618
1623
  useEffect(function () {
1619
1624
  return function () {
1620
1625
  var _ctrl$current2;
1621
- cancelSessionEventsFlush();
1622
1626
  (_ctrl$current2 = ctrl.current) === null || _ctrl$current2 === void 0 || _ctrl$current2.abort();
1623
1627
  backgroundRef.current.forEach(function (bg) {
1624
1628
  var _bg$ctrl;
@@ -1626,7 +1630,7 @@ function useADKChat(_ref2) {
1626
1630
  });
1627
1631
  backgroundRef.current.clear();
1628
1632
  };
1629
- }, [cancelSessionEventsFlush]);
1633
+ }, []);
1630
1634
  return {
1631
1635
  appInfo: appInfo,
1632
1636
  startChat: startChat,