@glodon-aiot/chat-app-sdk 0.0.14 → 0.0.16

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.
package/es/index.esm.js CHANGED
@@ -267864,6 +267864,43 @@ class PreSendLocalMessageFactory {
267864
267864
  }
267865
267865
  }
267866
267866
 
267867
+ ;// CONCATENATED MODULE: ../../../../../common/temp/default/node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isEqual.js
267868
+
267869
+
267870
+ /**
267871
+ * Performs a deep comparison between two values to determine if they are
267872
+ * equivalent.
267873
+ *
267874
+ * **Note:** This method supports comparing arrays, array buffers, booleans,
267875
+ * date objects, error objects, maps, numbers, `Object` objects, regexes,
267876
+ * sets, strings, symbols, and typed arrays. `Object` objects are compared
267877
+ * by their own, not inherited, enumerable properties. Functions and DOM
267878
+ * nodes are compared by strict equality, i.e. `===`.
267879
+ *
267880
+ * @static
267881
+ * @memberOf _
267882
+ * @since 0.1.0
267883
+ * @category Lang
267884
+ * @param {*} value The value to compare.
267885
+ * @param {*} other The other value to compare.
267886
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
267887
+ * @example
267888
+ *
267889
+ * var object = { 'a': 1 };
267890
+ * var other = { 'a': 1 };
267891
+ *
267892
+ * _.isEqual(object, other);
267893
+ * // => true
267894
+ *
267895
+ * object === other;
267896
+ * // => false
267897
+ */
267898
+ function lodash_es_isEqual_isEqual(value, other) {
267899
+ return _baseIsEqual(value, other);
267900
+ }
267901
+
267902
+ /* ESM default export */ const lodash_es_isEqual = (lodash_es_isEqual_isEqual);
267903
+
267867
267904
  ;// CONCATENATED MODULE: ../../../../../common/temp/default/node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_baseLodash.js
267868
267905
  /**
267869
267906
  * The function whose prototype chain sequence wrappers inherit from.
@@ -268366,10 +268403,126 @@ var flow = _createFlow();
268366
268403
 
268367
268404
  class StreamBufferHelper {
268368
268405
  /**
268406
+ * 合并两个 JSON 对象(深度合并)
268407
+ * - 如果两个值都是对象,递归合并
268408
+ * - 如果两个值都是字符串,追加字符串
268409
+ * - 如果两个值都是数组,合并数组(按索引合并)
268410
+ * - 否则使用 curr 的值(覆盖)
268411
+ */ mergeJsonObjects(prev, curr) {
268412
+ const merged = {
268413
+ ...prev
268414
+ };
268415
+ for(const key in curr){
268416
+ if (Object.prototype.hasOwnProperty.call(curr, key)) {
268417
+ const prevValue = prev[key];
268418
+ const currValue = curr[key];
268419
+ // 如果两个值都是对象,递归合并
268420
+ if (typeof prevValue === 'object' && prevValue !== null && !Array.isArray(prevValue) && typeof currValue === 'object' && currValue !== null && !Array.isArray(currValue)) {
268421
+ merged[key] = this.mergeJsonObjects(prevValue, currValue);
268422
+ } else if (typeof prevValue === 'string' && typeof currValue === 'string') {
268423
+ // 如果两个值都是字符串,追加字符串
268424
+ merged[key] = prevValue + currValue;
268425
+ } else if (Array.isArray(prevValue) && Array.isArray(currValue)) {
268426
+ // 如果两个值都是数组,按索引合并
268427
+ const mergedArray = [
268428
+ ...prevValue
268429
+ ];
268430
+ for(let i = 0; i < currValue.length; i++){
268431
+ const currItem = currValue[i];
268432
+ if (i < mergedArray.length) {
268433
+ // 如果索引已存在,尝试合并
268434
+ const prevItem = mergedArray[i];
268435
+ if (typeof prevItem === 'object' && prevItem !== null && !Array.isArray(prevItem) && typeof currItem === 'object' && currItem !== null && !Array.isArray(currItem)) {
268436
+ mergedArray[i] = this.mergeJsonObjects(prevItem, currItem);
268437
+ } else {
268438
+ mergedArray[i] = currItem;
268439
+ }
268440
+ } else {
268441
+ // 如果索引超出范围,追加新元素
268442
+ mergedArray.push(currItem);
268443
+ }
268444
+ }
268445
+ merged[key] = mergedArray;
268446
+ } else {
268447
+ // 否则使用 curr 的值(覆盖)
268448
+ merged[key] = currValue;
268449
+ }
268450
+ }
268451
+ }
268452
+ return merged;
268453
+ }
268454
+ /**
268369
268455
  * Added Chunk message cache
268370
268456
  */ pushChunk(chunk) {
268371
268457
  this.streamChunkBuffer.push(chunk);
268372
268458
  }
268459
+ /**
268460
+ * Merge item_list arrays by index, handling JSON items specially
268461
+ */ mergeItemList(prevItemList, currItemList) {
268462
+ const mergedItemList = [
268463
+ ...prevItemList
268464
+ ];
268465
+ for(let i = 0; i < currItemList.length; i++){
268466
+ const currItem = currItemList[i];
268467
+ if (i < mergedItemList.length) {
268468
+ const prevItem = mergedItemList[i];
268469
+ // Special handling: if both items are JSON items, merge their json fields
268470
+ if (typeof prevItem === 'object' && prevItem !== null && typeof currItem === 'object' && currItem !== null && 'type' in prevItem && 'type' in currItem && prevItem.type === types_ContentType.Json && currItem.type === types_ContentType.Json && 'json' in prevItem && 'json' in currItem) {
268471
+ const prevJson = prevItem.json;
268472
+ const currJson = currItem.json;
268473
+ // If both json fields are arrays, merge arrays by index
268474
+ if (Array.isArray(prevJson) && Array.isArray(currJson)) {
268475
+ const prevJsonArray = prevJson;
268476
+ const currJsonArray = currJson;
268477
+ const mergedJsonArray = [
268478
+ ...prevJsonArray
268479
+ ];
268480
+ for(let j = 0; j < currJsonArray.length; j++){
268481
+ if (j < mergedJsonArray.length) {
268482
+ mergedJsonArray[j] = currJsonArray[j];
268483
+ } else {
268484
+ mergedJsonArray.push(currJsonArray[j]);
268485
+ }
268486
+ }
268487
+ mergedItemList[i] = {
268488
+ ...currItem,
268489
+ json: mergedJsonArray
268490
+ };
268491
+ } else if (typeof prevJson === 'object' && prevJson !== null && !Array.isArray(prevJson) && typeof currJson === 'object' && currJson !== null && !Array.isArray(currJson)) {
268492
+ // If both json fields are objects, deep merge them
268493
+ const mergedJson = this.mergeJsonObjects(prevJson, currJson);
268494
+ mergedItemList[i] = {
268495
+ ...currItem,
268496
+ json: mergedJson
268497
+ };
268498
+ } else if (!lodash_es_isEqual(prevItem, currItem)) {
268499
+ mergedItemList[i] = currItem;
268500
+ }
268501
+ } else if (!lodash_es_isEqual(prevItem, currItem)) {
268502
+ mergedItemList[i] = currItem;
268503
+ }
268504
+ } else {
268505
+ mergedItemList.push(currItem);
268506
+ }
268507
+ }
268508
+ return mergedItemList;
268509
+ }
268510
+ /**
268511
+ * Merge refer_items arrays by index
268512
+ */ mergeReferItems(prevReferItems, currReferItems) {
268513
+ const mergedReferItems = [
268514
+ ...prevReferItems
268515
+ ];
268516
+ for(let i = 0; i < currReferItems.length; i++){
268517
+ const currReferItem = currReferItems[i];
268518
+ if (i < mergedReferItems.length) {
268519
+ mergedReferItems[i] = currReferItem;
268520
+ } else {
268521
+ mergedReferItems.push(currReferItem);
268522
+ }
268523
+ }
268524
+ return mergedReferItems;
268525
+ }
268373
268526
  concatContentAndUpdateMessage(message) {
268374
268527
  const previousIndex = this.streamMessageBuffer.findIndex((item)=>item.message_id === message.message_id);
268375
268528
  // new
@@ -268379,27 +268532,45 @@ class StreamBufferHelper {
268379
268532
  }
268380
268533
  // update
268381
268534
  const previousMessage = this.streamMessageBuffer.at(previousIndex);
268535
+ // 如果是 completed 消息(is_finish = true),直接替换,不合并
268536
+ if (message.is_finish) {
268537
+ this.streamMessageBuffer[previousIndex] = message;
268538
+ return;
268539
+ }
268382
268540
  // For Mix type (object_string), merge item_list arrays instead of string concatenation
268541
+ // Support streaming messages: when receiving message.delta data with object_string content,
268542
+ // merge it with previous data of the same message using append rule
268543
+ // IMPORTANT: Current content may contain the full accumulated array, so we need to compare
268544
+ // and only add new items that don't exist in the previous content
268383
268545
  if (message.content_type === types_ContentType.Mix) {
268384
268546
  try {
268385
- var _prevContent_value, _currContent_value;
268547
+ var _prevContent_value, _currContent_value, _prevContent_value1, _currContent_value1;
268386
268548
  const prevContent = safeJSONParse((previousMessage === null || previousMessage === void 0 ? void 0 : previousMessage.content) || '{}', {
268387
- item_list: []
268549
+ item_list: [],
268550
+ refer_items: []
268388
268551
  });
268389
268552
  const currContent = safeJSONParse(message.content, {
268390
- item_list: []
268553
+ item_list: [],
268554
+ refer_items: []
268391
268555
  });
268556
+ const prevItemList = ((_prevContent_value = prevContent.value) === null || _prevContent_value === void 0 ? void 0 : _prevContent_value.item_list) || [];
268557
+ const currItemList = ((_currContent_value = currContent.value) === null || _currContent_value === void 0 ? void 0 : _currContent_value.item_list) || [];
268558
+ const prevReferItems = ((_prevContent_value1 = prevContent.value) === null || _prevContent_value1 === void 0 ? void 0 : _prevContent_value1.refer_items) || [];
268559
+ const currReferItems = ((_currContent_value1 = currContent.value) === null || _currContent_value1 === void 0 ? void 0 : _currContent_value1.refer_items) || [];
268560
+ // Merge item_list by index: update items at the same index, keep previous items if not updated
268561
+ // For arrays, merge by index position: update existing items, keep others unchanged
268562
+ // Special handling: if both prevItem and currItem are JSON items with array json fields, merge the arrays
268563
+ const mergedItemList = this.mergeItemList(prevItemList, currItemList);
268564
+ const mergedReferItems = this.mergeReferItems(prevReferItems, currReferItems);
268392
268565
  const mergedContent = {
268393
- item_list: [
268394
- ...((_prevContent_value = prevContent.value) === null || _prevContent_value === void 0 ? void 0 : _prevContent_value.item_list) || [],
268395
- ...((_currContent_value = currContent.value) === null || _currContent_value === void 0 ? void 0 : _currContent_value.item_list) || []
268396
- ],
268397
- refer_items: []
268566
+ item_list: mergedItemList,
268567
+ refer_items: mergedReferItems
268398
268568
  };
268399
268569
  message.content = JSON.stringify(mergedContent);
268400
268570
  message.content_obj = mergedContent;
268401
268571
  } catch (e) {
268402
268572
  // Fallback to string concatenation if parsing fails
268573
+ // Error is intentionally ignored as we fallback to string concatenation
268403
268574
  message.content = ((previousMessage === null || previousMessage === void 0 ? void 0 : previousMessage.content) || '') + message.content;
268404
268575
  message.content_obj = message.content;
268405
268576
  }
@@ -268510,7 +268681,8 @@ class ChunkProcessor {
268510
268681
  const { message } = chunk;
268511
268682
  // Find all corresponding replies
268512
268683
  const replyMessages = this.getReplyMessagesByReplyId(message.reply_id);
268513
- // Find if there is a verbose message, and identify the end of the answer, and filter out the finish of the interrupt scene
268684
+ // Find if there is a verbose message, and identify the end of the answer,
268685
+ // and filter out the finish of the interrupt scene
268514
268686
  const finalAnswerVerboseMessage = replyMessages.find((replyMessage)=>{
268515
268687
  const { type, content } = replyMessage;
268516
268688
  if (type !== 'verbose') {
@@ -268521,7 +268693,9 @@ class ChunkProcessor {
268521
268693
  return false;
268522
268694
  }
268523
268695
  const { value: verboseContentData } = safeJSONParse(verboseContent.data, null);
268524
- // At present, there may be a finish package in a group. If you need to filter out the interrupt scene through finish_reason, you will get the finish that answers all the ends.
268696
+ // At present, there may be a finish package in a group.
268697
+ // If you need to filter out the interrupt scene through finish_reason,
268698
+ // you will get the finish that answers all the ends.
268525
268699
  return verboseContent.msg_type === types_VerboseMsgType.GENERATE_ANSWER_FINISH && (verboseContentData === null || verboseContentData === void 0 ? void 0 : verboseContentData.finish_reason) !== types_FinishReasonType.INTERRUPT;
268526
268700
  });
268527
268701
  return Boolean(finalAnswerVerboseMessage);
@@ -269345,6 +269519,14 @@ class HttpChunk extends CustomEventEmitter {
269345
269519
  }
269346
269520
  await fetchStream(channelFetchInfo.url, {
269347
269521
  onStart: (response)=>{
269522
+ // 如果状态码是错误状态码(>= HTTP_BAD_REQUEST),尝试读取响应体以获取错误信息
269523
+ const HTTP_BAD_REQUEST = 400;
269524
+ if (response.status >= HTTP_BAD_REQUEST) {
269525
+ const cloneResponse = response.clone();
269526
+ cloneResponse.text().catch(()=>{
269527
+ // 忽略读取错误
269528
+ });
269529
+ }
269348
269530
  fetchDataHelper.setLogID(response.headers.get('x-tt-logid'));
269349
269531
  return Promise.resolve();
269350
269532
  },
@@ -269392,7 +269574,8 @@ class HttpChunk extends CustomEventEmitter {
269392
269574
  streamParser: (onGetMessageStreamParser === null || onGetMessageStreamParser === void 0 ? void 0 : onGetMessageStreamParser(value)) || utils_streamParser,
269393
269575
  dataClump: fetchDataHelper,
269394
269576
  body: channelFetchInfo.body,
269395
- headers: channelFetchInfo.headers,
269577
+ // 将 headers 数组转换为 Headers 对象,因为 fetch API 需要 Headers 对象或普通对象
269578
+ headers: new Headers(channelFetchInfo.headers),
269396
269579
  method: channelFetchInfo.method,
269397
269580
  signal: fetchDataHelper.abortSignal.signal,
269398
269581
  totalFetchTimeout: fetchDataHelper.totalFetchTimeout,
@@ -269418,7 +269601,8 @@ class HttpChunk extends CustomEventEmitter {
269418
269601
  }
269419
269602
  this.customEmit(http_chunk_events_HttpChunkEvents.READ_STREAM_ERROR, errorInfo);
269420
269603
  return;
269421
- // TODO: The following should be the logic to re-pull the message. The server level did not have time to do it in this issue.
269604
+ // TODO: The following should be the logic to re-pull the message.
269605
+ // The server level did not have time to do it in this issue.
269422
269606
  // if (dataClump.retryCounter.matchMaxRetryAttempts()) {
269423
269607
  // this.customOnError?.(errorInfo);
269424
269608
  // //give up trying and try again
@@ -271827,43 +272011,6 @@ function isError_isError(value) {
271827
272011
 
271828
272012
  /* ESM default export */ const lodash_es_isError = (isError_isError);
271829
272013
 
271830
- ;// CONCATENATED MODULE: ../../../../../common/temp/default/node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isEqual.js
271831
-
271832
-
271833
- /**
271834
- * Performs a deep comparison between two values to determine if they are
271835
- * equivalent.
271836
- *
271837
- * **Note:** This method supports comparing arrays, array buffers, booleans,
271838
- * date objects, error objects, maps, numbers, `Object` objects, regexes,
271839
- * sets, strings, symbols, and typed arrays. `Object` objects are compared
271840
- * by their own, not inherited, enumerable properties. Functions and DOM
271841
- * nodes are compared by strict equality, i.e. `===`.
271842
- *
271843
- * @static
271844
- * @memberOf _
271845
- * @since 0.1.0
271846
- * @category Lang
271847
- * @param {*} value The value to compare.
271848
- * @param {*} other The other value to compare.
271849
- * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
271850
- * @example
271851
- *
271852
- * var object = { 'a': 1 };
271853
- * var other = { 'a': 1 };
271854
- *
271855
- * _.isEqual(object, other);
271856
- * // => true
271857
- *
271858
- * object === other;
271859
- * // => false
271860
- */
271861
- function lodash_es_isEqual_isEqual(value, other) {
271862
- return _baseIsEqual(value, other);
271863
- }
271864
-
271865
- /* ESM default export */ const lodash_es_isEqual = (lodash_es_isEqual_isEqual);
271866
-
271867
272014
  ;// CONCATENATED MODULE: ../../../../../common/temp/default/node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/head.js
271868
272015
  /**
271869
272016
  * Gets the first element of `array`.
@@ -297547,6 +297694,9 @@ const JsonItemList = (param)=>{
297547
297694
  }
297548
297695
  return /*#__PURE__*/ (0,jsx_runtime.jsx)(jsx_runtime.Fragment, {
297549
297696
  children: jsonItemList.map((item, idx)=>{
297697
+ // Use a stable key based on item content and index to avoid React reusing components incorrectly
297698
+ // This ensures that when items are updated, React correctly updates the component instead of reusing it
297699
+ const itemKey = `json-${message.message_id}-${idx}-${JSON.stringify(item.json).slice(0, 50)}`;
297550
297700
  if (JsonItem) {
297551
297701
  return /*#__PURE__*/ (0,jsx_runtime.jsx)("div", {
297552
297702
  // className={typeSafeMessageBoxInnerVariants({
@@ -297563,7 +297713,7 @@ const JsonItemList = (param)=>{
297563
297713
  schemaVersion: item.schema_version,
297564
297714
  message: message
297565
297715
  })
297566
- }, `json-${idx}`);
297716
+ }, itemKey);
297567
297717
  }
297568
297718
  // Default fallback: use built-in JSON viewer
297569
297719
  return /*#__PURE__*/ (0,jsx_runtime.jsx)("div", {
@@ -297574,7 +297724,7 @@ const JsonItemList = (param)=>{
297574
297724
  data: item.json,
297575
297725
  schemaVersion: item.schema_version
297576
297726
  })
297577
- }, `json-${idx}`);
297727
+ }, itemKey);
297578
297728
  })
297579
297729
  });
297580
297730
  };
@@ -407437,6 +407587,8 @@ const usePaginationRequest = (param)=>{
407437
407587
  const [currentPage, setCurrentPage] = (0,react.useState)(initialPageNum);
407438
407588
  const [allData, setAllData] = (0,react.useState)([]);
407439
407589
  const [hasMore, setHasMore] = (0,react.useState)(true);
407590
+ // 使用 ref 跟踪是否已经自动加载过,避免重复请求
407591
+ const hasAutoLoadedRef = (0,react.useRef)(false);
407440
407592
  const { loading, error, run } = es_useRequest(async (pageNum)=>{
407441
407593
  const targetPage = pageNum ?? currentPage;
407442
407594
  const params = {
@@ -407526,8 +407678,10 @@ const usePaginationRequest = (param)=>{
407526
407678
  });
407527
407679
  }, []);
407528
407680
  // 组件挂载时自动加载第一页
407681
+ // 修复:使用 ref 跟踪是否已加载,避免重复请求
407529
407682
  (0,react.useEffect)(()=>{
407530
- if (autoLoad) {
407683
+ if (autoLoad && !hasAutoLoadedRef.current) {
407684
+ hasAutoLoadedRef.current = true;
407531
407685
  run(initialPageNum);
407532
407686
  }
407533
407687
  }, [
@@ -407551,7 +407705,7 @@ const usePaginationRequest = (param)=>{
407551
407705
  };
407552
407706
 
407553
407707
  ;// CONCATENATED MODULE: ../open-chat/src/components/studio-open-chat/hooks/use-conversation-list.ts
407554
- /*
407708
+ /* eslint-disable max-lines -- This file handles complex conversation list synchronization logic that requires many lines */ /*
407555
407709
  * Copyright 2025 coze-dev Authors
407556
407710
  *
407557
407711
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -407581,7 +407735,7 @@ const usePaginationRequest = (param)=>{
407581
407735
 
407582
407736
 
407583
407737
 
407584
- // eslint-disable-next-line @coze-arch/max-line-per-function
407738
+ // eslint-disable-next-line @coze-arch/max-line-per-function -- This hook handles complex conversation list management logic that requires many lines
407585
407739
  const useConversationList = (conversationListParams)=>{
407586
407740
  const { pageSize = 20, initialPageNum = 1, order = 'updated_at' } = conversationListParams ?? {};
407587
407741
  const { chatConfig: { bot_id: botId, appInfo, type: chatType, auth: { connectorId } = {} } } = context_useChatAppProps();
@@ -407601,15 +407755,29 @@ const useConversationList = (conversationListParams)=>{
407601
407755
  // 使用 ref 来跟踪是否正在同步,避免循环更新
407602
407756
  const isSyncingRef = (0,react.useRef)(false);
407603
407757
  const lastStoreIdsRef = (0,react.useRef)('');
407758
+ // 使用 ref 原子性地跟踪 App 模式的加载状态,防止竞态条件
407759
+ const appModeLoadingRef = (0,react.useRef)(false);
407604
407760
  // App 模式:使用基于 before_updated_at 和 limit 的加载逻辑
407605
407761
  const [appModeData, setAppModeData] = (0,react.useState)([]);
407606
407762
  const [appModeLoading, setAppModeLoading] = (0,react.useState)(false);
407607
407763
  const [appModeHasMore, setAppModeHasMore] = (0,react.useState)(true);
407608
407764
  const [beforeUpdatedAt, setBeforeUpdatedAt] = (0,react.useState)(undefined);
407765
+ // 同步 appModeLoading 状态到 ref
407766
+ (0,react.useEffect)(()=>{
407767
+ appModeLoadingRef.current = appModeLoading;
407768
+ }, [
407769
+ appModeLoading
407770
+ ]);
407609
407771
  const loadAppModeConversations = (0,react.useCallback)(async (beforeTimestamp)=>{
407610
- if (!cozeApiSdk || !hasValidId || !isAppType || appModeLoading) {
407772
+ if (!cozeApiSdk || !hasValidId || !isAppType) {
407773
+ return;
407774
+ }
407775
+ // 原子性检查:如果已经在加载,直接返回
407776
+ if (appModeLoadingRef.current) {
407611
407777
  return;
407612
407778
  }
407779
+ // 原子性设置:立即设置 ref,防止并发调用
407780
+ appModeLoadingRef.current = true;
407613
407781
  setAppModeLoading(true);
407614
407782
  try {
407615
407783
  var _res_data, _res_data1;
@@ -407674,6 +407842,8 @@ const useConversationList = (conversationListParams)=>{
407674
407842
  console.error(e);
407675
407843
  setAppModeHasMore(false);
407676
407844
  } finally{
407845
+ // 原子性重置:先重置 ref,再更新状态
407846
+ appModeLoadingRef.current = false;
407677
407847
  setAppModeLoading(false);
407678
407848
  }
407679
407849
  }, [
@@ -407787,18 +407957,36 @@ const useConversationList = (conversationListParams)=>{
407787
407957
  // 同步 finalData 到 store(只在本地数据源变化时更新 store,避免循环)
407788
407958
  // 注意:这个 useEffect 只在从服务器加载数据时触发,不会在 store 同步到本地数据源时触发
407789
407959
  (0,react.useEffect)(()=>{
407790
- if (finalData && !isSyncingRef.current) {
407960
+ // 如果正在同步,跳过,避免循环更新
407961
+ if (isSyncingRef.current) {
407962
+ return;
407963
+ }
407964
+ if (finalData && finalData.length > 0) {
407791
407965
  // 比较数据是否真的变化了,避免不必要的更新
407792
407966
  const currentIds = conversations.map((c)=>c.id).sort().join(',');
407793
407967
  const newIds = finalData.map((c)=>c.id).sort().join(',');
407968
+ // 只有在 ID 列表真的发生变化时才更新 store
407969
+ // 这样可以避免因为对象引用变化导致的重复更新
407794
407970
  if (currentIds !== newIds) {
407795
- // 只有在本地数据源是从服务器加载的新数据时才更新 store
407796
- // 如果是因为 store 同步导致的 finalData 变化,不应该再次更新 store
407797
- isSyncingRef.current = true;
407798
- updateConversations(finalData, 'replace');
407799
- setTimeout(()=>{
407800
- isSyncingRef.current = false;
407801
- }, 0);
407971
+ // 检查是否是store新增了会话(store的ID数量增加了)
407972
+ // 如果是store新增,不应该在这里更新store,应该让监听store的useEffect处理
407973
+ const storeCount = conversations.length;
407974
+ const finalDataCount = finalData.length;
407975
+ const isStoreNewer = storeCount > finalDataCount;
407976
+ // 只有在finalData比store新(从服务器加载了新数据)时才更新store
407977
+ // 如果store比finalData新(store新增了会话),不应该更新store
407978
+ if (!isStoreNewer) {
407979
+ // 只有在本地数据源是从服务器加载的新数据时才更新 store
407980
+ // 如果是因为 store 同步导致的 finalData 变化,不应该再次更新 store
407981
+ isSyncingRef.current = true;
407982
+ updateConversations(finalData, 'replace');
407983
+ // 使用 requestAnimationFrame 确保在下一个事件循环中重置标志
407984
+ requestAnimationFrame(()=>{
407985
+ setTimeout(()=>{
407986
+ isSyncingRef.current = false;
407987
+ }, 0);
407988
+ });
407989
+ }
407802
407990
  }
407803
407991
  }
407804
407992
  }, [
@@ -407806,99 +407994,182 @@ const useConversationList = (conversationListParams)=>{
407806
407994
  updateConversations,
407807
407995
  conversations
407808
407996
  ]);
407997
+ // 检查会话是否有更新的辅助函数
407998
+ const checkConversationUpdates = (0,react.useCallback)((storeConversations, localConversations)=>{
407999
+ const localMap = new Map(localConversations.map((c)=>[
408000
+ c.id,
408001
+ c
408002
+ ]));
408003
+ return storeConversations.some((storeConv)=>{
408004
+ const localConv = localMap.get(storeConv.id);
408005
+ if (!localConv) {
408006
+ return false;
408007
+ }
408008
+ const storeConvWithTitle = storeConv;
408009
+ const localConvWithTitle = localConv;
408010
+ return storeConvWithTitle.title !== localConvWithTitle.title || storeConv.name !== localConv.name || storeConv.updated_at !== localConv.updated_at;
408011
+ });
408012
+ }, []);
408013
+ // 同步 App 模式更新的辅助函数
408014
+ const syncAppModeUpdates = (0,react.useCallback)(()=>{
408015
+ isSyncingRef.current = true;
408016
+ setAppModeData((prev)=>{
408017
+ const prevMap = new Map(prev.map((c)=>[
408018
+ c.id,
408019
+ c
408020
+ ]));
408021
+ return conversations.map((storeConv)=>{
408022
+ const prevConv = prevMap.get(storeConv.id);
408023
+ return prevConv ? {
408024
+ ...prevConv,
408025
+ ...storeConv
408026
+ } : storeConv;
408027
+ });
408028
+ });
408029
+ requestAnimationFrame(()=>{
408030
+ setTimeout(()=>{
408031
+ isSyncingRef.current = false;
408032
+ }, 0);
408033
+ });
408034
+ }, [
408035
+ conversations
408036
+ ]);
408037
+ // 同步 Bot 模式更新的辅助函数
408038
+ const syncBotModeUpdates = (0,react.useCallback)(()=>{
408039
+ isSyncingRef.current = true;
408040
+ const localMap = new Map(data.map((c)=>[
408041
+ c.id,
408042
+ c
408043
+ ]));
408044
+ const updatedConversations = conversations.filter((storeConv)=>localMap.has(storeConv.id)).map((storeConv)=>{
408045
+ const localConv = localMap.get(storeConv.id);
408046
+ return localConv ? {
408047
+ ...localConv,
408048
+ ...storeConv
408049
+ } : storeConv;
408050
+ });
408051
+ updatedConversations.forEach((conv)=>{
408052
+ removeBotModeItem((c)=>c.id === conv.id);
408053
+ });
408054
+ updatedConversations.reverse().forEach((conv)=>{
408055
+ addBotModeItem(conv, 'start');
408056
+ });
408057
+ requestAnimationFrame(()=>{
408058
+ setTimeout(()=>{
408059
+ isSyncingRef.current = false;
408060
+ }, 0);
408061
+ });
408062
+ }, [
408063
+ conversations,
408064
+ data,
408065
+ removeBotModeItem,
408066
+ addBotModeItem
408067
+ ]);
408068
+ // 处理删除的辅助函数
408069
+ const handleDeletions = (0,react.useCallback)((storeIds, localIds)=>{
408070
+ const deletedIds = Array.from(localIds).filter((id)=>!storeIds.has(id));
408071
+ if (deletedIds.length === 0) {
408072
+ return;
408073
+ }
408074
+ isSyncingRef.current = true;
408075
+ if (isAppType) {
408076
+ setAppModeData((prev)=>prev.filter((c)=>!deletedIds.includes(c.id)));
408077
+ } else {
408078
+ removeBotModeItem((c)=>deletedIds.includes(c.id));
408079
+ }
408080
+ requestAnimationFrame(()=>{
408081
+ setTimeout(()=>{
408082
+ isSyncingRef.current = false;
408083
+ }, 0);
408084
+ });
408085
+ }, [
408086
+ isAppType,
408087
+ removeBotModeItem
408088
+ ]);
408089
+ // 处理新增会话的辅助函数
408090
+ const handleNewConversations = (0,react.useCallback)((newConversations)=>{
408091
+ if (newConversations.length === 0) {
408092
+ return;
408093
+ }
408094
+ isSyncingRef.current = true;
408095
+ try {
408096
+ if (isAppType) {
408097
+ setAppModeData((prev)=>[
408098
+ ...newConversations,
408099
+ ...prev
408100
+ ]);
408101
+ } else {
408102
+ newConversations.forEach((conv)=>{
408103
+ addBotModeItem(conv, 'start');
408104
+ });
408105
+ }
408106
+ } finally{
408107
+ requestAnimationFrame(()=>{
408108
+ setTimeout(()=>{
408109
+ isSyncingRef.current = false;
408110
+ }, 50);
408111
+ });
408112
+ }
408113
+ }, [
408114
+ isAppType,
408115
+ addBotModeItem
408116
+ ]);
407809
408117
  // 监听 store 的 conversations 变化,同步更新本地数据源
407810
408118
  // 只在 store 有新增会话时才同步,避免循环更新
407811
408119
  (0,react.useEffect)(()=>{
408120
+ if (isSyncingRef.current) {
408121
+ return;
408122
+ }
407812
408123
  const currentStoreIds = conversations.map((c)=>c.id).sort().join(',');
407813
- // 如果 store ID 列表没有变化,跳过
407814
- if (currentStoreIds === lastStoreIdsRef.current) {
408124
+ const localConversations = isAppType ? appModeData : data;
408125
+ const hasUpdates = checkConversationUpdates(conversations, localConversations);
408126
+ if (currentStoreIds === lastStoreIdsRef.current && currentStoreIds !== '' && !hasUpdates) {
408127
+ return;
408128
+ }
408129
+ if (hasUpdates && (currentStoreIds === lastStoreIdsRef.current && currentStoreIds !== '' || lastStoreIdsRef.current === '' && currentStoreIds !== '')) {
408130
+ if (isAppType) {
408131
+ syncAppModeUpdates();
408132
+ } else {
408133
+ syncBotModeUpdates();
408134
+ }
408135
+ lastStoreIdsRef.current = currentStoreIds;
407815
408136
  return;
407816
408137
  }
407817
- // 检查是否有新增(store 的数量增加了)
407818
408138
  const storeCount = conversations.length;
407819
- const lastStoreCount = lastStoreIdsRef.current.split(',').filter(Boolean).length;
408139
+ const lastStoreCount = lastStoreIdsRef.current ? lastStoreIdsRef.current.split(',').filter(Boolean).length : 0;
407820
408140
  const hasNewConversations = storeCount > lastStoreCount;
407821
408141
  const hasDeletedConversations = storeCount < lastStoreCount;
407822
- // 更新 ref
407823
408142
  lastStoreIdsRef.current = currentStoreIds;
407824
- // 如果有删除,同步到本地数据源
407825
408143
  if (hasDeletedConversations) {
407826
- // 如果正在同步(可能是删除操作触发的),跳过
407827
- if (isSyncingRef.current) {
407828
- return;
407829
- }
407830
- if (isAppType) {
407831
- const storeIds = new Set(conversations.map((c)=>c.id));
407832
- const localIds = new Set(appModeData.map((c)=>c.id));
407833
- const deletedIds = Array.from(localIds).filter((id)=>!storeIds.has(id));
407834
- if (deletedIds.length > 0) {
407835
- isSyncingRef.current = true;
407836
- setAppModeData((prev)=>prev.filter((c)=>!deletedIds.includes(c.id)));
407837
- setTimeout(()=>{
407838
- isSyncingRef.current = false;
407839
- }, 0);
407840
- }
407841
- } else {
407842
- const storeIds = new Set(conversations.map((c)=>c.id));
407843
- const localIds = new Set(data.map((c)=>c.id));
407844
- const deletedIds = Array.from(localIds).filter((id)=>!storeIds.has(id));
407845
- if (deletedIds.length > 0) {
407846
- isSyncingRef.current = true;
407847
- removeBotModeItem((c)=>deletedIds.includes(c.id));
407848
- setTimeout(()=>{
407849
- isSyncingRef.current = false;
407850
- }, 0);
407851
- }
407852
- }
408144
+ const storeIds = new Set(conversations.map((c)=>c.id));
408145
+ const localIds = new Set(localConversations.map((c)=>c.id));
408146
+ handleDeletions(storeIds, localIds);
407853
408147
  return;
407854
408148
  }
407855
- // 只在有新增会话时才同步,避免其他变化导致的循环
407856
408149
  if (!hasNewConversations && storeCount === lastStoreCount) {
407857
- // 可能是更新,不需要同步
407858
- return;
407859
- }
407860
- // 有新增会话,同步到本地数据源
407861
- // 注意:即使 isSyncingRef.current 为 true,也要处理新增,因为删除和新增可能同时发生
407862
- isSyncingRef.current = true;
407863
- try {
407864
- if (isAppType) {
407865
- // App 模式:检查 store 中是否有新会话不在本地数据源中
407866
- const localIds = new Set(appModeData.map((c)=>c.id));
407867
- // 找出 store 中有但本地没有的会话(新添加的)
407868
- const newConversations = conversations.filter((c)=>!localIds.has(c.id));
407869
- if (newConversations.length > 0) {
407870
- // 将新会话添加到本地数据源的开头(最新的会话应该在前面)
407871
- setAppModeData((prev)=>[
407872
- ...newConversations,
407873
- ...prev
407874
- ]);
407875
- }
407876
- } else {
407877
- // Bot 模式:检查 store 中是否有新会话不在本地数据源中
407878
- const localIds = new Set(data.map((c)=>c.id));
407879
- // 找出 store 中有但本地没有的会话(新添加的)
407880
- const newConversations = conversations.filter((c)=>!localIds.has(c.id));
407881
- if (newConversations.length > 0) {
407882
- // 将新会话添加到本地数据源的开头(最新的会话应该在前面)
407883
- newConversations.forEach((conv)=>{
407884
- addBotModeItem(conv, 'start');
407885
- });
408150
+ const hasLocalUpdates = checkConversationUpdates(conversations, localConversations);
408151
+ if (hasLocalUpdates) {
408152
+ if (isAppType) {
408153
+ syncAppModeUpdates();
408154
+ } else {
408155
+ syncBotModeUpdates();
407886
408156
  }
407887
408157
  }
407888
- } finally{
407889
- // 使用 setTimeout 确保状态更新完成后再重置标志
407890
- // 延迟时间稍微长一点,确保第一个 useEffect 不会触发
407891
- setTimeout(()=>{
407892
- isSyncingRef.current = false;
407893
- }, 100);
408158
+ return;
407894
408159
  }
408160
+ const localIds = new Set(localConversations.map((c)=>c.id));
408161
+ const newConversations = conversations.filter((c)=>!localIds.has(c.id));
408162
+ handleNewConversations(newConversations);
407895
408163
  }, [
407896
408164
  conversations,
407897
408165
  isAppType,
407898
408166
  appModeData,
407899
408167
  data,
407900
- removeBotModeItem,
407901
- addBotModeItem
408168
+ checkConversationUpdates,
408169
+ syncAppModeUpdates,
408170
+ syncBotModeUpdates,
408171
+ handleDeletions,
408172
+ handleNewConversations
407902
408173
  ]);
407903
408174
  (0,react.useEffect)(()=>{
407904
408175
  if (!currentConversationInfo) {
@@ -407935,7 +408206,7 @@ const useConversationList = (conversationListParams)=>{
407935
408206
  updateCurrentConversationInfo
407936
408207
  ]);
407937
408208
  return {
407938
- conversations,
408209
+ conversations: finalData,
407939
408210
  loading: finalLoading,
407940
408211
  hasMore: finalHasMore,
407941
408212
  loadMore: finalLoadMore,
@@ -408020,6 +408291,48 @@ const useGroupedConversations = (conversations)=>{
408020
408291
  return groupedConversations;
408021
408292
  };
408022
408293
 
408294
+ ;// CONCATENATED MODULE: ../open-chat/src/util/conversation-display-name.ts
408295
+ /*
408296
+ * Copyright 2025 coze-dev Authors
408297
+ *
408298
+ * Licensed under the Apache License, Version 2.0 (the "License");
408299
+ * you may not use this file except in compliance with the License.
408300
+ * You may obtain a copy of the License at
408301
+ *
408302
+ * http://www.apache.org/licenses/LICENSE-2.0
408303
+ *
408304
+ * Unless required by applicable law or agreed to in writing, software
408305
+ * distributed under the License is distributed on an "AS IS" BASIS,
408306
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
408307
+ * See the License for the specific language governing permissions and
408308
+ * limitations under the License.
408309
+ */
408310
+ // UUID格式判断:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
408311
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
408312
+ const isUUID = (str)=>{
408313
+ if (!str) {
408314
+ return false;
408315
+ }
408316
+ return UUID_REGEX.test(str);
408317
+ };
408318
+ /**
408319
+ * 获取会话显示名称
408320
+ * 优先级:title(如果不是UUID)> name(如果不是UUID)> 新创建的会话
408321
+ * @param item 会话项
408322
+ * @returns 显示名称
408323
+ */ const getConversationDisplayName = (item)=>{
408324
+ // 优先使用title,但需要检查是否是UUID
408325
+ if (item.title && !isUUID(item.title)) {
408326
+ return item.title;
408327
+ }
408328
+ // 如果name存在且不是UUID格式,使用name
408329
+ if (item.name && !isUUID(item.name)) {
408330
+ return item.name;
408331
+ }
408332
+ // 否则显示"新创建的会话"
408333
+ return intl_i18n.t('web_sdk_conversation_default_name', {}, '新创建的会话');
408334
+ };
408335
+
408023
408336
  ;// CONCATENATED MODULE: ../open-chat/src/components/conversation-list-sider/conversation-item/mobile/operate/index.tsx
408024
408337
  /*
408025
408338
  * Copyright 2025 coze-dev Authors
@@ -408220,7 +408533,7 @@ const MobileConversationItem = (param)=>{
408220
408533
  ellipsis: {
408221
408534
  showTooltip: {
408222
408535
  opts: {
408223
- content: item.title || intl_i18n.t('web_sdk_conversation_default_name'),
408536
+ content: getConversationDisplayName(item),
408224
408537
  style: {
408225
408538
  wordBreak: 'break-all'
408226
408539
  },
@@ -408229,7 +408542,7 @@ const MobileConversationItem = (param)=>{
408229
408542
  }
408230
408543
  }
408231
408544
  },
408232
- children: item.title || intl_i18n.t('web_sdk_conversation_default_name')
408545
+ children: getConversationDisplayName(item)
408233
408546
  })
408234
408547
  })
408235
408548
  }),
@@ -408388,7 +408701,7 @@ const PcConversationItem = (param)=>{
408388
408701
  ellipsis: {
408389
408702
  showTooltip: {
408390
408703
  opts: {
408391
- content: item.title,
408704
+ content: getConversationDisplayName(item),
408392
408705
  style: {
408393
408706
  wordBreak: 'break-all'
408394
408707
  },
@@ -408397,7 +408710,7 @@ const PcConversationItem = (param)=>{
408397
408710
  }
408398
408711
  }
408399
408712
  },
408400
- children: item.title || intl_i18n.t('web_sdk_conversation_default_name')
408713
+ children: getConversationDisplayName(item)
408401
408714
  }),
408402
408715
  /*#__PURE__*/ (0,jsx_runtime.jsx)("div", {
408403
408716
  className: conversation_item_pc_index_module["conversation-operate-wrapper"],
@@ -408513,6 +408826,16 @@ const ConversationList = /*#__PURE__*/ (0,react.forwardRef)(// eslint-disable-ne
408513
408826
  ]);
408514
408827
  const listContainerRef = (0,react.useRef)(null);
408515
408828
  const loadMoreRef = (0,react.useRef)(null);
408829
+ // 使用 ref 存储 loadMore 函数,避免 useEffect 依赖项变化导致 observer 重新创建
408830
+ const loadMoreRefFn = (0,react.useRef)(loadMore);
408831
+ // 使用 ref 跟踪是否正在加载,防止重复调用
408832
+ const isLoadingRef = (0,react.useRef)(false);
408833
+ // 更新 loadMoreRefFn 当 loadMore 变化时
408834
+ (0,react.useEffect)(()=>{
408835
+ loadMoreRefFn.current = loadMore;
408836
+ }, [
408837
+ loadMore
408838
+ ]);
408516
408839
  const handleCreateConversation = ()=>{
408517
408840
  if (!currentConversationInfo) {
408518
408841
  return Promise.resolve();
@@ -408654,11 +408977,21 @@ const ConversationList = /*#__PURE__*/ (0,react.forwardRef)(// eslint-disable-ne
408654
408977
  handleCreateConversation,
408655
408978
  handleDeleteConversation
408656
408979
  }));
408980
+ // 同步 loading 状态到 ref
408981
+ (0,react.useEffect)(()=>{
408982
+ isLoadingRef.current = loading;
408983
+ }, [
408984
+ loading
408985
+ ]);
408657
408986
  (0,react.useEffect)(()=>{
408658
408987
  const observer = new IntersectionObserver((entries)=>{
408659
408988
  const [entry] = entries;
408660
- if (entry.isIntersecting && hasMore && !loading) {
408661
- loadMore();
408989
+ // 使用 ref 中的最新值,避免闭包问题
408990
+ // 注意:loadMore 函数内部已经有原子性检查,这里只需要检查基本条件
408991
+ if (entry.isIntersecting && hasMore && !isLoadingRef.current) {
408992
+ // 使用 ref 中的最新函数
408993
+ // loadMore 函数内部已经有原子性检查,可以安全地多次调用
408994
+ loadMoreRefFn.current();
408662
408995
  }
408663
408996
  }, {
408664
408997
  root: listContainerRef.current,
@@ -408669,15 +409002,12 @@ const ConversationList = /*#__PURE__*/ (0,react.forwardRef)(// eslint-disable-ne
408669
409002
  observer.observe(loadMoreRef.current);
408670
409003
  }
408671
409004
  return ()=>{
408672
- if (loadMoreRef.current) {
408673
- observer.unobserve(loadMoreRef.current);
408674
- }
409005
+ // 确保完全断开 observer 连接,防止旧的 observer 继续工作
409006
+ observer.disconnect();
408675
409007
  };
408676
409008
  }, [
408677
- hasMore,
408678
- loading,
408679
- loadMore
408680
- ]);
409009
+ hasMore
409010
+ ]); // 只依赖 hasMore,不依赖 loading 和 loadMore
408681
409011
  return /*#__PURE__*/ (0,jsx_runtime.jsxs)("div", {
408682
409012
  className: conversation_list_sider_conversation_list_index_module.conversations,
408683
409013
  style: {
@@ -409177,6 +409507,13 @@ const storageUtil = {
409177
409507
 
409178
409508
 
409179
409509
 
409510
+
409511
+
409512
+
409513
+
409514
+
409515
+
409516
+
409180
409517
  const getDefaultUserInfo = (userInfo)=>{
409181
409518
  if (userInfo === null || userInfo === void 0 ? void 0 : userInfo.id) {
409182
409519
  return userInfo;
@@ -409188,6 +409525,7 @@ const getDefaultUserInfo = (userInfo)=>{
409188
409525
  url: ''
409189
409526
  };
409190
409527
  };
409528
+ // eslint-disable-next-line @coze-arch/max-line-per-function -- Store creation function contains comprehensive state management logic
409191
409529
  const createChatStore = (chatConfig, userInfo)=>{
409192
409530
  var _chatConfig_auth, _chatConfig_auth1;
409193
409531
  const isNeedToken = (chatConfig === null || chatConfig === void 0 ? void 0 : (_chatConfig_auth = chatConfig.auth) === null || _chatConfig_auth === void 0 ? void 0 : _chatConfig_auth.type) === client_AuthType.TOKEN;
@@ -409314,9 +409652,20 @@ const createChatStore = (chatConfig, userInfo)=>{
409314
409652
  }));
409315
409653
  },
409316
409654
  updateCurrentConversationNameByMessage: async (name)=>{
409655
+ var _chatConfigRef_current;
409317
409656
  const { currentConversationInfo, updateCurrentConversationInfo, updateConversations, cozeApi } = get();
409318
- // 如果没有 conversationInfo 或已有名称,直接返回
409319
- if (!currentConversationInfo || currentConversationInfo.name) {
409657
+ // 如果没有 conversationInfo,直接返回
409658
+ if (!currentConversationInfo) {
409659
+ return Promise.resolve(false);
409660
+ }
409661
+ const isAppType = ((_chatConfigRef_current = chatConfigRef.current) === null || _chatConfigRef_current === void 0 ? void 0 : _chatConfigRef_current.type) === client_ChatType.APP;
409662
+ // 对于App模式,title应该只由chat接口的更新事件来设置,不应该主动PUT请求
409663
+ // 完全禁用App模式下的PUT请求,避免死循环
409664
+ if (isAppType) {
409665
+ return Promise.resolve(false);
409666
+ }
409667
+ // 对于Bot模式,如果已有名称,直接返回
409668
+ if (currentConversationInfo.name) {
409320
409669
  return Promise.resolve(false);
409321
409670
  }
409322
409671
  // 如果 conversationId 不存在或为空,说明 conversation 还未创建成功,直接返回
@@ -409325,13 +409674,9 @@ const createChatStore = (chatConfig, userInfo)=>{
409325
409674
  return Promise.resolve(false);
409326
409675
  }
409327
409676
  try {
409328
- var _chatConfigRef_current;
409329
- const isAppType = ((_chatConfigRef_current = chatConfigRef.current) === null || _chatConfigRef_current === void 0 ? void 0 : _chatConfigRef_current.type) === client_ChatType.APP;
409330
- const url = isAppType ? `/v1/workflow/conversations/${currentConversationInfo.id}` : `/v1/conversations/${currentConversationInfo.id}`;
409331
- // App 模式使用 title 字段,Bot 模式使用 name 字段
409332
- const requestBody = isAppType ? {
409333
- title: name
409334
- } : {
409677
+ const url = `/v1/conversations/${currentConversationInfo.id}`;
409678
+ // Bot 模式使用 name 字段
409679
+ const requestBody = {
409335
409680
  name
409336
409681
  };
409337
409682
  console.log('cozeApi?.put', url);
@@ -409355,12 +409700,37 @@ const createChatStore = (chatConfig, userInfo)=>{
409355
409700
  updateConversations: (conversations, operate)=>{
409356
409701
  set(immer_produce((s)=>{
409357
409702
  if (operate === 'replace') {
409358
- s.conversations = conversations;
409703
+ // 修复:在replace时,保留store中已有的title字段
409704
+ // 因为服务器返回的数据可能不包含最新的title(title是通过chat接口更新事件更新的)
409705
+ // 需要将新数据与store中已有的数据合并,保留store中的title
409706
+ const existingConversationsMap = new Map(s.conversations.map((c)=>[
409707
+ c.id,
409708
+ c
409709
+ ]));
409710
+ s.conversations = conversations.map((newConv)=>{
409711
+ const existingConv = existingConversationsMap.get(newConv.id);
409712
+ const existingConvWithTitle = existingConv;
409713
+ if (existingConvWithTitle === null || existingConvWithTitle === void 0 ? void 0 : existingConvWithTitle.title) {
409714
+ // 如果store中已有该会话且有title,保留title
409715
+ return Object.assign({
409716
+ ...newConv
409717
+ }, {
409718
+ title: existingConvWithTitle.title
409719
+ });
409720
+ }
409721
+ return newConv;
409722
+ });
409359
409723
  } else if (operate === 'add') {
409360
- s.conversations = [
409361
- ...s.conversations,
409362
- ...conversations
409363
- ];
409724
+ // 修复:添加去重逻辑,避免重复添加相同的会话
409725
+ const existingIds = new Set(s.conversations.map((c)=>c.id));
409726
+ const newConversations = conversations.filter((c)=>c.id && !existingIds.has(c.id));
409727
+ if (newConversations.length > 0) {
409728
+ // 将新会话添加到列表开头(最新的会话应该在前面)
409729
+ s.conversations = [
409730
+ ...newConversations,
409731
+ ...s.conversations
409732
+ ];
409733
+ }
409364
409734
  } else if (operate === 'remove') {
409365
409735
  conversations.forEach((conversation)=>{
409366
409736
  const index = s.conversations.findIndex((c)=>c.id === conversation.id);
@@ -409372,8 +409742,12 @@ const createChatStore = (chatConfig, userInfo)=>{
409372
409742
  conversations.forEach((conversation)=>{
409373
409743
  const index = s.conversations.findIndex((c)=>c.id === conversation.id);
409374
409744
  if (index !== -1) {
409745
+ // 修复:确保只更新传入的字段,保留原有字段
409746
+ // 先展开原有对象,再展开新对象,确保新对象的字段覆盖原有字段
409747
+ // 但不会丢失原有对象中的其他字段(如其他会话的title)
409748
+ const existingConversation = s.conversations[index];
409375
409749
  s.conversations[index] = {
409376
- ...s.conversations[index],
409750
+ ...existingConversation,
409377
409751
  ...conversation
409378
409752
  };
409379
409753
  }
@@ -413704,30 +414078,41 @@ studioOpenClientReporter.init(studio_open_client_reporter_slardarInstance);
413704
414078
 
413705
414079
 
413706
414080
 
414081
+
413707
414082
  const useUpdateConversationNameByMessage = ()=>{
413708
414083
  const currentConversationNameRef = (0,react.useRef)();
413709
414084
  const { updateCurrentConversationNameByMessage, currentConversationInfo } = context_useChatAppStore(shallow_useShallow((s)=>({
413710
414085
  updateCurrentConversationNameByMessage: s.updateCurrentConversationNameByMessage,
413711
414086
  currentConversationInfo: s.currentConversationInfo
413712
414087
  })));
414088
+ const { chatConfig: { type: chatType } } = context_useChatAppProps();
414089
+ const isAppType = chatType === client_ChatType.APP;
413713
414090
  const { useMessagesStore } = use_chat_area_context_useChatAreaStoreSet();
413714
414091
  const messages = useMessagesStore((s)=>s.messages, lodash_es_isEqual);
413715
414092
  (0,react.useEffect)(()=>{
413716
- currentConversationNameRef.current = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.name;
414093
+ // 对于App模式,检查title;对于Bot模式,检查name
414094
+ if (isAppType) {
414095
+ currentConversationNameRef.current = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.title;
414096
+ } else {
414097
+ currentConversationNameRef.current = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.name;
414098
+ }
413717
414099
  }, [
413718
- currentConversationInfo
414100
+ currentConversationInfo,
414101
+ isAppType
413719
414102
  ]);
413720
414103
  (0,react.useEffect)(()=>{
413721
414104
  const message = messages[messages.length - 1];
413722
414105
  const name = message === null || message === void 0 ? void 0 : message.content.slice(0, 100);
413723
414106
  // 确保 conversation 已创建成功(有 id)且还没有名称时才更新
414107
+ // 对于App模式,如果title已经存在(通过chat接口更新事件设置的),不应该再PUT请求
413724
414108
  if (message && !currentConversationNameRef.current && (currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.id) && currentConversationInfo.id.trim() !== '') {
413725
414109
  updateCurrentConversationNameByMessage(name);
413726
414110
  currentConversationNameRef.current = name;
413727
414111
  }
413728
414112
  }, [
413729
414113
  messages,
413730
- currentConversationInfo
414114
+ currentConversationInfo,
414115
+ updateCurrentConversationNameByMessage
413731
414116
  ]);
413732
414117
  };
413733
414118
 
@@ -416979,6 +417364,7 @@ const convertShortcutData = (shortcutCommands, botInfo)=>//@ts-expect-error: 不
416979
417364
 
416980
417365
 
416981
417366
 
417367
+
416982
417368
  const microSeconds = 1000;
416983
417369
  /**
416984
417370
  * 从URL中提取文件名
@@ -417017,6 +417403,55 @@ const microSeconds = 1000;
417017
417403
  };
417018
417404
  // 消息转换成 Coze的消息,主要用于消息接收后,在页面显示。
417019
417405
  class MessageConverseToCoze {
417406
+ /**
417407
+ * 合并两个 JSON 对象(深度合并)
417408
+ * - 如果两个值都是对象,递归合并
417409
+ * - 如果两个值都是字符串,追加字符串
417410
+ * - 如果两个值都是数组,合并数组(按索引合并)
417411
+ * - 否则使用 curr 的值(覆盖)
417412
+ */ mergeJsonObjects(prev, curr) {
417413
+ const merged = {
417414
+ ...prev
417415
+ };
417416
+ for(const key in curr){
417417
+ if (Object.prototype.hasOwnProperty.call(curr, key)) {
417418
+ const prevValue = prev[key];
417419
+ const currValue = curr[key];
417420
+ // 如果两个值都是对象,递归合并
417421
+ if (typeof prevValue === 'object' && prevValue !== null && !Array.isArray(prevValue) && typeof currValue === 'object' && currValue !== null && !Array.isArray(currValue)) {
417422
+ merged[key] = this.mergeJsonObjects(prevValue, currValue);
417423
+ } else if (typeof prevValue === 'string' && typeof currValue === 'string') {
417424
+ // 如果两个值都是字符串,追加字符串
417425
+ merged[key] = prevValue + currValue;
417426
+ } else if (Array.isArray(prevValue) && Array.isArray(currValue)) {
417427
+ // 如果两个值都是数组,按索引合并
417428
+ const mergedArray = [
417429
+ ...prevValue
417430
+ ];
417431
+ for(let i = 0; i < currValue.length; i++){
417432
+ const currItem = currValue[i];
417433
+ if (i < mergedArray.length) {
417434
+ // 如果索引已存在,尝试合并
417435
+ const prevItem = mergedArray[i];
417436
+ if (typeof prevItem === 'object' && prevItem !== null && !Array.isArray(prevItem) && typeof currItem === 'object' && currItem !== null && !Array.isArray(currItem)) {
417437
+ mergedArray[i] = this.mergeJsonObjects(prevItem, currItem);
417438
+ } else {
417439
+ mergedArray[i] = currItem;
417440
+ }
417441
+ } else {
417442
+ // 如果索引超出范围,追加新元素
417443
+ mergedArray.push(currItem);
417444
+ }
417445
+ }
417446
+ merged[key] = mergedArray;
417447
+ } else {
417448
+ // 否则使用 curr 的值(覆盖)
417449
+ merged[key] = currValue;
417450
+ }
417451
+ }
417452
+ }
417453
+ return merged;
417454
+ }
417020
417455
  convertMessageListResponse(res) {
417021
417456
  let botId = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : '';
417022
417457
  const { data: messageList = [], has_more: hasMore, first_id: firstId, last_id: lastId } = res;
@@ -417035,7 +417470,11 @@ class MessageConverseToCoze {
417035
417470
  const { content_type, content } = this.convertContent(message.content_type, message.content) || {};
417036
417471
  const isQuestion = message.type === 'question';
417037
417472
  const replyId = message.chat_id || `--custom-replyId--${index_browser_nanoid()}`;
417038
- const messageId = isQuestion ? replyId : message.id || `--custom-messageId-${index_browser_nanoid()}`; // 无messageId,输出一个默认的
417473
+ // 对于流式的 object_string 消息,使用 message.id 作为 message_id
417474
+ // 这样可以确保不同 id 的消息不会被合并
417475
+ const isStreamingObjectString = message.content_type === 'object_string' && message.type === 'answer';
417476
+ const messageId = isQuestion ? replyId : isStreamingObjectString ? message.id || `--custom-messageId-${index_browser_nanoid()}` // 对于流式 object_string,使用 message.id 作为 message_id
417477
+ : message.id || `--custom-messageId-${index_browser_nanoid()}`; // 无messageId,输出一个默认的
417039
417478
  const senderId = isQuestion ? '' : message.bot_id || botId;
417040
417479
  if (!content_type || !messageId || !replyId) {
417041
417480
  return {};
@@ -417126,12 +417565,30 @@ class MessageConverseToCoze {
417126
417565
  }
417127
417566
  }
417128
417567
  convertMixContent(content) {
417129
- const contentObj = catchParse(content);
417130
- if (!contentObj) {
417568
+ // 先尝试解析 JSON 字符串为数组
417569
+ // 对于流式 object_string 消息,content 可能是 JSON 数组字符串,需要先解析
417570
+ let parsedContent;
417571
+ try {
417572
+ const trimmedContent = content.trim();
417573
+ // 尝试解析为 JSON 数组
417574
+ const parsed = JSON.parse(trimmedContent);
417575
+ if (Array.isArray(parsed)) {
417576
+ parsedContent = parsed;
417577
+ } else {
417578
+ // 如果不是数组,尝试作为单个对象解析
417579
+ parsedContent = [
417580
+ parsed
417581
+ ];
417582
+ }
417583
+ } catch (e) {
417584
+ // 解析失败,尝试使用 catchParse(可能是不完整的 JSON)
417585
+ parsedContent = catchParse(content);
417586
+ }
417587
+ if (!parsedContent) {
417131
417588
  return;
417132
417589
  }
417133
- console.log('convertMixContent contentObj', contentObj);
417134
- const itemList = contentObj === null || contentObj === void 0 ? void 0 : contentObj.map((item)=>{
417590
+ console.log('convertMixContent parsedContent', parsedContent);
417591
+ const itemList = parsedContent === null || parsedContent === void 0 ? void 0 : parsedContent.map((item)=>{
417135
417592
  switch(item.type){
417136
417593
  case 'text':
417137
417594
  {
@@ -417200,9 +417657,52 @@ class MessageConverseToCoze {
417200
417657
  }
417201
417658
  }
417202
417659
  }).filter((item)=>!!item);
417660
+ // 合并所有 JSON item 为一个 item
417661
+ // 如果 item_list 中有多个 JSON item,将它们合并为一个 JSON item
417662
+ const jsonItems = itemList.filter((item)=>item.type === types_ContentType.Json);
417663
+ const nonJsonItems = itemList.filter((item)=>item.type !== types_ContentType.Json);
417664
+ let mergedJsonItem;
417665
+ if (jsonItems.length > 0) {
417666
+ // 如果只有一个 JSON item,直接使用它
417667
+ if (jsonItems.length === 1) {
417668
+ mergedJsonItem = jsonItems[0];
417669
+ } else {
417670
+ // 如果有多个 JSON item,合并它们的 json 字段
417671
+ // 如果所有 json 字段都是对象,深度合并它们;否则合并为数组
417672
+ const jsonValues = jsonItems.map((item)=>item.json);
417673
+ const allAreObjects = jsonValues.every((json)=>typeof json === 'object' && json !== null && !Array.isArray(json));
417674
+ let mergedJson;
417675
+ if (allAreObjects) {
417676
+ // 深度合并所有对象
417677
+ mergedJson = jsonValues.reduce((acc, curr)=>{
417678
+ if (typeof acc === 'object' && acc !== null && !Array.isArray(acc) && typeof curr === 'object' && curr !== null && !Array.isArray(curr)) {
417679
+ return this.mergeJsonObjects(acc, curr);
417680
+ }
417681
+ return curr;
417682
+ }, jsonValues[0]);
417683
+ } else {
417684
+ // 如果有一个不是对象,合并为数组
417685
+ mergedJson = jsonValues.length === 1 ? jsonValues[0] : jsonValues;
417686
+ }
417687
+ // 使用第一个 JSON item 的 schema_version
417688
+ const schemaVersion = jsonItems[0].schema_version;
417689
+ mergedJsonItem = {
417690
+ type: types_ContentType.Json,
417691
+ json: mergedJson,
417692
+ schema_version: schemaVersion
417693
+ };
417694
+ }
417695
+ }
417696
+ // 合并所有 item:非 JSON item + 合并后的 JSON item
417697
+ const finalItemList = [
417698
+ ...nonJsonItems
417699
+ ];
417700
+ if (mergedJsonItem) {
417701
+ finalItemList.push(mergedJsonItem);
417702
+ }
417203
417703
  const contentResult = {
417204
- item_list: itemList.filter((item)=>!item.is_refer),
417205
- refer_items: itemList.filter((item)=>item.is_refer)
417704
+ item_list: finalItemList.filter((item)=>!item.is_refer),
417705
+ refer_items: finalItemList.filter((item)=>item.is_refer)
417206
417706
  };
417207
417707
  return JSON.stringify(contentResult);
417208
417708
  }
@@ -417470,11 +417970,19 @@ const useRequestInit = ()=>{
417470
417970
  const updateShortcuts = context_useChatAppStore((s)=>s.updateShortcuts);
417471
417971
  const setIsStartBotVoiceCall = context_useChatAppStore((s)=>s.setIsStartBotVoiceCall);
417472
417972
  const updateBackgroundInfo = context_useChatAppStore((s)=>s.updateBackgroundInfo);
417473
- const { currentConversationInfo, updateCurrentConversationInfo, conversations } = context_useChatAppStore(shallow_useShallow((s)=>({
417474
- currentConversationInfo: s.currentConversationInfo,
417475
- updateCurrentConversationInfo: s.updateCurrentConversationInfo,
417476
- conversations: s.conversations
417973
+ const { updateCurrentConversationInfo } = context_useChatAppStore(shallow_useShallow((s)=>({
417974
+ updateCurrentConversationInfo: s.updateCurrentConversationInfo
417477
417975
  })));
417976
+ // 使用 ref 来存储最新的 currentConversationInfo 和 conversations
417977
+ // 这样可以避免将它们放在依赖数组中,从而避免 title/name 更新时触发重新初始化
417978
+ const currentConversationInfoRef = (0,react.useRef)();
417979
+ const conversationsRef = (0,react.useRef)([]);
417980
+ // 通过 hook 获取值并更新 ref
417981
+ context_useChatAppStore(shallow_useShallow((s)=>{
417982
+ currentConversationInfoRef.current = s.currentConversationInfo;
417983
+ conversationsRef.current = s.conversations;
417984
+ return null; // 不返回任何值,只用于同步 ref
417985
+ }));
417478
417986
  const getMessageListByPairs = useGetMessageListByPairs();
417479
417987
  const connectorId = (chatConfig === null || chatConfig === void 0 ? void 0 : (_chatConfig_auth = chatConfig.auth) === null || _chatConfig_auth === void 0 ? void 0 : _chatConfig_auth.connectorId) || '';
417480
417988
  const { bot_id: botId = '', type: chatType, appInfo } = chatConfig;
@@ -417522,6 +418030,9 @@ const useRequestInit = ()=>{
417522
418030
  const actualSectionId = requestDataConversationInfo.lastSectionId || '';
417523
418031
  // 如果从 openRequestInit 返回了 conversationName,使用它
417524
418032
  const actualConversationName = conversationName || '';
418033
+ // 从 ref 中获取最新的 currentConversationInfo 和 conversations
418034
+ const currentConversationInfo = currentConversationInfoRef.current;
418035
+ const conversations = conversationsRef.current;
417525
418036
  if (actualConversationId) {
417526
418037
  // 检查 currentConversationInfo 是否与实际的 conversationId 一致
417527
418038
  // 如果 currentConversationInfo.id 为空字符串,说明是从空会话创建的,此时不应该更新
@@ -417569,14 +418080,21 @@ const useRequestInit = ()=>{
417569
418080
  });
417570
418081
  }
417571
418082
  } else {
417572
- // 如果 conversationId 一致,更新 sectionId 和 name(如果有)
418083
+ // 如果 conversationId 一致,只更新 sectionId(如果有变化)
418084
+ // 不更新 name,因为 name 会通过 conversation.update 事件自动更新
418085
+ // 这样可以避免 title/name 更新时触发重新初始化
417573
418086
  const updates = {};
417574
418087
  if (currentConversationInfo.last_section_id !== actualSectionId && actualSectionId) {
417575
418088
  updates.last_section_id = actualSectionId;
417576
418089
  }
417577
- if (actualConversationName && currentConversationInfo.name !== actualConversationName) {
417578
- updates.name = actualConversationName;
417579
- }
418090
+ // 移除 name 的更新,因为 name 会通过 conversation.update 事件自动更新
418091
+ // 这样可以避免 title/name 更新时触发重新初始化
418092
+ // if (
418093
+ // actualConversationName &&
418094
+ // currentConversationInfo.name !== actualConversationName
418095
+ // ) {
418096
+ // updates.name = actualConversationName;
418097
+ // }
417580
418098
  if (Object.keys(updates).length > 0) {
417581
418099
  updateCurrentConversationInfo({
417582
418100
  ...currentConversationInfo,
@@ -417630,8 +418148,8 @@ const useRequestInit = ()=>{
417630
418148
  onDefaultHistoryClear,
417631
418149
  chatType,
417632
418150
  appInfo,
417633
- currentConversationInfo,
417634
- conversations,
418151
+ // 不再依赖 currentConversationInfo 和 conversations
418152
+ // 在函数内部通过 getState() 获取最新值,避免 title/name 更新时触发重新初始化
417635
418153
  updateCurrentConversationInfo,
417636
418154
  userInfo,
417637
418155
  openRequestInit
@@ -417975,7 +418493,27 @@ class MessageConverterToSdk {
417975
418493
  const contentType = messageBody.content_type;
417976
418494
  const content = messageBody.query;
417977
418495
  const shortcutCommand = messageBody.shortcut_command;
417978
- return JSON.stringify({
418496
+ // API 要求 parameters 是 string 类型,如果传入的是对象,需要转换为 JSON 字符串
418497
+ // 如果 parameters 是对象,需要递归序列化所有嵌套对象(包括 SETTING 等字段)
418498
+ let parametersString;
418499
+ if (parameters) {
418500
+ if (typeof parameters === 'string') {
418501
+ // 如果已经是字符串,尝试解析并重新序列化,确保内部对象(如 SETTING)也被序列化
418502
+ try {
418503
+ const parsed = JSON.parse(parameters);
418504
+ parametersString = JSON.stringify(parsed);
418505
+ } catch {
418506
+ // 如果解析失败,说明不是有效的 JSON 字符串,直接使用原值
418507
+ parametersString = parameters;
418508
+ }
418509
+ } else if (typeof parameters === 'object') {
418510
+ // 如果是对象,直接序列化(JSON.stringify 会递归序列化所有嵌套对象)
418511
+ parametersString = JSON.stringify(parameters);
418512
+ } else {
418513
+ parametersString = String(parameters);
418514
+ }
418515
+ }
418516
+ const requestBody = {
417979
418517
  bot_id: messageBody.bot_id,
417980
418518
  user_id: userInfo === null || userInfo === void 0 ? void 0 : userInfo.id,
417981
418519
  stream: true,
@@ -417983,10 +418521,11 @@ class MessageConverterToSdk {
417983
418521
  additional_messages: [
417984
418522
  this.convertRequestMessage(contentType, content)
417985
418523
  ],
417986
- parameters,
418524
+ parameters: parametersString,
417987
418525
  shortcut_command: this.convertShortcuts(shortcuts || [], shortcutCommand),
417988
418526
  enable_card: true
417989
- });
418527
+ };
418528
+ return JSON.stringify(requestBody);
417990
418529
  }
417991
418530
  // 替换 chat请求中的 message部分
417992
418531
  convertRequestMessage(contentType, content) {
@@ -419649,7 +420188,7 @@ const src_timeoutPromise = (ms)=>new Promise((resolve)=>{
419649
420188
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
419650
420189
  * See the License for the specific language governing permissions and
419651
420190
  * limitations under the License.
419652
- */ function message_parser_define_property(obj, key, value) {
420191
+ */ /* eslint-disable max-lines */ function message_parser_define_property(obj, key, value) {
419653
420192
  if (key in obj) {
419654
420193
  Object.defineProperty(obj, key, {
419655
420194
  value: value,
@@ -419671,6 +420210,7 @@ const src_timeoutPromise = (ms)=>new Promise((resolve)=>{
419671
420210
 
419672
420211
 
419673
420212
 
420213
+
419674
420214
  // 消息解析,主要用于从服务端获取到消息后,解析成coze能适配的数据结构
419675
420215
  var message_parser_ChunkEvent = /*#__PURE__*/ (/* unused pure expression or super */ null && (function(ChunkEvent) {
419676
420216
  ChunkEvent["ERROR"] = "error";
@@ -419695,7 +420235,8 @@ class MessageParser {
419695
420235
  }
419696
420236
  case "conversation.message.delta":
419697
420237
  {
419698
- const message = this.createMessage(data, false);
420238
+ // conversation.message.delta 不应该累积和合并,应该直接使用新内容
420239
+ const message = this.createMessage(data, false, false);
419699
420240
  if (!message) {
419700
420241
  return;
419701
420242
  }
@@ -419703,7 +420244,8 @@ class MessageParser {
419703
420244
  }
419704
420245
  case "conversation.message.completed":
419705
420246
  {
419706
- const messageResult = this.createMessage(data, true);
420247
+ // conversation.message.completed 不应该累积和合并,应该直接使用新内容
420248
+ const messageResult = this.createMessage(data, true, false);
419707
420249
  // 清理流式 JSON 累积内容
419708
420250
  if (messageResult && typeof messageResult === 'object' && 'data' in messageResult) {
419709
420251
  const messageData = messageResult.data;
@@ -419714,6 +420256,8 @@ class MessageParser {
419714
420256
  if (contentKey) {
419715
420257
  this.streamingJsonContent.delete(contentKey);
419716
420258
  this.lastValidJsonContent.delete(contentKey);
420259
+ this.lastReturnedParsedContent.delete(contentKey);
420260
+ this.lastReturnedAccumulatedContent.delete(contentKey);
419717
420261
  }
419718
420262
  }
419719
420263
  }
@@ -419760,7 +420304,7 @@ class MessageParser {
419760
420304
  }
419761
420305
  }
419762
420306
  createMessage(data) {
419763
- let isComplete = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
420307
+ let isComplete = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false, shouldAccumulate = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
419764
420308
  const dataValue = catchParse(data);
419765
420309
  if (!dataValue) {
419766
420310
  return;
@@ -419771,31 +420315,124 @@ class MessageParser {
419771
420315
  // 处理流式 JSON:当 content_type 是 object_string 且 type 是 answer 时
419772
420316
  const isStreamingJson = dataValue.content_type === 'object_string' && dataValue.type === 'answer';
419773
420317
  if (isStreamingJson && dataValue.content) {
419774
- // 使用 messageId chatId 作为 key 来累积内容
419775
- const contentKey = dataValue.id || dataValue.chat_id || '';
420318
+ // 对于流式 object_string 消息,使用 id 作为 key 来累积内容
420319
+ // 这样可以确保不同 id 的消息不会被合并
420320
+ // 但是,只有 shouldAccumulate 为 true 时才累积(conversation.chat.* 事件)
420321
+ // conversation.message.delta 不应该累积,应该直接使用新内容
420322
+ const contentKey = dataValue.id || '';
419776
420323
  if (contentKey) {
419777
- // 累积流式 JSON 内容
419778
- const accumulatedContent = this.streamingJsonContent.get(contentKey) || '';
419779
- const newContent = accumulatedContent + dataValue.content;
419780
- this.streamingJsonContent.set(contentKey, newContent);
419781
- // 尝试解析累积的内容
419782
- const parsedContent = this.tryParseStreamingJson(newContent);
419783
- if (parsedContent !== null) {
419784
- // 如果解析成功,使用解析后的内容,并保存为最后一次有效内容
419785
- dataValue.content = parsedContent;
419786
- this.lastValidJsonContent.set(contentKey, parsedContent);
420324
+ let newContent;
420325
+ if (shouldAccumulate) {
420326
+ // 累积流式 JSON 内容(conversation.chat.* 事件)
420327
+ const accumulatedContent = this.streamingJsonContent.get(contentKey) || '';
420328
+ newContent = accumulatedContent + dataValue.content;
420329
+ this.streamingJsonContent.set(contentKey, newContent);
419787
420330
  } else {
419788
- // 如果解析失败(JSON 不完整),使用上一次成功解析的内容(如果有)
419789
- // 这样可以避免每次 delta 都创建新消息,导致重复渲染
419790
- const lastValidContent = this.lastValidJsonContent.get(contentKey);
419791
- if (lastValidContent) {
419792
- // 使用上一次成功解析的内容,避免重复渲染
419793
- dataValue.content = lastValidContent;
419794
- } else {
419795
- // 如果完全没有成功解析的内容,使用累积的原始内容
419796
- // 但这样可能会导致 convertMixContent 失败,返回 undefined
419797
- dataValue.content = newContent;
420331
+ // 不累积,直接使用新内容(conversation.message.delta 事件)
420332
+ newContent = dataValue.content;
420333
+ }
420334
+ // 对于流式数组,我们需要传递完整的累积内容,而不是部分解析的内容
420335
+ // 检查数组是否完整(以 ] 结尾)
420336
+ const trimmedContent = newContent.trim();
420337
+ const isArrayComplete = trimmedContent.startsWith('[') && trimmedContent.endsWith(']');
420338
+ if (isArrayComplete) {
420339
+ // 数组完整,尝试解析完整的数组
420340
+ try {
420341
+ const parsed = JSON.parse(trimmedContent);
420342
+ if (Array.isArray(parsed)) {
420343
+ // 解析成功
420344
+ // 如果是 completed 消息,直接使用新的内容,不合并
420345
+ // 如果是 delta 消息,合并数组中元素的 json 字段
420346
+ let mergedArray = parsed;
420347
+ if (!isComplete && shouldAccumulate) {
420348
+ // 只对 delta 消息且 shouldAccumulate 为 true 时进行合并
420349
+ // 获取上一次解析的内容
420350
+ const lastValidContent = this.lastValidJsonContent.get(contentKey);
420351
+ if (lastValidContent) {
420352
+ try {
420353
+ const lastParsed = JSON.parse(lastValidContent);
420354
+ if (Array.isArray(lastParsed) && lastParsed.length > 0 && parsed.length > 0) {
420355
+ // 合并第一个元素的 json 字段
420356
+ const lastFirstItem = lastParsed[0];
420357
+ const currFirstItem = parsed[0];
420358
+ if (typeof lastFirstItem === 'object' && lastFirstItem !== null && typeof currFirstItem === 'object' && currFirstItem !== null && 'json' in lastFirstItem && 'json' in currFirstItem && typeof lastFirstItem.json === 'object' && lastFirstItem.json !== null && typeof currFirstItem.json === 'object' && currFirstItem.json !== null) {
420359
+ // 合并 json 字段(深度合并对象)
420360
+ const mergedJson = this.mergeJsonObjects(lastFirstItem.json, currFirstItem.json);
420361
+ // 创建合并后的第一个元素
420362
+ mergedArray = [
420363
+ {
420364
+ ...currFirstItem,
420365
+ json: mergedJson
420366
+ },
420367
+ ...parsed.slice(1)
420368
+ ];
420369
+ }
420370
+ }
420371
+ } catch (parseError) {
420372
+ // 解析上一次内容失败,使用当前解析的内容
420373
+ // Error is intentionally ignored as we fallback to current parsed content
420374
+ void parseError;
420375
+ }
420376
+ }
420377
+ } else {
420378
+ // completed 消息:直接使用新的内容,不合并
420379
+ }
420380
+ const parsedContent = JSON.stringify(mergedArray);
420381
+ dataValue.content = parsedContent;
420382
+ this.lastValidJsonContent.set(contentKey, parsedContent);
420383
+ } else {
420384
+ // 不是数组,使用原始内容
420385
+ dataValue.content = newContent;
420386
+ }
420387
+ } catch (e) {
420388
+ // 解析失败,可能是多个数组拼接(如 [{...}][{...}]),尝试合并
420389
+ const mergedArray = this.mergeStreamingArrays(trimmedContent);
420390
+ if (mergedArray !== null) {
420391
+ // 如果是 completed 消息,直接使用新的内容,不合并
420392
+ // 如果是 delta 消息,合并数组中元素的 json 字段
420393
+ let finalMergedArray = mergedArray;
420394
+ if (!isComplete && shouldAccumulate) {
420395
+ // 只对 delta 消息且 shouldAccumulate 为 true 时进行合并
420396
+ const lastValidContent = this.lastValidJsonContent.get(contentKey);
420397
+ if (lastValidContent) {
420398
+ try {
420399
+ const lastParsed = JSON.parse(lastValidContent);
420400
+ if (Array.isArray(lastParsed) && lastParsed.length > 0 && mergedArray.length > 0) {
420401
+ // 合并第一个元素的 json 字段
420402
+ const lastFirstItem = lastParsed[0];
420403
+ const currFirstItem = mergedArray[0];
420404
+ if (typeof lastFirstItem === 'object' && lastFirstItem !== null && typeof currFirstItem === 'object' && currFirstItem !== null && 'json' in lastFirstItem && 'json' in currFirstItem && typeof lastFirstItem.json === 'object' && lastFirstItem.json !== null && typeof currFirstItem.json === 'object' && currFirstItem.json !== null) {
420405
+ // 合并 json 字段(深度合并对象)
420406
+ const mergedJson = this.mergeJsonObjects(lastFirstItem.json, currFirstItem.json);
420407
+ // 创建合并后的第一个元素
420408
+ finalMergedArray = [
420409
+ {
420410
+ ...currFirstItem,
420411
+ json: mergedJson
420412
+ },
420413
+ ...mergedArray.slice(1)
420414
+ ];
420415
+ }
420416
+ }
420417
+ } catch (parseError) {
420418
+ // 解析上一次内容失败,使用当前合并的数组
420419
+ // Error is intentionally ignored as we fallback to current merged array
420420
+ void parseError;
420421
+ }
420422
+ }
420423
+ }
420424
+ const parsedContent = JSON.stringify(finalMergedArray);
420425
+ dataValue.content = parsedContent;
420426
+ this.lastValidJsonContent.set(contentKey, parsedContent);
420427
+ } else {
420428
+ // 合并失败,使用原始内容
420429
+ dataValue.content = newContent;
420430
+ }
419798
420431
  }
420432
+ } else {
420433
+ // 数组不完整,使用累积的原始内容
420434
+ // convertMixContent 会尝试解析部分数组,如果失败会返回 undefined
420435
+ dataValue.content = newContent;
419799
420436
  }
419800
420437
  }
419801
420438
  }
@@ -419806,7 +420443,8 @@ class MessageParser {
419806
420443
  // 对于流式 JSON,如果 convertMixContent 返回 undefined(JSON 不完整),
419807
420444
  // 且没有上一次成功解析的内容,不返回消息,避免重复渲染
419808
420445
  if (isStreamingJson && !isComplete && message.content_type === types_ContentType.Mix && (!message.content || message.content === 'undefined')) {
419809
- const contentKey = dataValue.id || dataValue.chat_id || '';
420446
+ // 对于流式 object_string 消息,使用 id 作为 key
420447
+ const contentKey = dataValue.id || '';
419810
420448
  const lastValidContent = contentKey ? this.lastValidJsonContent.get(contentKey) : undefined;
419811
420449
  // 如果没有上一次成功解析的内容,不返回消息
419812
420450
  if (!lastValidContent) {
@@ -419815,15 +420453,30 @@ class MessageParser {
419815
420453
  // 如果有上一次成功解析的内容,使用它
419816
420454
  message.content = lastValidContent;
419817
420455
  }
420456
+ // 对于流式 object_string 消息,检查内容是否真正变化
420457
+ // 如果内容没有变化,且不是完成消息,则不返回消息,避免重复渲染
420458
+ if (isStreamingJson && !isComplete && message.message_id) {
420459
+ // 对于流式 object_string 消息,使用 id 作为 key
420460
+ const contentKey = dataValue.id || '';
420461
+ if (contentKey) {
420462
+ // 比较转换后的 message.content(这是最终传递给 UI 的内容)
420463
+ const currentMessageContent = message.content || '';
420464
+ const lastReturnedMessageContent = this.lastReturnedParsedContent.get(contentKey) || '';
420465
+ // 如果转换后的内容没有变化,不返回消息
420466
+ if (currentMessageContent && lastReturnedMessageContent && currentMessageContent === lastReturnedMessageContent) {
420467
+ // 内容没有变化,不返回消息
420468
+ return;
420469
+ }
420470
+ // 更新上一次返回的转换后的内容
420471
+ if (currentMessageContent) {
420472
+ this.lastReturnedParsedContent.set(contentKey, currentMessageContent);
420473
+ }
420474
+ }
420475
+ }
419818
420476
  if (isComplete && message.content_type === types_ContentType.Text && message.type === 'answer') {
419819
420477
  message.content = '';
419820
- } else if (isComplete && message.content_type === types_ContentType.Mix && message.type === 'answer') {
419821
- message.content = JSON.stringify({
419822
- item_list: []
419823
- });
419824
420478
  }
419825
420479
  message.section_id = message.section_id || this.sectionId;
419826
- console.log('createMessage message', message);
419827
420480
  return {
419828
420481
  event: 'message',
419829
420482
  data: {
@@ -419839,6 +420492,127 @@ class MessageParser {
419839
420492
  };
419840
420493
  }
419841
420494
  /**
420495
+ * 合并两个 JSON 对象(深度合并)
420496
+ * - 如果两个值都是对象,递归合并
420497
+ * - 如果两个值都是字符串,追加字符串
420498
+ * - 如果两个值都是数组,合并数组(按索引合并)
420499
+ * - 否则使用 curr 的值(覆盖)
420500
+ */ mergeJsonObjects(prev, curr) {
420501
+ const merged = {
420502
+ ...prev
420503
+ };
420504
+ for(const key in curr){
420505
+ if (Object.prototype.hasOwnProperty.call(curr, key)) {
420506
+ const prevValue = prev[key];
420507
+ const currValue = curr[key];
420508
+ // 如果两个值都是对象,递归合并
420509
+ if (typeof prevValue === 'object' && prevValue !== null && !Array.isArray(prevValue) && typeof currValue === 'object' && currValue !== null && !Array.isArray(currValue)) {
420510
+ merged[key] = this.mergeJsonObjects(prevValue, currValue);
420511
+ } else if (typeof prevValue === 'string' && typeof currValue === 'string') {
420512
+ // 如果两个值都是字符串,追加字符串
420513
+ merged[key] = prevValue + currValue;
420514
+ } else if (Array.isArray(prevValue) && Array.isArray(currValue)) {
420515
+ // 如果两个值都是数组,按索引合并
420516
+ const mergedArray = [
420517
+ ...prevValue
420518
+ ];
420519
+ for(let i = 0; i < currValue.length; i++){
420520
+ const currItem = currValue[i];
420521
+ if (i < mergedArray.length) {
420522
+ // 如果索引已存在,尝试合并
420523
+ const prevItem = mergedArray[i];
420524
+ if (typeof prevItem === 'object' && prevItem !== null && !Array.isArray(prevItem) && typeof currItem === 'object' && currItem !== null && !Array.isArray(currItem)) {
420525
+ mergedArray[i] = this.mergeJsonObjects(prevItem, currItem);
420526
+ } else {
420527
+ mergedArray[i] = currItem;
420528
+ }
420529
+ } else {
420530
+ // 如果索引超出范围,追加新元素
420531
+ mergedArray.push(currItem);
420532
+ }
420533
+ }
420534
+ merged[key] = mergedArray;
420535
+ } else {
420536
+ // 否则使用 curr 的值(覆盖)
420537
+ merged[key] = currValue;
420538
+ }
420539
+ }
420540
+ }
420541
+ return merged;
420542
+ }
420543
+ /**
420544
+ * 合并多个流式数组(如 [{...}][{...}] 合并为 [{...},{...}])
420545
+ * 返回合并后的数组,如果无法合并则返回 null
420546
+ */ mergeStreamingArrays(content) {
420547
+ if (!content || !content.trim().startsWith('[')) {
420548
+ return null;
420549
+ }
420550
+ const trimmed = content.trim();
420551
+ const arrays = [];
420552
+ let currentIndex = 0;
420553
+ while(currentIndex < trimmed.length){
420554
+ // 跳过空白字符
420555
+ while(currentIndex < trimmed.length && /\s/.test(trimmed[currentIndex])){
420556
+ currentIndex++;
420557
+ }
420558
+ if (currentIndex >= trimmed.length || trimmed[currentIndex] !== '[') {
420559
+ break;
420560
+ }
420561
+ // 找到完整的数组
420562
+ let bracketCount = 0;
420563
+ let inString = false;
420564
+ let escapeNext = false;
420565
+ let arrayEndIndex = -1;
420566
+ for(let i = currentIndex; i < trimmed.length; i++){
420567
+ const char = trimmed[i];
420568
+ if (escapeNext) {
420569
+ escapeNext = false;
420570
+ continue;
420571
+ }
420572
+ if (char === '\\') {
420573
+ escapeNext = true;
420574
+ continue;
420575
+ }
420576
+ if (char === '"') {
420577
+ inString = !inString;
420578
+ continue;
420579
+ }
420580
+ if (inString) {
420581
+ continue;
420582
+ }
420583
+ if (char === '[') {
420584
+ bracketCount++;
420585
+ } else if (char === ']') {
420586
+ bracketCount--;
420587
+ if (bracketCount === 0) {
420588
+ arrayEndIndex = i;
420589
+ break;
420590
+ }
420591
+ }
420592
+ }
420593
+ if (arrayEndIndex > currentIndex) {
420594
+ try {
420595
+ const arrayStr = trimmed.substring(currentIndex, arrayEndIndex + 1);
420596
+ const parsed = JSON.parse(arrayStr);
420597
+ if (Array.isArray(parsed)) {
420598
+ arrays.push(...parsed);
420599
+ } else {
420600
+ arrays.push(parsed);
420601
+ }
420602
+ currentIndex = arrayEndIndex + 1;
420603
+ } catch (parseError) {
420604
+ // 解析失败,停止合并
420605
+ // Error is intentionally ignored as we break the loop
420606
+ void parseError;
420607
+ break;
420608
+ }
420609
+ } else {
420610
+ break;
420611
+ }
420612
+ }
420613
+ return arrays.length > 0 ? arrays : null;
420614
+ }
420615
+ /**
419842
420616
  * 尝试解析流式 JSON 内容
419843
420617
  * 如果 JSON 不完整,返回 null;如果解析成功,返回完整的 JSON 字符串
419844
420618
  */ tryParseStreamingJson(content) {
@@ -420052,6 +420826,10 @@ class MessageParser {
420052
420826
  message_parser_define_property(this, "streamingJsonContent", new Map());
420053
420827
  // 用于存储上一次成功解析的完整 JSON 内容,避免重复渲染
420054
420828
  message_parser_define_property(this, "lastValidJsonContent", new Map());
420829
+ // 用于存储上一次返回的解析后的 JSON 内容,用于检测内容是否真正变化
420830
+ message_parser_define_property(this, "lastReturnedParsedContent", new Map());
420831
+ // 用于存储上一次返回的原始累积内容,用于检测 JSON 不完整时内容是否变化
420832
+ message_parser_define_property(this, "lastReturnedAccumulatedContent", new Map());
420055
420833
  this.conversationId = requestMessageRawBody.conversation_id;
420056
420834
  this.localMessageId = requestMessageRawBody.local_message_id;
420057
420835
  this.sendMessageContent = requestMessageRawBody.query;
@@ -420134,36 +420912,85 @@ class MessageParser {
420134
420912
  const currentConversation = refCurrentConversationInfo.current;
420135
420913
  const updateConversationsFn = refUpdateConversations.current;
420136
420914
  const updateCurrentConversationInfoFn = refUpdateCurrentConversationInfo.current;
420137
- const conversationsList = refConversations.current;
420138
420915
  const { title } = updates;
420139
420916
  if (title !== undefined) {
420140
- // 找到要更新的会话,保留其原有字段
420141
- const existingConversation = conversationsList.find((c)=>c.id === conversation_id);
420917
+ var _refPendingConversationInfo_current;
420142
420918
  // 更新会话列表中的会话名称
420143
420919
  // 注意:PC 端和 Mobile 端都优先使用 title,如果没有 title 则使用 name
420144
420920
  // 对于 App 模式,需要同时更新 name 和 title
420145
420921
  // 对于 Bot 模式,也需要更新 name 和 title(如果存在)
420146
- const updatedConversation = {
420922
+ // 修复:使用函数式更新,确保从最新的store状态中获取并保留所有字段
420923
+ // 这样可以避免因为ref未及时更新而导致的数据丢失问题
420924
+ const updatedConversationObj = {
420147
420925
  id: conversation_id,
420148
420926
  name: title,
420149
420927
  title,
420150
- updated_at: Math.floor(updated_at / 1000),
420151
- // 保留原有字段(如果存在),否则使用默认值
420152
- created_at: (existingConversation === null || existingConversation === void 0 ? void 0 : existingConversation.created_at) ?? Math.floor(updated_at / 1000),
420153
- meta_data: (existingConversation === null || existingConversation === void 0 ? void 0 : existingConversation.meta_data) ?? {},
420154
- last_section_id: (existingConversation === null || existingConversation === void 0 ? void 0 : existingConversation.last_section_id) ?? ''
420928
+ updated_at: Math.floor(updated_at / 1000)
420155
420929
  };
420156
- updateConversationsFn([
420157
- updatedConversation
420158
- ], 'update');
420930
+ if (updateConversationsFn) {
420931
+ updateConversationsFn([
420932
+ updatedConversationObj
420933
+ ], 'update');
420934
+ }
420159
420935
  // 如果当前会话是被更新的会话,同时更新 currentConversationInfo
420160
- if ((currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.id) === conversation_id) {
420161
- updateCurrentConversationInfoFn({
420162
- ...currentConversation,
420163
- name: title,
420164
- // @ts-expect-error: title 字段可能不在类型定义中,但实际需要
420165
- title
420166
- });
420936
+ // 修复:无论ref是否是最新的,都更新currentConversationInfo,确保header能显示title
420937
+ // updateCurrentConversationInfo函数会合并字段,所以即使ref不是最新的,title也会被正确设置
420938
+ // 从conversations列表中查找对应的会话,如果找到说明这个会话存在,可能是当前会话
420939
+ const conversationsList = refConversations.current;
420940
+ const foundConversation = conversationsList.find((c)=>c.id === conversation_id);
420941
+ // 检查是否是当前会话或待处理的会话
420942
+ // 1. 如果 refCurrentConversationInfo.current.id 匹配,说明是当前会话
420943
+ // 2. 如果 refPendingConversationInfo.current.conversationId 匹配,说明是新创建的会话,但 currentConversationInfo 还没有更新
420944
+ const isCurrentConversation = (currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.id) === conversation_id;
420945
+ const isPendingConversation = ((_refPendingConversationInfo_current = refPendingConversationInfo.current) === null || _refPendingConversationInfo_current === void 0 ? void 0 : _refPendingConversationInfo_current.conversationId) === conversation_id;
420946
+ // 如果ref中的id匹配,或者找到了对应的会话,就更新title
420947
+ // 这样可以确保即使ref不是最新的,也能正确更新title
420948
+ // 注意:只有当 conversation_id 与当前会话的 id 匹配时才更新 currentConversationInfo
420949
+ // 这样可以避免在更新其他会话的 title 时触发当前会话的重新初始化
420950
+ if (isCurrentConversation || isPendingConversation) {
420951
+ // 优先使用ref中的currentConversationInfo,保留其所有字段
420952
+ // 只更新 title 和 name,不改变其他字段,避免触发不必要的重新初始化
420953
+ if (currentConversation) {
420954
+ // 只有当 title 或 name 真的发生变化时才更新,避免创建不必要的对象引用
420955
+ if (currentConversation.title !== title || currentConversation.name !== title) {
420956
+ updateCurrentConversationInfoFn({
420957
+ ...currentConversation,
420958
+ name: title,
420959
+ title,
420960
+ updated_at: Math.floor(updated_at / 1000)
420961
+ });
420962
+ }
420963
+ } else if (foundConversation) {
420964
+ // 如果 currentConversation 不存在但找到了会话,使用找到的会话信息
420965
+ // 注意:foundConversation 是 Conversation 类型,不包含 conversationListVisible 和 isLargeWidth
420966
+ // 这些字段只在 currentConversationInfo 中存在,所以使用默认值
420967
+ updateCurrentConversationInfoFn({
420968
+ ...foundConversation,
420969
+ name: title,
420970
+ title,
420971
+ updated_at: Math.floor(updated_at / 1000),
420972
+ // 使用默认值,因为 foundConversation 不包含这些字段
420973
+ conversationListVisible: false,
420974
+ isLargeWidth: false
420975
+ });
420976
+ } else if (isPendingConversation && refPendingConversationInfo.current) {
420977
+ // 如果是待处理的会话,但 currentConversation 和 foundConversation 都不存在
420978
+ // 使用 refPendingConversationInfo 中的信息创建新的 currentConversationInfo
420979
+ const pendingInfo = refPendingConversationInfo.current;
420980
+ // 从 refCurrentConversationInfo 获取保留的字段(如果存在)
420981
+ const existingInfo = refCurrentConversationInfo.current;
420982
+ updateCurrentConversationInfoFn({
420983
+ id: pendingInfo.conversationId,
420984
+ last_section_id: pendingInfo.sectionId,
420985
+ name: title,
420986
+ title,
420987
+ created_at: Math.floor(Date.now() / 1000),
420988
+ updated_at: Math.floor(updated_at / 1000),
420989
+ meta_data: {},
420990
+ conversationListVisible: (existingInfo === null || existingInfo === void 0 ? void 0 : existingInfo.conversationListVisible) ?? false,
420991
+ isLargeWidth: (existingInfo === null || existingInfo === void 0 ? void 0 : existingInfo.isLargeWidth) ?? false
420992
+ });
420993
+ }
420167
420994
  }
420168
420995
  }
420169
420996
  };
@@ -420257,12 +421084,28 @@ class MessageParser {
420257
421084
  conversationName: conversationResult.conversationName
420258
421085
  });
420259
421086
  }
420260
- // 只更新 messageBody 中的 conversation_id
420261
- // 不在这里更新 currentConversationInfo 或调用 setConversationId
420262
- // 这些更新会在消息发送成功后通过消息解析器进行
420263
- // 避免在消息发送前触发状态更新,导致 SDK 重新初始化
421087
+ // 选中新创建的会话:先更新 currentConversationInfo,确保会话被选中
421088
+ // 不立即调用 updateCurrentConversationInfo,避免触发组件卸载
421089
+ // 将在消息流完成(DONE 事件)时更新 currentConversationInfo
421090
+ // 这样可以避免触发 useRequestInit 的依赖变化,防止重新初始化
421091
+ // 会话列表中的新会话已经通过 updateConversations 添加,可以正常显示
421092
+ console.log('onBeforeSendMessage: Conversation created, will update currentConversationInfo after stream completes', {
421093
+ conversationId: conversationResult.conversationId
421094
+ });
421095
+ // 确保 pendingInfo 被设置,以便在 DONE 事件时更新
421096
+ if (refPendingConversationInfo) {
421097
+ refPendingConversationInfo.current = {
421098
+ conversationId: conversationResult.conversationId,
421099
+ sectionId: conversationResult.sectionId || ''
421100
+ };
421101
+ }
421102
+ // 注意:不在这里调用 setConversationId,因为它会触发额外的 updateConversations 调用
421103
+ // 这会导致 useConversationList 的同步逻辑被触发,设置 isSyncing = true
421104
+ // 当真正的 store 更新发生时,isSyncing 仍然是 true,导致 useEffect 被跳过
421105
+ // setConversationId 会在消息流完成时被调用(在 onGetMessageStreamParser 的 DONE 事件中)
421106
+ // 更新 messageBody 中的 conversation_id
420264
421107
  messageBody.conversation_id = conversationId;
420265
- console.log('onBeforeSendMessage: Conversation created, will update state after message sent', {
421108
+ console.log('onBeforeSendMessage: Conversation created and selected, ready to send message', {
420266
421109
  conversationId,
420267
421110
  pendingInfo: refPendingConversationInfo.current
420268
421111
  });
@@ -420278,7 +421121,7 @@ class MessageParser {
420278
421121
  throw new Error('conversationId is required for sending message');
420279
421122
  }
420280
421123
  // 使用计算后的 API URL
420281
- const url = `${finalApiUrl}/v3/chat?conversation_id=${conversationId}`;
421124
+ const url = `${finalApiUrl}/v1/workflows/chat?conversation_id=${conversationId}`;
420282
421125
  // 根据 chatType 选择正确的 parameters 来源
420283
421126
  // App 模式使用 appInfo.parameters,Bot 模式使用 botInfo.parameters
420284
421127
  const parameters = ((_refChatConfig_current = refChatConfig.current) === null || _refChatConfig_current === void 0 ? void 0 : _refChatConfig_current.type) === client_ChatType.APP ? (_refChatConfig_current_appInfo = refChatConfig.current.appInfo) === null || _refChatConfig_current_appInfo === void 0 ? void 0 : _refChatConfig_current_appInfo.parameters : (_refChatConfig_current_botInfo = refChatConfig.current.botInfo) === null || _refChatConfig_current_botInfo === void 0 ? void 0 : _refChatConfig_current_botInfo.parameters;
@@ -420423,6 +421266,8 @@ class MessageParser {
420423
421266
  // 使用 setTimeout 确保在消息发送流程完全完成后更新状态
420424
421267
  setTimeout(()=>{
420425
421268
  if (refUpdateCurrentConversationInfo.current) {
421269
+ // 保留当前的 conversationListVisible 和 isLargeWidth,避免会话列表被收起
421270
+ const currentInfo = refCurrentConversationInfo.current;
420426
421271
  refUpdateCurrentConversationInfo.current({
420427
421272
  id: pendingInfo.conversationId,
420428
421273
  last_section_id: pendingInfo.sectionId,
@@ -420431,8 +421276,10 @@ class MessageParser {
420431
421276
  created_at: Math.floor(Date.now() / 1000),
420432
421277
  updated_at: Math.floor(Date.now() / 1000),
420433
421278
  meta_data: {},
420434
- conversationListVisible: false,
420435
- isLargeWidth: false
421279
+ // 保留当前的 conversationListVisible,避免会话列表被收起
421280
+ conversationListVisible: (currentInfo === null || currentInfo === void 0 ? void 0 : currentInfo.conversationListVisible) ?? false,
421281
+ // 保留当前的 isLargeWidth
421282
+ isLargeWidth: (currentInfo === null || currentInfo === void 0 ? void 0 : currentInfo.isLargeWidth) ?? false
420436
421283
  });
420437
421284
  }
420438
421285
  if (refChatFunc === null || refChatFunc === void 0 ? void 0 : refChatFunc.current) {
@@ -423554,36 +424401,89 @@ const ChatProviderFuncComp = /*#__PURE__*/ (0,react.forwardRef)((param, ref)=>{
423554
424401
  // 监听 currentConversationInfo 的变化,同步更新 chat-area 的 conversationId 和 sectionId
423555
424402
  // 这样当切换会话时,可以避免出现"上下文已清除"提示
423556
424403
  // 注意:必须在消息加载之前更新 sectionId,否则会触发上下文分隔线
424404
+ // 使用 ref 来存储上一次的 id 和 last_section_id,避免 title/name 更新时触发重新初始化
424405
+ const prevConversationIdRef = (0,react.useRef)(undefined);
424406
+ const prevSectionIdRef = (0,react.useRef)(undefined);
424407
+ // 使用 useMemo 来稳定 id 和 last_section_id 的值,避免对象引用变化导致 useEffect 触发
424408
+ const stableConversationId = (0,react.useMemo)(()=>currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.id, [
424409
+ currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.id
424410
+ ]);
424411
+ const stableLastSectionId = (0,react.useMemo)(()=>currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.last_section_id, [
424412
+ currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.last_section_id
424413
+ ]);
424414
+ // 更新 sectionId 的辅助函数
424415
+ const updateSectionIdIfNeeded = useMemoizedFn((targetSectionId, currentSectionId)=>{
424416
+ if (targetSectionId && targetSectionId !== currentSectionId) {
424417
+ const { setLatestSectionId } = useSectionIdStore.getState();
424418
+ setLatestSectionId(targetSectionId);
424419
+ }
424420
+ });
424421
+ // 检查是否需要更新的辅助函数
424422
+ const shouldSkipUpdate = useMemoizedFn((currentId, currentSectionId, refs)=>{
424423
+ const isFirstRun = refs.prevIdRef.current === undefined && refs.prevSectionIdRef.current === undefined;
424424
+ const hasIdChanged = !isFirstRun && currentId !== refs.prevIdRef.current;
424425
+ const hasSectionIdChanged = !isFirstRun && currentSectionId !== refs.prevSectionIdRef.current;
424426
+ if (!isFirstRun && !hasIdChanged && !hasSectionIdChanged) {
424427
+ refs.prevIdRef.current = currentId;
424428
+ refs.prevSectionIdRef.current = currentSectionId;
424429
+ return true;
424430
+ }
424431
+ refs.prevIdRef.current = currentId;
424432
+ refs.prevSectionIdRef.current = currentSectionId;
424433
+ return false;
424434
+ });
424435
+ // 更新 conversationId 的辅助函数
424436
+ const updateConversationId = useMemoizedFn((currentId, currentSectionId, isNewConversationFromEmpty)=>{
424437
+ if (isNewConversationFromEmpty) {
424438
+ // 只更新 chatCore 的 conversationId,不调用 setConversationIdInArea
424439
+ // 这样可以避免触发重新初始化,导致正在进行的请求被中止
424440
+ chatCore === null || chatCore === void 0 ? void 0 : chatCore.updateConversationId(currentId);
424441
+ updateSectionIdIfNeeded(currentSectionId, sectionId);
424442
+ } else {
424443
+ // 正常切换会话,立即更新
424444
+ setConversationIdInArea(currentId);
424445
+ chatCore === null || chatCore === void 0 ? void 0 : chatCore.updateConversationId(currentId);
424446
+ updateSectionIdIfNeeded(currentSectionId, sectionId);
424447
+ }
424448
+ });
423557
424449
  (0,react.useEffect)(()=>{
423558
424450
  var _chatConfig_ui_conversations, _chatConfig_ui;
423559
- if ((currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.id) && ((_chatConfig_ui = chatConfig.ui) === null || _chatConfig_ui === void 0 ? void 0 : (_chatConfig_ui_conversations = _chatConfig_ui.conversations) === null || _chatConfig_ui_conversations === void 0 ? void 0 : _chatConfig_ui_conversations.isNeed)) {
423560
- const currentId = currentConversationInfo.id;
423561
- const currentConversationWithSectionId = currentConversationInfo;
423562
- const currentSectionId = currentConversationInfo.last_section_id || currentConversationWithSectionId.sectionId || '';
423563
- // 先更新 sectionId,再更新 conversationId,确保消息加载时使用正确的 sectionId
423564
- if (currentSectionId && currentSectionId !== sectionId) {
423565
- const { setLatestSectionId } = useSectionIdStore.getState();
423566
- setLatestSectionId(currentSectionId);
423567
- }
423568
- // 只有当 conversationId 发生变化时才更新
423569
- if (currentId !== conversationId) {
423570
- setConversationIdInArea(currentId);
423571
- chatCore === null || chatCore === void 0 ? void 0 : chatCore.updateConversationId(currentId);
423572
- // 如果 conversationId 变化了,确保 sectionId 也同步更新
423573
- if (currentSectionId && currentSectionId !== sectionId) {
423574
- const { setLatestSectionId } = useSectionIdStore.getState();
423575
- setLatestSectionId(currentSectionId);
423576
- }
423577
- }
424451
+ if (!stableConversationId || !((_chatConfig_ui = chatConfig.ui) === null || _chatConfig_ui === void 0 ? void 0 : (_chatConfig_ui_conversations = _chatConfig_ui.conversations) === null || _chatConfig_ui_conversations === void 0 ? void 0 : _chatConfig_ui_conversations.isNeed)) {
424452
+ return;
424453
+ }
424454
+ const currentId = stableConversationId;
424455
+ const currentConversationWithSectionId = currentConversationInfo;
424456
+ const currentSectionId = stableLastSectionId || currentConversationWithSectionId.sectionId || '';
424457
+ // 先更新 sectionId,再更新 conversationId,确保消息加载时使用正确的 sectionId
424458
+ updateSectionIdIfNeeded(currentSectionId, sectionId);
424459
+ // 检查是否需要跳过更新
424460
+ if (shouldSkipUpdate(currentId, currentSectionId, {
424461
+ prevIdRef: prevConversationIdRef,
424462
+ prevSectionIdRef
424463
+ })) {
424464
+ return;
424465
+ }
424466
+ // 只有当 conversationId 发生变化时才更新
424467
+ if (currentId !== conversationId) {
424468
+ // 如果 conversationId 从空字符串或null变为新ID,说明是从空会话创建的新会话
424469
+ // 此时不应该立即调用 setConversationIdInArea,因为它可能触发重新初始化
424470
+ // 导致正在进行的请求被中止
424471
+ // setConversationIdInArea 会在消息流完成时被调用(在 onGetMessageStreamParser 的 DONE 事件中)
424472
+ const isNewConversationFromEmpty = conversationId === '' || conversationId === null;
424473
+ updateConversationId(currentId, currentSectionId, isNewConversationFromEmpty);
423578
424474
  }
423579
424475
  }, [
423580
- currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.id,
423581
- currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.last_section_id,
424476
+ stableConversationId,
424477
+ stableLastSectionId,
423582
424478
  conversationId,
423583
424479
  sectionId,
423584
424480
  setConversationIdInArea,
423585
424481
  chatCore,
423586
- (_chatConfig_ui = chatConfig.ui) === null || _chatConfig_ui === void 0 ? void 0 : (_chatConfig_ui_conversations = _chatConfig_ui.conversations) === null || _chatConfig_ui_conversations === void 0 ? void 0 : _chatConfig_ui_conversations.isNeed
424482
+ (_chatConfig_ui = chatConfig.ui) === null || _chatConfig_ui === void 0 ? void 0 : (_chatConfig_ui_conversations = _chatConfig_ui.conversations) === null || _chatConfig_ui_conversations === void 0 ? void 0 : _chatConfig_ui_conversations.isNeed,
424483
+ updateSectionIdIfNeeded,
424484
+ shouldSkipUpdate,
424485
+ updateConversationId,
424486
+ currentConversationInfo
423587
424487
  ]);
423588
424488
  useUpdateConversationNameByMessage();
423589
424489
  const regenerateMessageByUserMessageId = useRegenerateMessageByUserMessageId();
@@ -435889,6 +436789,7 @@ var header_pc_index_module_update = injectStylesIntoStyleTag_default()(header_pc
435889
436789
 
435890
436790
 
435891
436791
 
436792
+
435892
436793
  const ChatHeader = (param)=>{
435893
436794
  let { iconUrl = coze_logo_namespaceObject, title = 'Coze Bot', extra, theme, isShowConversations, isNeedLogo = true } = param;
435894
436795
  const { headerTopLeftOps } = useChatOpInfo();
@@ -435904,18 +436805,20 @@ const ChatHeader = (param)=>{
435904
436805
  const [renameInputValue, setRenameInputValue] = (0,react.useState)('');
435905
436806
  const [isRenameLoading, setIsRenameLoading] = (0,react.useState)(false);
435906
436807
  // 使用 useMemo 计算显示文本,确保 title 变化时能及时更新
435907
- const conversationTitle = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.title;
435908
- const conversationName = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.name;
435909
436808
  const conversationId = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.id;
435910
436809
  const displayTitle = (0,react.useMemo)(()=>{
435911
436810
  if (conversationId === '') {
435912
436811
  return title;
435913
436812
  }
435914
- return conversationTitle || conversationName || title;
436813
+ if (!currentConversationInfo) {
436814
+ return title;
436815
+ }
436816
+ // 使用 getConversationDisplayName 处理 UUID 检查
436817
+ const conversationItem = currentConversationInfo;
436818
+ return getConversationDisplayName(conversationItem) || title;
435915
436819
  }, [
435916
436820
  conversationId,
435917
- conversationTitle,
435918
- conversationName,
436821
+ currentConversationInfo,
435919
436822
  title
435920
436823
  ]);
435921
436824
  // 打开重命名弹窗
@@ -436141,6 +437044,7 @@ var header_mobile_index_module_update = injectStylesIntoStyleTag_default()(heade
436141
437044
 
436142
437045
 
436143
437046
 
437047
+
436144
437048
  const ChatHeaderMobile = (param)=>{
436145
437049
  let { iconUrl = coze_logo_namespaceObject, title = 'Coze Bot', extra, theme, isShowConversations, isNeedLogo = true } = param;
436146
437050
  const { headerTopLeftOps } = useChatOpInfo();
@@ -436156,18 +437060,20 @@ const ChatHeaderMobile = (param)=>{
436156
437060
  const [renameInputValue, setRenameInputValue] = (0,react.useState)('');
436157
437061
  const [isRenameLoading, setIsRenameLoading] = (0,react.useState)(false);
436158
437062
  // 使用 useMemo 计算显示文本,确保 title 变化时能及时更新
436159
- const conversationTitle = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.title;
436160
- const conversationName = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.name;
436161
437063
  const conversationId = currentConversationInfo === null || currentConversationInfo === void 0 ? void 0 : currentConversationInfo.id;
436162
437064
  const displayTitle = (0,react.useMemo)(()=>{
436163
437065
  if (conversationId === '') {
436164
437066
  return title;
436165
437067
  }
436166
- return conversationTitle || conversationName || title;
437068
+ if (!currentConversationInfo) {
437069
+ return title;
437070
+ }
437071
+ // 使用 getConversationDisplayName 处理 UUID 检查
437072
+ const conversationItem = currentConversationInfo;
437073
+ return getConversationDisplayName(conversationItem) || title;
436167
437074
  }, [
436168
437075
  conversationId,
436169
- conversationTitle,
436170
- conversationName,
437076
+ currentConversationInfo,
436171
437077
  title
436172
437078
  ]);
436173
437079
  // 打开重命名弹窗
@@ -437610,7 +438516,20 @@ const use_core_manager_useCoreManager = (props)=>{
437610
438516
  bodyData.additional_messages = bodyDataOld.additional_messages || [];
437611
438517
  bodyData.connector_id = bodyDataOld.connector_id;
437612
438518
  bodyData.workflow_id = refProps === null || refProps === void 0 ? void 0 : (_refProps_current = refProps.current) === null || _refProps_current === void 0 ? void 0 : (_refProps_current_workflow = _refProps_current.workflow) === null || _refProps_current_workflow === void 0 ? void 0 : _refProps_current_workflow.id;
437613
- bodyData.parameters = refProps === null || refProps === void 0 ? void 0 : (_refProps_current1 = refProps.current) === null || _refProps_current1 === void 0 ? void 0 : (_refProps_current_workflow1 = _refProps_current1.workflow) === null || _refProps_current_workflow1 === void 0 ? void 0 : _refProps_current_workflow1.parameters;
438519
+ // parameters 应该是一个对象,但 SETTING 字段的值需要序列化为字符串
438520
+ const workflowParameters = refProps === null || refProps === void 0 ? void 0 : (_refProps_current1 = refProps.current) === null || _refProps_current1 === void 0 ? void 0 : (_refProps_current_workflow1 = _refProps_current1.workflow) === null || _refProps_current_workflow1 === void 0 ? void 0 : _refProps_current_workflow1.parameters;
438521
+ if (workflowParameters && typeof workflowParameters === 'object') {
438522
+ // 复制 parameters 对象,只序列化 SETTING 字段的值
438523
+ const processedParameters = {
438524
+ ...workflowParameters
438525
+ };
438526
+ if (processedParameters.SETTING && typeof processedParameters.SETTING === 'object') {
438527
+ processedParameters.SETTING = JSON.stringify(processedParameters.SETTING);
438528
+ }
438529
+ bodyData.parameters = processedParameters;
438530
+ } else {
438531
+ bodyData.parameters = workflowParameters;
438532
+ }
437614
438533
  bodyData.version = (refProps === null || refProps === void 0 ? void 0 : (_refProps_current2 = refProps.current) === null || _refProps_current2 === void 0 ? void 0 : (_refProps_current_project = _refProps_current2.project) === null || _refProps_current_project === void 0 ? void 0 : _refProps_current_project.version) || undefined;
437615
438534
  bodyData.execute_mode = (refProps === null || refProps === void 0 ? void 0 : (_refProps_current3 = refProps.current) === null || _refProps_current3 === void 0 ? void 0 : (_refProps_current_project1 = _refProps_current3.project) === null || _refProps_current_project1 === void 0 ? void 0 : _refProps_current_project1.mode) === 'draft' ? 'DEBUG' : undefined;
437616
438535
  bodyData.app_id = (refProps === null || refProps === void 0 ? void 0 : (_refProps_current4 = refProps.current) === null || _refProps_current4 === void 0 ? void 0 : (_refProps_current_project2 = _refProps_current4.project) === null || _refProps_current_project2 === void 0 ? void 0 : _refProps_current_project2.type) === 'app' ? refProps === null || refProps === void 0 ? void 0 : (_refProps_current5 = refProps.current) === null || _refProps_current5 === void 0 ? void 0 : (_refProps_current_project3 = _refProps_current5.project) === null || _refProps_current_project3 === void 0 ? void 0 : _refProps_current_project3.id : undefined;
@@ -437626,8 +438545,13 @@ const use_core_manager_useCoreManager = (props)=>{
437626
438545
  customized_suggest_prompt: (_refAppData_current2 = refAppData.current) === null || _refAppData_current2 === void 0 ? void 0 : (_refAppData_current_suggestPromoteInfo1 = _refAppData_current2.suggestPromoteInfo) === null || _refAppData_current_suggestPromoteInfo1 === void 0 ? void 0 : _refAppData_current_suggestPromoteInfo1.customizedSuggestPrompt
437627
438546
  } : undefined;
437628
438547
  requestConfig.body = JSON.stringify(bodyData);
437629
- // 使用计算后的 finalApiUrl
437630
- requestConfig.url = `${finalApiUrl}/v1/workflows/chat`;
438548
+ // 只有在URL还没有被其他hook设置时才设置URL
438549
+ // 如果URL已经包含conversation_id参数(说明已经被useSendMessageAdapter设置了),保留原URL
438550
+ const urlHasConversationId = new URL(requestConfig.url, 'http://dummy.com').searchParams.has('conversation_id');
438551
+ if (!urlHasConversationId) {
438552
+ // 使用计算后的 finalApiUrl
438553
+ requestConfig.url = `${finalApiUrl}/v1/workflows/chat`;
438554
+ }
437631
438555
  requestConfig.headers.push(...Object.entries(((_refProps_current9 = refProps.current) === null || _refProps_current9 === void 0 ? void 0 : (_refProps_current_workflow2 = _refProps_current9.workflow) === null || _refProps_current_workflow2 === void 0 ? void 0 : _refProps_current_workflow2.header) || {}));
437632
438556
  return {
437633
438557
  ...requestConfig