#
Junjie
2 天以前 338f3b81425ab96d8c856909a775124af5365e3c
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;
    }
@@ -680,6 +692,7 @@
          renderIntervalMs: 120,
          stepChars: 6,
          runTokenUsage: null,
          sessionTokenBase: null,
          userInput: '',
          autoScrollThreshold: 80,
          chats: [],
@@ -732,14 +745,15 @@
          var current = this.findChat(this.currentChatId);
          if (!current && this.resetting) return '新建会话,等待首条消息';
          if (!current) return '会话 ' + this.currentChatId;
          var tokenText = this.tokenSummaryText(current);
          var tokenText = this.sessionTokenSummaryText(current, '累计');
          return tokenText ? (this.chatLabel(current) + ' · ' + tokenText) : this.chatLabel(current);
        },
        currentChatTokenSummary: function() {
          var current = this.findChat(this.currentChatId);
          return current ? this.tokenSummaryText(current) : '';
          return this.currentChatId ? this.sessionTokenSummaryText(current, '当前会话') : '';
        },
        currentRunTokenSummary: function() {
          if (this.currentChatId) return '';
          return this.runTokenUsage ? this.tokenSummaryText(this.runTokenUsage, '本次') : '';
        },
        inlinePrompts: function() {
@@ -811,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);
@@ -842,13 +865,61 @@
        },
        chatOptionLabel: function(chat) {
          if (!chat) return '未命名会话';
          var suffix = this.tokenSummaryText(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 '';
@@ -858,6 +929,12 @@
          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 '刚刚创建';
@@ -882,6 +959,7 @@
          if (!chatId || this.streaming) return;
          this.currentChatId = chatId;
          this.runTokenUsage = null;
          this.sessionTokenBase = null;
          this.resetting = false;
          this.persistAssistantState();
          this.switchChat();
@@ -900,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 {
@@ -930,6 +1009,7 @@
          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();
        },
@@ -946,6 +1026,8 @@
                self.clearAssistantState();
                self.currentChatId = '';
                self.clear();
                self.runTokenUsage = null;
                self.sessionTokenBase = null;
                self.loadChats(false);
              }
            })
@@ -993,6 +1075,8 @@
          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;
@@ -1035,15 +1119,30 @@
        },
        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;
          };
@@ -1066,6 +1165,8 @@
          this.source.onerror = function() {
            self.stop();
          };
          this.resetting = false;
          this.persistAssistantState();
        },
        ensureTyping: function() {
          if (this.typingTimer) return;
@@ -1123,6 +1224,9 @@
        clear: function() {
          this.messages = [];
          this.pendingText = '';
          if (!this.currentChatId) {
            this.sessionTokenBase = null;
          }
        }
      },
      mounted: function() {