/** * 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 = `
${title}
${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; }