/**
* ReAct Visualization UI
* Renders the thought process of the ReAct Engine
*/
class ReActVisualization {
constructor(containerId) {
this.container = document.getElementById(containerId);
if (!this.container) {
console.error(`ReActVisualization: Container #${containerId} not found`);
return;
}
this.stepsContainer = null;
this.statusEl = null;
this.init();
}
init() {
this.container.innerHTML = '';
this.container.className = 'react-viz-container';
// Header
const header = document.createElement('div');
header.className = 'react-viz-header';
header.innerHTML = `
ReAct Reasoning Engine
Idle
`;
this.container.appendChild(header);
// Steps Container
this.stepsContainer = document.createElement('div');
this.stepsContainer.className = 'react-steps-container';
this.container.appendChild(this.stepsContainer);
this.statusEl = this.container.querySelector('#react-status');
}
reset() {
if (this.stepsContainer) {
this.stepsContainer.innerHTML = '';
}
this.updateStatus('Idle', 'react-status-idle');
}
updateStatus(text, className) {
if (this.statusEl) {
this.statusEl.textContent = text;
this.statusEl.className = `react-status-badge ${className}`;
}
}
/**
* Bind to a ReActEngine instance
* @param {ReActEngine} engine
*/
bind(engine) {
// Session Start
engine.on('session_start', (data) => {
this.reset();
this.updateStatus('Thinking...', 'react-status-thinking');
this.addSystemMessage(`Goal: ${data.question}`);
});
// Iteration Start
engine.on('iteration_start', (data) => {
this.updateStatus(`Step ${data.iteration}/${data.maxIterations}: Thinking`, 'react-status-thinking');
});
// Reasoning Complete (Thought)
engine.on('reasoning_complete', (data) => {
if (data.thought) {
this.addStep(data.iteration, 'thought', data.thought);
}
});
// Tool Call Start (Action)
engine.on('tool_call_start', (data) => {
this.updateStatus(`Step ${data.iteration}: Executing ${data.tool}`, 'react-status-executing');
const paramsStr = typeof data.params === 'string' ? data.params : JSON.stringify(data.params, null, 2);
this.addStep(data.iteration, 'action', `Tool: ${data.tool}\nInput: ${paramsStr}`);
});
// Tool Call Complete (Observation)
engine.on('tool_call_complete', (data) => {
const resultStr = typeof data.result === 'string' ? data.result : JSON.stringify(data.result, null, 2);
// Truncate long observations for display
const displayResult = resultStr.length > 500 ? resultStr.slice(0, 500) + '... (truncated)' : resultStr;
this.addStep(data.iteration, 'observation', displayResult);
});
// Final Answer
engine.on('final_answer', (data) => {
this.updateStatus('Completed', 'react-status-completed');
this.addStep('Final', 'answer', data.answer);
});
// Error
engine.on('error', (data) => {
this.updateStatus('Error', 'react-status-error');
this.addSystemMessage(`Error: ${data.error}`, 'text-red-500');
});
// Context Pruned
engine.on('context_pruned', (data) => {
this.addSystemMessage(`Context pruned: ${data.before} -> ${data.after} tokens`);
});
}
addStep(stepNum, type, content) {
const stepEl = document.createElement('div');
stepEl.className = 'react-step-item';
let icon = '';
let title = '';
let typeClass = '';
switch (type) {
case 'thought':
icon = 'carbon:idea';
title = `Thought ${stepNum}`;
typeClass = 'step-thought';
break;
case 'action':
icon = 'carbon:tools';
title = `Action ${stepNum}`;
typeClass = 'step-action';
break;
case 'observation':
icon = 'carbon:view';
title = `Observation ${stepNum}`;
typeClass = 'step-observation';
break;
case 'answer':
icon = 'carbon:checkmark-filled';
title = 'Final Answer';
typeClass = 'step-answer';
break;
default:
typeClass = '';
}
stepEl.classList.add(typeClass);
// Format content
const formattedContent = this.formatContent(content);
stepEl.innerHTML = `
${formattedContent}
`;
this.stepsContainer.appendChild(stepEl);
// Auto scroll
this.stepsContainer.scrollTop = this.stepsContainer.scrollHeight;
}
addSystemMessage(text) {
const msg = document.createElement('div');
msg.className = 'react-system-message';
msg.textContent = text;
this.stepsContainer.appendChild(msg);
this.stepsContainer.scrollTop = this.stepsContainer.scrollHeight;
}
formatContent(text) {
if (!text) return '';
// Basic escaping to avoid XSS while preserving display
let safe = text.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
// Highlight code blocks roughly
safe = safe.replace(/```([\s\S]*?)```/g, '$1');
safe = safe.replace(/`([^`]+)`/g, '$1');
return safe;
}
}
// Export
if (typeof window !== 'undefined') {
window.ReActVisualization = ReActVisualization;
}