mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-31 08:07:29 +02:00
Add files via upload
This commit is contained in:
+114
-5
@@ -2164,6 +2164,97 @@ function showCopySuccess(button) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Claude extended thinking 内部尾缀(与后端 DisplayReasoningContent 一致,UI 不展示) */
|
||||||
|
const CLAUDE_REASONING_UI_SUFFIX = '\n---CSAI_CLAUDE_THINKING_BLOCKS---\n';
|
||||||
|
|
||||||
|
function normalizeReasoningContentForDisplay(text) {
|
||||||
|
if (text == null) return '';
|
||||||
|
let s = String(text).trim();
|
||||||
|
if (!s) return '';
|
||||||
|
const idx = s.lastIndexOf(CLAUDE_REASONING_UI_SUFFIX);
|
||||||
|
if (idx >= 0) {
|
||||||
|
s = s.slice(0, idx).trim();
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMessageReasoningContent(messageIdOrEl, reasoningContent) {
|
||||||
|
const el = typeof messageIdOrEl === 'string' ? document.getElementById(messageIdOrEl) : messageIdOrEl;
|
||||||
|
if (!el || !el.dataset) return;
|
||||||
|
const rc = normalizeReasoningContentForDisplay(reasoningContent);
|
||||||
|
if (rc) {
|
||||||
|
el.dataset.reasoningContent = rc;
|
||||||
|
} else {
|
||||||
|
delete el.dataset.reasoningContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMessageReasoningContent(messageIdOrEl) {
|
||||||
|
const el = typeof messageIdOrEl === 'string' ? document.getElementById(messageIdOrEl) : messageIdOrEl;
|
||||||
|
if (!el || !el.dataset) return '';
|
||||||
|
return normalizeReasoningContentForDisplay(el.dataset.reasoningContent || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function reasoningTextAlreadyInProcessDetails(processDetails, rc) {
|
||||||
|
if (!rc) return true;
|
||||||
|
const list = Array.isArray(processDetails) ? processDetails : [];
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const d = list[i];
|
||||||
|
if (!d) continue;
|
||||||
|
const et = d.eventType || '';
|
||||||
|
if (et !== 'reasoning_chain' && et !== 'thinking') continue;
|
||||||
|
const msg = normalizeReasoningContentForDisplay(d.message || '');
|
||||||
|
if (!msg) continue;
|
||||||
|
if (msg === rc || msg.includes(rc) || rc.includes(msg)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 合并 messages.reasoningContent 与 process_details 中的 reasoning_chain,两者都读、都展示(去重后) */
|
||||||
|
function mergeMessageReasoningContentIntoProcessDetails(processDetails, reasoningContent) {
|
||||||
|
const rc = normalizeReasoningContentForDisplay(reasoningContent);
|
||||||
|
const details = Array.isArray(processDetails) ? processDetails.slice() : [];
|
||||||
|
if (!rc || reasoningTextAlreadyInProcessDetails(details, rc)) {
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
details.push({
|
||||||
|
eventType: 'reasoning_chain',
|
||||||
|
message: rc,
|
||||||
|
data: { source: 'message.reasoningContent' }
|
||||||
|
});
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function syncAssistantReasoningContentFromServer(backendMessageId, domAssistantId) {
|
||||||
|
if (!backendMessageId || !domAssistantId || !currentConversationId || typeof apiFetch !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const convRes = await apiFetch(`/api/conversations/${encodeURIComponent(currentConversationId)}?include_process_details=0`);
|
||||||
|
const conv = await convRes.json().catch(() => ({}));
|
||||||
|
if (!convRes.ok || !Array.isArray(conv.messages)) return;
|
||||||
|
const msg = conv.messages.find((m) => m && String(m.id) === String(backendMessageId));
|
||||||
|
if (!msg || !msg.reasoningContent) return;
|
||||||
|
setMessageReasoningContent(domAssistantId, msg.reasoningContent);
|
||||||
|
const pdRes = await apiFetch(`/api/messages/${encodeURIComponent(String(backendMessageId))}/process-details`);
|
||||||
|
const pdJson = await pdRes.json().catch(() => ({}));
|
||||||
|
const details = pdRes.ok && Array.isArray(pdJson.processDetails) ? pdJson.processDetails : [];
|
||||||
|
if (typeof renderProcessDetails === 'function') {
|
||||||
|
renderProcessDetails(domAssistantId, details);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('syncAssistantReasoningContentFromServer failed', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.normalizeReasoningContentForDisplay = normalizeReasoningContentForDisplay;
|
||||||
|
window.setMessageReasoningContent = setMessageReasoningContent;
|
||||||
|
window.getMessageReasoningContent = getMessageReasoningContent;
|
||||||
|
window.mergeMessageReasoningContentIntoProcessDetails = mergeMessageReasoningContentIntoProcessDetails;
|
||||||
|
window.syncAssistantReasoningContentFromServer = syncAssistantReasoningContentFromServer;
|
||||||
|
|
||||||
/** 相邻且类型/正文/data 完全一致的过程详情只保留一条(与后端去重一致,避免时间线叠多条相同块) */
|
/** 相邻且类型/正文/data 完全一致的过程详情只保留一条(与后端去重一致,避免时间线叠多条相同块) */
|
||||||
function dedupeConsecutiveProcessDetailRows(details) {
|
function dedupeConsecutiveProcessDetailRows(details) {
|
||||||
if (!Array.isArray(details) || details.length < 2) {
|
if (!Array.isArray(details) || details.length < 2) {
|
||||||
@@ -2282,20 +2373,27 @@ function renderProcessDetails(messageId, processDetails) {
|
|||||||
detailsContainer.appendChild(contentDiv);
|
detailsContainer.appendChild(contentDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// processDetails === null 表示“尚未加载(懒加载)”
|
// processDetails === null 表示“尚未加载(懒加载)”;messages.reasoningContent 可先展示
|
||||||
const isLazyNotLoaded = (processDetails === null);
|
const isLazyNotLoaded = (processDetails === null);
|
||||||
if (isLazyNotLoaded) {
|
const reasoningFromMessage = getMessageReasoningContent(messageElement);
|
||||||
|
if (isLazyNotLoaded && !reasoningFromMessage) {
|
||||||
detailsContainer.dataset.lazyNotLoaded = '1';
|
detailsContainer.dataset.lazyNotLoaded = '1';
|
||||||
detailsContainer.dataset.loaded = '0';
|
detailsContainer.dataset.loaded = '0';
|
||||||
timeline.innerHTML = '<div class="progress-timeline-empty">' +
|
timeline.innerHTML = '<div class="progress-timeline-empty">' +
|
||||||
(typeof window.t === 'function' ? window.t('chat.expandDetail') : '展开详情') +
|
(typeof window.t === 'function' ? window.t('chat.expandDetail') : '展开详情') +
|
||||||
'(点击后加载)</div>';
|
'(点击后加载)</div>';
|
||||||
// 默认折叠
|
|
||||||
timeline.classList.remove('expanded');
|
timeline.classList.remove('expanded');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
detailsContainer.dataset.lazyNotLoaded = '0';
|
if (isLazyNotLoaded) {
|
||||||
detailsContainer.dataset.loaded = '1';
|
detailsContainer.dataset.lazyNotLoaded = '1';
|
||||||
|
detailsContainer.dataset.loaded = '0';
|
||||||
|
processDetails = [];
|
||||||
|
} else {
|
||||||
|
detailsContainer.dataset.lazyNotLoaded = '0';
|
||||||
|
detailsContainer.dataset.loaded = '1';
|
||||||
|
}
|
||||||
|
processDetails = mergeMessageReasoningContentIntoProcessDetails(processDetails, reasoningFromMessage);
|
||||||
processDetails = dedupeConsecutiveProcessDetailRows(processDetails);
|
processDetails = dedupeConsecutiveProcessDetailRows(processDetails);
|
||||||
if (typeof window.coalesceProcessDetailsToolPairs === 'function') {
|
if (typeof window.coalesceProcessDetailsToolPairs === 'function') {
|
||||||
processDetails = window.coalesceProcessDetailsToolPairs(processDetails);
|
processDetails = window.coalesceProcessDetailsToolPairs(processDetails);
|
||||||
@@ -2426,6 +2524,14 @@ function renderProcessDetails(messageId, processDetails) {
|
|||||||
}
|
}
|
||||||
addTimelineItem(timeline, eventType, timelineOpts);
|
addTimelineItem(timeline, eventType, timelineOpts);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isLazyNotLoaded && reasoningFromMessage) {
|
||||||
|
const lazyHint = document.createElement('div');
|
||||||
|
lazyHint.className = 'progress-timeline-empty progress-timeline-lazy-hint';
|
||||||
|
lazyHint.textContent = (typeof window.t === 'function' ? window.t('chat.expandDetail') : '展开详情') +
|
||||||
|
'(点击后加载完整过程详情)';
|
||||||
|
timeline.appendChild(lazyHint);
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否有错误或取消事件,如果有,确保详情默认折叠(但仍有待审批 HITL 时保持展开,由 restoreHitlInlineForConversation 处理)
|
// 检查是否有错误或取消事件,如果有,确保详情默认折叠(但仍有待审批 HITL 时保持展开,由 restoreHitlInlineForConversation 处理)
|
||||||
const hasPendingHitlInDetails = processDetails.some(d => d && d.eventType === 'hitl_interrupt');
|
const hasPendingHitlInDetails = processDetails.some(d => d && d.eventType === 'hitl_interrupt');
|
||||||
@@ -3193,6 +3299,9 @@ async function loadConversation(conversationId) {
|
|||||||
attachDeleteTurnButton(messageEl);
|
attachDeleteTurnButton(messageEl);
|
||||||
}
|
}
|
||||||
if (msg.role === 'assistant') {
|
if (msg.role === 'assistant') {
|
||||||
|
if (messageEl && msg.reasoningContent) {
|
||||||
|
setMessageReasoningContent(messageEl, msg.reasoningContent);
|
||||||
|
}
|
||||||
const hasField = msg && Object.prototype.hasOwnProperty.call(msg, 'processDetails');
|
const hasField = msg && Object.prototype.hasOwnProperty.call(msg, 'processDetails');
|
||||||
renderProcessDetails(messageId, hasField ? (msg.processDetails || []) : null);
|
renderProcessDetails(messageId, hasField ? (msg.processDetails || []) : null);
|
||||||
if (msg.processDetails && msg.processDetails.length > 0) {
|
if (msg.processDetails && msg.processDetails.length > 0) {
|
||||||
|
|||||||
@@ -2429,6 +2429,11 @@ function handleStreamEvent(event, progressElement, progressId,
|
|||||||
const respMid = responseData.messageId;
|
const respMid = responseData.messageId;
|
||||||
if (respMid) {
|
if (respMid) {
|
||||||
applyBackendMessageIdToAssistantDom(assistantIdFinal, respMid);
|
applyBackendMessageIdToAssistantDom(assistantIdFinal, respMid);
|
||||||
|
if (typeof window.syncAssistantReasoningContentFromServer === 'function') {
|
||||||
|
setTimeout(function () {
|
||||||
|
window.syncAssistantReasoningContentFromServer(respMid, assistantIdFinal);
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -2098,9 +2098,14 @@ function webshellAiConvListSelect(conn, convId, messagesContainer, listEl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
messagesContainer.appendChild(div);
|
messagesContainer.appendChild(div);
|
||||||
if (role === 'assistant' && msg.processDetails && msg.processDetails.length > 0) {
|
if (role === 'assistant') {
|
||||||
var block = renderWebshellProcessDetailsBlock(msg.processDetails, true);
|
var wsMergedDetails = (typeof window.mergeMessageReasoningContentIntoProcessDetails === 'function')
|
||||||
if (block) messagesContainer.appendChild(block);
|
? window.mergeMessageReasoningContentIntoProcessDetails(msg.processDetails || [], msg.reasoningContent)
|
||||||
|
: (msg.processDetails || []);
|
||||||
|
if (wsMergedDetails.length > 0) {
|
||||||
|
var block = renderWebshellProcessDetailsBlock(wsMergedDetails, true);
|
||||||
|
if (block) messagesContainer.appendChild(block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (list.length === 0) {
|
if (list.length === 0) {
|
||||||
@@ -3023,9 +3028,14 @@ function loadWebshellAiHistory(conn, messagesContainer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
messagesContainer.appendChild(div);
|
messagesContainer.appendChild(div);
|
||||||
if (role === 'assistant' && msg.processDetails && msg.processDetails.length > 0) {
|
if (role === 'assistant') {
|
||||||
var block = renderWebshellProcessDetailsBlock(msg.processDetails, true);
|
var wsHistMerged = (typeof window.mergeMessageReasoningContentIntoProcessDetails === 'function')
|
||||||
if (block) messagesContainer.appendChild(block);
|
? window.mergeMessageReasoningContentIntoProcessDetails(msg.processDetails || [], msg.reasoningContent)
|
||||||
|
: (msg.processDetails || []);
|
||||||
|
if (wsHistMerged.length > 0) {
|
||||||
|
var block = renderWebshellProcessDetailsBlock(wsHistMerged, true);
|
||||||
|
if (block) messagesContainer.appendChild(block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (list.length === 0) {
|
if (list.length === 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user