perf(hub-ai): reduce CPU load during trading coach chat

Cache chat context, parallelize exchange fetches, skip fund history writes, defer rolling summary to a background thread, and cache markdown rendering on the client.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-14 01:59:43 +08:00
parent 28a23008f3
commit 467d160f4d
5 changed files with 148 additions and 39 deletions
+29 -8
View File
@@ -3623,14 +3623,32 @@
addAiChatPendingFiles(imageFiles);
}
function renderHubMarkdown(text) {
const AI_CHAT_MAX_ATTACHMENTS = 3;
let aiChatPendingFiles = [];
const aiChatMdCache = new Map();
const AI_CHAT_MD_CACHE_MAX = 120;
function renderHubMarkdown(text, cacheKey) {
const raw = String(text || "");
if (typeof window !== "undefined" && window.AiReviewRender && window.AiReviewRender.renderMarkdown) {
return window.AiReviewRender.renderMarkdown(raw);
if (cacheKey && aiChatMdCache.has(cacheKey)) {
return aiChatMdCache.get(cacheKey);
}
return esc(raw)
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
.replace(/\n/g, "<br>");
let html;
if (typeof window !== "undefined" && window.AiReviewRender && window.AiReviewRender.renderMarkdown) {
html = window.AiReviewRender.renderMarkdown(raw);
} else {
html = esc(raw)
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
.replace(/\n/g, "<br>");
}
if (cacheKey) {
if (aiChatMdCache.size >= AI_CHAT_MD_CACHE_MAX) {
const firstKey = aiChatMdCache.keys().next().value;
if (firstKey != null) aiChatMdCache.delete(firstKey);
}
aiChatMdCache.set(cacheKey, html);
}
return html;
}
function scrollAiChatToEnd() {
@@ -3716,7 +3734,9 @@
!isUser &&
!isThinking &&
/^(AI 调用失败|AI 生成失败)/.test(String(content || "").trim());
const bubbleInner = isUser || isThinking ? esc(content || "") : renderHubMarkdown(content || "");
const mdKey =
!isUser && !isThinking && opts.cacheKey ? String(opts.cacheKey) : "";
const bubbleInner = isUser || isThinking ? esc(content || "") : renderHubMarkdown(content || "", mdKey);
const mdCls = !isUser && !isThinking ? " ai-result-md" : "";
const attList = Array.isArray(attachments) ? attachments : [];
const attHtml = attList.length
@@ -3768,6 +3788,7 @@
box.innerHTML = `<p class="ai-placeholder">${hint}</p>`;
return;
}
const sessionId = session && session.id ? String(session.id) : "local";
let html = msgs
.map((m, idx) =>
renderAiChatRow(
@@ -3775,7 +3796,7 @@
m.content || "",
null,
m.attachments,
{ botMode, msgIdx: idx }
{ botMode, msgIdx: idx, cacheKey: sessionId + ":" + idx }
)
)
.join("");