`;
}
} else {
// 普通文本/Markdown/LaTeX
// Only show the logo if there is NO content, NO reasoning, and NO tool calls.
// If there is reasoning or tool calls, they serve as the "activity indicator".
const isPurelyEmpty = (!m.content || String(m.content).trim() === '') && !m.reasoningContent && !m.toolCallHtml;
if (m.role === 'assistant' && isPurelyEmpty) {
// Determine the correct path for the logo based on the current page
const isHistoryDetail = window.location.pathname.includes('/history_detail.html');
const logoPath = isHistoryDetail ? '../../public/pure.svg' : 'public/pure.svg';
renderedContent = `
`;
}
}
// ReAct Visualization Block
let reactVizBlock = '';
if (m.reactLog && m.reactLog.length > 0) {
const vizId = `react-viz-${index}`;
// Create a container for the visualization
// Note: The actual visualization will be rendered by the ReActVisualization class
// We just provide the container here.
// To make it work with the static HTML string return, we might need to trigger the render after insertion.
// However, since we are returning HTML string, we can't easily bind the instance here.
// A better approach for this specific architecture might be to render the static HTML structure
// that matches what ReActVisualization produces, or use a placeholder and hydrate it later.
// Let's try to render a static snapshot of the ReAct log if available
let stepsHtml = '';
m.reactLog.forEach((step, i) => {
let icon = '';
let title = '';
let typeClass = '';
let content = '';
if (step.type === 'thought') {
icon = 'carbon:idea';
title = `Thought ${step.iteration || i+1}`;
typeClass = 'step-thought';
content = step.content;
} else if (step.type === 'action') {
icon = 'carbon:tools';
title = `Action ${step.iteration || i+1}`;
typeClass = 'step-action';
content = `Tool: ${step.tool}\nInput: ${JSON.stringify(step.params, null, 2)}`;
} else if (step.type === 'observation') {
icon = 'carbon:view';
title = `Observation ${step.iteration || i+1}`;
typeClass = 'step-observation';
content = typeof step.result === 'string' ? step.result : JSON.stringify(step.result, null, 2);
if (content.length > 500) content = content.slice(0, 500) + '... (truncated)';
}
if (content) {
// Escape HTML and preserve newlines
content = window.ChatbotUtils.escapeHtml(content);
content = content.replace(/\n/g, ' ');
stepsHtml += `
${title}
${content}
`;
}
});
if (stepsHtml) {
reactVizBlock = `
ReAct Reasoning Engine
Completed
${stepsHtml}
`;
}
}
// 工具调用块 (Legacy or Fallback)
let toolCallBlock = '';
if (m.toolCallHtml && !reactVizBlock) {
toolCallBlock = m.toolCallHtml;
}
const actionButtons = this._createActionButtonsHTML('assistant', index);
// Phase 3: 复制、导出等快捷操作按钮
let existingActions = '';
if (USE_EVENT_DELEGATION) {
existingActions = `