浏览代码

[Fix R4-R4-2] extractMarkdownLinks 加 MD_LINK_CACHE_MAX=200 LRU,流式期胶囊链接提取不再每 token 全量 lexer

lizuo 2 周之前
父节点
当前提交
3b45e485be
共有 1 个文件被更改,包括 20 次插入 和 0 次删除
  1. 20 0
      src/views/ventAI/manageAssistent/lib/markdown.ts

+ 20 - 0
src/views/ventAI/manageAssistent/lib/markdown.ts

@@ -105,8 +105,22 @@ export const renderMarkdownCached = (
 // 从 markdown 文本中提取链接(与 renderMarkdown 共用同一套 marked 解析,
 // 仅收集 link token,代码块/图片等不会被误收)
 
+// [审计-第四轮 R4-2] extractMarkdownLinks 的按内容 LRU:
+// 原行为:每次调用都 marked.lexer 全量解析文本。流式期间胶囊 computed(buildCapsuleLinks 等)
+//         每个 token 都会全量重扫所有 AI 消息,lexer 是全模块最重的解析,CPU 随会话长度线性放大。
+// 现行为:与 renderMarkdownCached 同型(MD_CACHE_MAX=200 的 FIFO Map),以原文为 key 记忆化;
+//         纯函数(入参相同输出相同),命中即返回上次结果。
+// 为什么:流式期每 token 的链接提取从 O(全部 AI 消息全文) 降为 O(1)(未变化消息全部命中)。
+// 注意:返回的数组被缓存共享,调用方只读遍历(lib/capsule.ts 仅 for...of),不得原地修改。
+const MD_LINK_CACHE_MAX = 200;
+
+const mdLinkCache = new Map<string, Array<{ title: string; href: string }>>();
+
 export const extractMarkdownLinks = (text: string): Array<{ title: string; href: string }> => {
   if (!text) return [];
+  const cached = mdLinkCache.get(text);
+  if (cached !== undefined) return cached;
+
   const links: Array<{ title: string; href: string }> = [];
   try {
     const tokens = marked.lexer(text);
@@ -122,6 +136,12 @@ export const extractMarkdownLinks = (text: string): Array<{ title: string; href:
   } catch {
     // 文本解析异常时返回已收集到的部分链接
   }
+  // 简单 FIFO 淘汰,缓存条目数有上限(与 mdCache 同策略)
+  if (mdLinkCache.size >= MD_LINK_CACHE_MAX) {
+    const oldestKey = mdLinkCache.keys().next().value as string | undefined;
+    if (oldestKey !== undefined) mdLinkCache.delete(oldestKey);
+  }
+  mdLinkCache.set(text, links);
   return links;
 };