smart-chat-append.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /**
  2. * Func 标签:smart-chat-append
  3. *
  4. * 约定:src/pages/processing/func/ 目录下每个文件名就是一个"可用标签/能力"。
  5. * 本文件用于声明该标签存在(供文档/提示词/后续动态加载使用)。
  6. *
  7. * 语义:智能合并历史聊天记录和当前聊天记录,自动检测并去除连续重合部分后返回新的聊天记录字符串。
  8. * 输入和输出都是 JSON 字符串格式(参考 chat-history.txt 格式)。
  9. */
  10. export const tagName = 'smart-chat-append';
  11. export const schema = {
  12. description: '智能合并历史聊天记录和当前聊天记录,自动检测并去除连续重合部分后返回新的聊天记录字符串(JSON格式)。',
  13. inputs: {
  14. history: '历史聊天记录的 JSON 字符串',
  15. current: '当前聊天记录的 JSON 字符串',
  16. },
  17. outputs: {
  18. result: '合并去重后的聊天记录 JSON 字符串',
  19. },
  20. };
  21. /**
  22. * 解析 chat-history.txt 格式的 JSON 字符串为条目数组
  23. * 格式:{"data":"时间戳","friend":"消息","me":"消息"},允许重复键
  24. * @param {string} jsonString - JSON 字符串
  25. * @returns {Array<{type: string, value: string}>}
  26. */
  27. function parseChatHistoryJson(jsonString) {
  28. const entries = [];
  29. if (!jsonString || typeof jsonString !== 'string') {
  30. return entries;
  31. }
  32. try {
  33. // 由于JSON不支持重复键,我们需要从文本解析每一行
  34. const lines = jsonString.split('\n');
  35. for (const line of lines) {
  36. // 匹配格式:\t"key":"value", 或 \t"key":"value" 或 "key":"value"
  37. const match = line.match(/^\s*"([^"]+)":\s*"([^"]*)"\s*,?\s*$/);
  38. if (match) {
  39. entries.push({ type: match[1], value: match[2] });
  40. }
  41. }
  42. } catch (e) {
  43. // 解析失败,返回空数组
  44. return entries;
  45. }
  46. return entries;
  47. }
  48. /**
  49. * 将条目数组格式化为 chat-history.txt 格式的 JSON 字符串
  50. * @param {Array<{type: string, value: string}>} entries - 条目数组
  51. * @returns {string} 格式化的 JSON 字符串
  52. */
  53. function formatChatHistoryJson(entries) {
  54. const formattedLines = [];
  55. formattedLines.push('{');
  56. for (let i = 0; i < entries.length; i++) {
  57. const item = entries[i];
  58. const key = JSON.stringify(item.type);
  59. const value = JSON.stringify(item.value);
  60. const comma = i < entries.length - 1 ? ',' : '';
  61. formattedLines.push(`\t${key}:${value}${comma}`);
  62. }
  63. formattedLines.push('}');
  64. return formattedLines.join('\n');
  65. }
  66. /**
  67. * 查找两个条目数组之间的连续重合部分
  68. * 从 historyEntries 的末尾和 currentEntries 的开头查找连续匹配的条目
  69. * @param {Array<{type: string, value: string}>} historyEntries - 历史记录条目数组
  70. * @param {Array<{type: string, value: string}>} currentEntries - 当前记录条目数组
  71. * @returns {number} 重合部分的长度(连续匹配的条目数量),如果没有重合返回 0
  72. */
  73. function findOverlapLength(historyEntries, currentEntries) {
  74. if (historyEntries.length === 0 || currentEntries.length === 0) {
  75. return 0;
  76. }
  77. // 从 historyEntries 的末尾开始,尝试匹配 currentEntries 的开头
  78. // 尝试的重合长度从 min(historyEntries.length, currentEntries.length) 到 1
  79. const maxOverlap = Math.min(historyEntries.length, currentEntries.length);
  80. for (let overlapLen = maxOverlap; overlapLen > 0; overlapLen--) {
  81. // 检查 historyEntries 的最后 overlapLen 个条目是否与 currentEntries 的前 overlapLen 个条目完全相同
  82. let isMatch = true;
  83. for (let i = 0; i < overlapLen; i++) {
  84. const historyIdx = historyEntries.length - overlapLen + i;
  85. const currentIdx = i;
  86. const historyEntry = historyEntries[historyIdx];
  87. const currentEntry = currentEntries[currentIdx];
  88. // 比较 type 和 value 是否完全相同
  89. if (historyEntry.type !== currentEntry.type || historyEntry.value !== currentEntry.value) {
  90. isMatch = false;
  91. break;
  92. }
  93. }
  94. if (isMatch) {
  95. return overlapLen;
  96. }
  97. }
  98. return 0;
  99. }
  100. /**
  101. * 执行智能合并聊天记录
  102. * 智能去重:先检查 chatHistoryMessage 的最后几行和 currentChatMessage 的最初几行是否有连续重合部分
  103. * 如果有重合,删除 currentChatMessage 的重合部分,再用剩余部分和原来的 chatHistoryMessage 合并
  104. * @param {Object} params - 参数对象
  105. * @param {string} params.history - 历史聊天记录的 JSON 字符串
  106. * @param {string} params.current - 当前聊天记录的 JSON 字符串
  107. * @returns {Promise<{success: boolean, error?: string, result?: string}>}
  108. */
  109. export async function executeSmartChatAppend({ history, current }) {
  110. try {
  111. // 如果历史记录为空字符串,直接返回当前记录
  112. if (!history || history.trim() === '') {
  113. const result = typeof current === 'string' ? current : formatChatHistoryJson(parseChatHistoryJson(current || ''));
  114. return {
  115. success: true,
  116. result: result
  117. };
  118. }
  119. // 解析历史记录
  120. const historyEntries = parseChatHistoryJson(history || '');
  121. // 解析当前记录
  122. const currentEntries = parseChatHistoryJson(current || '');
  123. // 如果历史记录解析后为空,直接返回当前记录
  124. if (historyEntries.length === 0) {
  125. const result = formatChatHistoryJson(currentEntries);
  126. return {
  127. success: true,
  128. result: result
  129. };
  130. }
  131. // 如果当前记录为空,直接返回历史记录
  132. if (currentEntries.length === 0) {
  133. const result = formatChatHistoryJson(historyEntries);
  134. return {
  135. success: true,
  136. result: result
  137. };
  138. }
  139. // 查找重合部分:从 historyEntries 的末尾和 currentEntries 的开头查找连续重合的条目
  140. const overlapLength = findOverlapLength(historyEntries, currentEntries);
  141. // 如果有重合部分,删除 currentEntries 中的重合部分
  142. const remainingCurrentEntries = overlapLength > 0
  143. ? currentEntries.slice(overlapLength)
  144. : currentEntries;
  145. // 合并历史记录和剩余的当前记录
  146. const mergedEntries = [...historyEntries, ...remainingCurrentEntries];
  147. // 格式化为 JSON 字符串
  148. const result = formatChatHistoryJson(mergedEntries);
  149. return {
  150. success: true,
  151. result: result
  152. };
  153. } catch (error) {
  154. return {
  155. success: false,
  156. error: error.message || '合并聊天记录失败'
  157. };
  158. }
  159. }