From cbb00d4729243e4949b3c921fc2f94cb03ca8aaa Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期五, 27 三月 2026 18:47:43 +0800
Subject: [PATCH] #
---
src/main/webapp/views/ai/diagnosis.html | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 206 insertions(+), 9 deletions(-)
diff --git a/src/main/webapp/views/ai/diagnosis.html b/src/main/webapp/views/ai/diagnosis.html
index 70686ae..59aaacd 100644
--- a/src/main/webapp/views/ai/diagnosis.html
+++ b/src/main/webapp/views/ai/diagnosis.html
@@ -374,6 +374,18 @@
margin-bottom: 0;
}
+ .markdown-body > details.think-block + h1,
+ .markdown-body > details.think-block + h2,
+ .markdown-body > details.think-block + h3,
+ .markdown-body > details.think-block + h4,
+ .markdown-body > details.think-block + p,
+ .markdown-body > details.think-block + ul,
+ .markdown-body > details.think-block + ol,
+ .markdown-body > details.think-block + pre,
+ .markdown-body > details.think-block + blockquote {
+ margin-top: 0;
+ }
+
.markdown-body p {
margin: 0 0 10px;
}
@@ -616,7 +628,7 @@
<footer class="composer-panel">
<div class="composer-head">
<div><strong>鍚� AI 鍔╂墜鎻愰棶</strong></div>
- <div>{{ currentChatId ? '浼氳瘽宸茬粦瀹�' : '涓存椂浼氳瘽' }}</div>
+ <div>{{ currentRunTokenSummary || currentChatTokenSummary || (currentChatId ? '浼氳瘽宸茬粦瀹�' : '涓存椂浼氳瘽') }}</div>
</div>
<el-input
v-model="userInput"
@@ -644,7 +656,7 @@
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
- <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
+ <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script src="../../static/js/marked.min.js"></script>
<script src="../../static/js/purify.min.js"></script>
<script>
@@ -679,6 +691,8 @@
lastRenderTs: 0,
renderIntervalMs: 120,
stepChars: 6,
+ runTokenUsage: null,
+ sessionTokenBase: null,
userInput: '',
autoScrollThreshold: 80,
chats: [],
@@ -731,13 +745,54 @@
var current = this.findChat(this.currentChatId);
if (!current && this.resetting) return '鏂板缓浼氳瘽锛岀瓑寰呴鏉℃秷鎭�';
if (!current) return '浼氳瘽 ' + this.currentChatId;
- return this.chatLabel(current);
+ var tokenText = this.sessionTokenSummaryText(current, '绱');
+ return tokenText ? (this.chatLabel(current) + ' 路 ' + tokenText) : this.chatLabel(current);
+ },
+ currentChatTokenSummary: function() {
+ var current = this.findChat(this.currentChatId);
+ return this.currentChatId ? this.sessionTokenSummaryText(current, '褰撳墠浼氳瘽') : '';
+ },
+ currentRunTokenSummary: function() {
+ if (this.currentChatId) return '';
+ return this.runTokenUsage ? this.tokenSummaryText(this.runTokenUsage, '鏈') : '';
},
inlinePrompts: function() {
return this.promptPresets.slice(1);
}
},
methods: {
+ stateHost: function() {
+ try {
+ if (window.top && window.top !== window) {
+ return window.top;
+ }
+ } catch (e) {}
+ return window;
+ },
+ getAssistantState: function() {
+ var host = this.stateHost();
+ if (!host.__WCS_AI_ASSISTANT_STATE__) {
+ host.__WCS_AI_ASSISTANT_STATE__ = {};
+ }
+ return host.__WCS_AI_ASSISTANT_STATE__;
+ },
+ restoreAssistantState: function() {
+ var state = this.getAssistantState();
+ return {
+ chatId: state.currentChatId || '',
+ resetting: state.resetting === true
+ };
+ },
+ persistAssistantState: function() {
+ var state = this.getAssistantState();
+ state.currentChatId = this.currentChatId || '';
+ state.resetting = this.resetting === true;
+ },
+ clearAssistantState: function() {
+ var state = this.getAssistantState();
+ state.currentChatId = '';
+ state.resetting = false;
+ },
authHeaders: function() {
var token = localStorage.getItem('token');
return token ? { token: token } : {};
@@ -770,12 +825,21 @@
}
return DOMPurify.sanitize(marked.parse(source));
},
+ composeAssistantMarkdown: function(content, reasoningContent) {
+ var answer = content || '';
+ var reasoning = reasoningContent || '';
+ if (!reasoning) {
+ return answer;
+ }
+ return '<think>\n' + reasoning + '\n</think>\n\n' + answer;
+ },
loadChats: function(preferKeepCurrent) {
var self = this;
fetch(baseUrl + '/ai/diagnose/chats', { headers: self.authHeaders() })
.then(function(r) { return r.json(); })
.then(function(arr) {
self.chats = Array.isArray(arr) ? arr : [];
+ self.syncSessionTokenState();
if (preferKeepCurrent && self.currentChatId) return;
if (!self.currentChatId && self.sortedChats.length > 0) {
self.openChat(self.sortedChats[0].chatId);
@@ -801,7 +865,76 @@
},
chatOptionLabel: function(chat) {
if (!chat) return '鏈懡鍚嶄細璇�';
- return this.chatLabel(chat) + ' 路 ' + (chat.size || 0) + ' 鏉� 路 ' + this.chatUpdatedAt(chat);
+ var suffix = this.sessionTokenSummaryText(chat, '绱');
+ return this.chatLabel(chat) + ' 路 ' + (chat.size || 0) + ' 鏉� 路 ' + this.chatUpdatedAt(chat) + (suffix ? (' 路 ' + suffix) : '');
+ },
+ numericValue: function(value) {
+ if (value === null || value === undefined || value === '') return 0;
+ var num = Number(value);
+ return isNaN(num) ? 0 : num;
+ },
+ buildTokenSnapshot: function(prompt, completion, total) {
+ return {
+ prompt: this.numericValue(prompt),
+ completion: this.numericValue(completion),
+ total: this.numericValue(total)
+ };
+ },
+ sessionTokenSnapshot: function(chat) {
+ return this.buildTokenSnapshot(
+ chat ? chat.sumPromptTokens : 0,
+ chat ? chat.sumCompletionTokens : 0,
+ chat ? chat.sumTotalTokens : 0
+ );
+ },
+ expectedSessionTokenSnapshot: function() {
+ if (!this.currentChatId || !this.runTokenUsage) {
+ return null;
+ }
+ var base = this.sessionTokenBase || this.buildTokenSnapshot(0, 0, 0);
+ return this.buildTokenSnapshot(
+ base.prompt + this.numericValue(this.runTokenUsage.promptTokens),
+ base.completion + this.numericValue(this.runTokenUsage.completionTokens),
+ base.total + this.numericValue(this.runTokenUsage.totalTokens)
+ );
+ },
+ mergedSessionTokenSnapshot: function(chat) {
+ var snapshot = this.sessionTokenSnapshot(chat);
+ var expected = this.expectedSessionTokenSnapshot();
+ if (!expected) {
+ return snapshot;
+ }
+ if (snapshot.total >= expected.total) {
+ return snapshot;
+ }
+ return expected;
+ },
+ syncSessionTokenState: function() {
+ if (!this.currentChatId || !this.runTokenUsage) {
+ return;
+ }
+ var current = this.findChat(this.currentChatId);
+ var snapshot = this.sessionTokenSnapshot(current);
+ var expected = this.expectedSessionTokenSnapshot();
+ if (expected && snapshot.total >= expected.total) {
+ this.runTokenUsage = null;
+ this.sessionTokenBase = null;
+ }
+ },
+ tokenSummaryText: function(source, prefix) {
+ if (!source) return '';
+ var total = this.numericValue(source.totalTokens != null ? source.totalTokens : source.lastTotalTokens);
+ if (!total) return '';
+ var prompt = this.numericValue(source.promptTokens != null ? source.promptTokens : source.lastPromptTokens);
+ var completion = this.numericValue(source.completionTokens != null ? source.completionTokens : source.lastCompletionTokens);
+ var label = prefix || '涓婃';
+ return label + ' tokens ' + total + '锛堣緭' + prompt + ' / 鍑�' + completion + '锛�';
+ },
+ sessionTokenSummaryText: function(chat, prefix) {
+ var snapshot = this.mergedSessionTokenSnapshot(chat);
+ if (!snapshot || !snapshot.total) return '';
+ var label = prefix || '褰撳墠浼氳瘽';
+ return label + ' tokens ' + snapshot.total + '锛堣緭' + snapshot.prompt + ' / 鍑�' + snapshot.completion + '锛�';
},
chatUpdatedAt: function(chat) {
if (!chat || !chat.updatedAt) return '鍒氬垰鍒涘缓';
@@ -825,6 +958,10 @@
openChat: function(chatId) {
if (!chatId || this.streaming) return;
this.currentChatId = chatId;
+ this.runTokenUsage = null;
+ this.sessionTokenBase = null;
+ this.resetting = false;
+ this.persistAssistantState();
this.switchChat();
},
switchChat: function() {
@@ -841,10 +978,11 @@
for (var i = 0; i < arr.length; i++) {
var m = arr[i] || {};
if (m.role === 'assistant') {
+ var md = self.composeAssistantMarkdown(m.content || '', m.reasoningContent || '');
msgs.push({
role: 'assistant',
- md: m.content || '',
- html: self.renderMarkdown(m.content || '', false),
+ md: md,
+ html: self.renderMarkdown(md, false),
ts: self.nowStr()
});
} else {
@@ -858,6 +996,7 @@
self.messages = msgs;
self.pendingText = '';
self.resetting = false;
+ self.persistAssistantState();
self.$nextTick(function() { self.scrollToBottom(true); });
})
.catch(function() {
@@ -869,7 +1008,10 @@
if (this.streaming) return;
this.currentChatId = Date.now() + '_' + Math.random().toString(36).substr(2, 8);
this.resetting = true;
+ this.runTokenUsage = null;
+ this.sessionTokenBase = this.buildTokenSnapshot(0, 0, 0);
this.clear();
+ this.persistAssistantState();
},
deleteChat: function() {
var self = this;
@@ -881,8 +1023,11 @@
.then(function(r) { return r.json(); })
.then(function(ok) {
if (ok === true) {
+ self.clearAssistantState();
self.currentChatId = '';
self.clear();
+ self.runTokenUsage = null;
+ self.sessionTokenBase = null;
self.loadChats(false);
}
})
@@ -930,8 +1075,11 @@
if (this.streaming) return;
var message = (this.userInput || '').trim();
if (!message) return;
+ var current = this.findChat(this.currentChatId);
+ this.sessionTokenBase = this.sessionTokenSnapshot(current);
this.loading = true;
this.streaming = true;
+ this.runTokenUsage = null;
this.messages.push({ role: 'user', text: message, ts: this.nowStr() });
this.appendAssistantPlaceholder();
this.scrollToBottom(true);
@@ -939,12 +1087,19 @@
var url = baseUrl + '/ai/diagnose/askStream?prompt=' + encodeURIComponent(message);
if (this.currentChatId) url += '&chatId=' + encodeURIComponent(this.currentChatId);
if (this.resetting) url += '&reset=true';
+ this.persistAssistantState();
this.source = new EventSource(url);
var self = this;
this.source.onopen = function() {
self.loading = false;
};
+ this.source.addEventListener('token_usage', function(e) {
+ if (!e || !e.data) return;
+ try {
+ self.runTokenUsage = JSON.parse(e.data);
+ } catch (ignore) {}
+ });
this.source.onmessage = function(e) {
if (!e || !e.data) return;
var chunk = (e.data || '').replace(/\\n/g, '\n');
@@ -960,20 +1115,43 @@
};
this.userInput = '';
this.resetting = false;
+ this.persistAssistantState();
},
start: function() {
if (this.streaming) return;
- this.clear();
+ var prompt = '瀵瑰綋鍓嶇郴缁熻繘琛屽贰妫�锛屽鏋滄湁寮傚父鎯呭喌灏辫繘琛岃缁嗙殑鍒嗘瀽锛屽鏋滄病鏈夊紓甯告儏鍐靛垯褰撴垚涓�娆℃鏌�';
+ var current = this.findChat(this.currentChatId);
+ this.sessionTokenBase = this.sessionTokenSnapshot(current);
this.loading = true;
this.streaming = true;
+ this.runTokenUsage = null;
+ this.messages.push({ role: 'user', text: prompt, ts: this.nowStr() });
this.appendAssistantPlaceholder();
this.scrollToBottom(true);
var self = this;
- this.source = new EventSource(baseUrl + '/ai/diagnose/runAiStream');
+ var url = baseUrl + '/ai/diagnose/runAiStream';
+ var params = [];
+ if (this.currentChatId) {
+ params.push('chatId=' + encodeURIComponent(this.currentChatId));
+ }
+ if (this.resetting) {
+ params.push('reset=true');
+ }
+ if (params.length > 0) {
+ url += '?' + params.join('&');
+ }
+ this.persistAssistantState();
+ this.source = new EventSource(url);
this.source.onopen = function() {
self.loading = false;
};
+ this.source.addEventListener('token_usage', function(e) {
+ if (!e || !e.data) return;
+ try {
+ self.runTokenUsage = JSON.parse(e.data);
+ } catch (ignore) {}
+ });
this.source.onmessage = function(e) {
if (!e || !e.data) return;
var chunk = (e.data || '').replace(/\\n/g, '\n');
@@ -987,6 +1165,8 @@
this.source.onerror = function() {
self.stop();
};
+ this.resetting = false;
+ this.persistAssistantState();
},
ensureTyping: function() {
if (this.typingTimer) return;
@@ -1044,12 +1224,29 @@
clear: function() {
this.messages = [];
this.pendingText = '';
+ if (!this.currentChatId) {
+ this.sessionTokenBase = null;
+ }
}
},
mounted: function() {
- this.loadChats(false);
+ var restored = this.restoreAssistantState();
+ if (restored.chatId) {
+ this.currentChatId = restored.chatId;
+ this.resetting = restored.resetting;
+ if (this.resetting) {
+ this.clear();
+ } else {
+ this.switchChat();
+ }
+ this.loadChats(true);
+ return;
+ }
+ this.newChat();
+ this.loadChats(true);
},
beforeDestroy: function() {
+ this.persistAssistantState();
this.stop(true);
}
});
--
Gitblit v1.9.1