let currentEventSource = null; function createTask() { const promptInput = document.getElementById('prompt-input'); const prompt = promptInput.value.trim(); if (!prompt) { alert("Please enter a valid prompt"); promptInput.focus(); return; } if (currentEventSource) { currentEventSource.close(); currentEventSource = null; } const container = document.getElementById('task-container'); container.innerHTML = '
Initializing task...
'; document.getElementById('input-container').classList.add('bottom'); fetch('/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt }) }) .then(response => { if (!response.ok) { return response.json().then(err => { throw new Error(err.detail || 'Request failed') }); } return response.json(); }) .then(data => { if (!data.task_id) { throw new Error('Invalid task ID'); } setupSSE(data.task_id); loadHistory(); }) .catch(error => { container.innerHTML = `
Error: ${error.message}
`; console.error('Failed to create task:', error); }); } function setupSSE(taskId) { let retryCount = 0; const maxRetries = 3; const retryDelay = 2000; function connect() { const eventSource = new EventSource(`/tasks/${taskId}/events`); currentEventSource = eventSource; const container = document.getElementById('task-container'); let heartbeatTimer = setInterval(() => { container.innerHTML += '
·
'; }, 5000); const pollInterval = setInterval(() => { fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Polling failed:', error); }); }, 10000); if (!eventSource._listenersAdded) { eventSource._listenersAdded = true; let lastResultContent = ''; eventSource.addEventListener('status', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.querySelector('.loading')?.remove(); container.classList.add('active'); const welcomeMessage = document.querySelector('.welcome-message'); if (welcomeMessage) { welcomeMessage.style.display = 'none'; } let stepContainer = container.querySelector('.step-container'); if (!stepContainer) { container.innerHTML = '
'; stepContainer = container.querySelector('.step-container'); } // Save result content if (data.steps && data.steps.length > 0) { // Iterate through all steps, find the last result type for (let i = data.steps.length - 1; i >= 0; i--) { if (data.steps[i].type === 'result') { lastResultContent = data.steps[i].result; break; } } } // Parse and display each step with proper formatting stepContainer.innerHTML = data.steps.map(step => { const content = step.result; const timestamp = new Date().toLocaleTimeString(); return `
${getEventIcon(step.type)} [${timestamp}] ${getEventLabel(step.type)}:
${content}
`; }).join(''); // Auto-scroll to bottom container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); } catch (e) { console.error('Status update failed:', e); } }); // Add handler for think event eventSource.addEventListener('think', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.querySelector('.loading')?.remove(); let stepContainer = container.querySelector('.step-container'); if (!stepContainer) { container.innerHTML = '
'; stepContainer = container.querySelector('.step-container'); } const content = data.result; const timestamp = new Date().toLocaleTimeString(); const step = document.createElement('div'); step.className = 'step-item think'; step.innerHTML = `
${getEventIcon('think')} [${timestamp}] ${getEventLabel('think')}:
${content}
`; stepContainer.appendChild(step); container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Update task status fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Status update failed:', error); }); } catch (e) { console.error('Think event handling failed:', e); } }); // Add handler for tool event eventSource.addEventListener('tool', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.querySelector('.loading')?.remove(); let stepContainer = container.querySelector('.step-container'); if (!stepContainer) { container.innerHTML = '
'; stepContainer = container.querySelector('.step-container'); } const content = data.result; const timestamp = new Date().toLocaleTimeString(); const step = document.createElement('div'); step.className = 'step-item tool'; step.innerHTML = `
${getEventIcon('tool')} [${timestamp}] ${getEventLabel('tool')}:
${content}
`; stepContainer.appendChild(step); container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Update task status fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Status update failed:', error); }); } catch (e) { console.error('Tool event handling failed:', e); } }); // Add handler for act event eventSource.addEventListener('act', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.querySelector('.loading')?.remove(); let stepContainer = container.querySelector('.step-container'); if (!stepContainer) { container.innerHTML = '
'; stepContainer = container.querySelector('.step-container'); } const content = data.result; const timestamp = new Date().toLocaleTimeString(); const step = document.createElement('div'); step.className = 'step-item act'; step.innerHTML = `
${getEventIcon('act')} [${timestamp}] ${getEventLabel('act')}:
${content}
`; stepContainer.appendChild(step); container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Update task status fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Status update failed:', error); }); } catch (e) { console.error('Act event handling failed:', e); } }); // Add handler for log event eventSource.addEventListener('log', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.querySelector('.loading')?.remove(); let stepContainer = container.querySelector('.step-container'); if (!stepContainer) { container.innerHTML = '
'; stepContainer = container.querySelector('.step-container'); } const content = data.result; const timestamp = new Date().toLocaleTimeString(); const step = document.createElement('div'); step.className = 'step-item log'; step.innerHTML = `
${getEventIcon('log')} [${timestamp}] ${getEventLabel('log')}:
${content}
`; stepContainer.appendChild(step); container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Update task status fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Status update failed:', error); }); } catch (e) { console.error('Log event handling failed:', e); } }); eventSource.addEventListener('run', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.querySelector('.loading')?.remove(); let stepContainer = container.querySelector('.step-container'); if (!stepContainer) { container.innerHTML = '
'; stepContainer = container.querySelector('.step-container'); } const content = data.result; const timestamp = new Date().toLocaleTimeString(); const step = document.createElement('div'); step.className = 'step-item run'; step.innerHTML = `
${getEventIcon('run')} [${timestamp}] ${getEventLabel('run')}:
${content}
`; stepContainer.appendChild(step); container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Update task status fetch(`/tasks/${taskId}`) .then(response => response.json()) .then(task => { updateTaskStatus(task); }) .catch(error => { console.error('Status update failed:', error); }); } catch (e) { console.error('Run event handling failed:', e); } }); eventSource.addEventListener('message', (event) => { clearInterval(heartbeatTimer); try { const data = JSON.parse(event.data); container.querySelector('.loading')?.remove(); let stepContainer = container.querySelector('.step-container'); if (!stepContainer) { container.innerHTML = '
'; stepContainer = container.querySelector('.step-container'); } // Create new step element const step = document.createElement('div'); step.className = `step-item ${data.type || 'step'}`; // Format content and timestamp const content = data.result; const timestamp = new Date().toLocaleTimeString(); step.innerHTML = `
${getEventIcon(data.type)} [${timestamp}] ${getEventLabel(data.type)}:
${content}
`; // Add step to container with animation stepContainer.prepend(step); setTimeout(() => { step.classList.add('show'); }, 10); // Auto-scroll to bottom container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); } catch (e) { console.error('Message handling failed:', e); } }); let isTaskComplete = false; eventSource.addEventListener('complete', (event) => { isTaskComplete = true; clearInterval(heartbeatTimer); clearInterval(pollInterval); container.innerHTML += `
✅ Task completed
${lastResultContent}
`; eventSource.close(); currentEventSource = null; lastResultContent = ''; // Clear result content }); eventSource.addEventListener('error', (event) => { clearInterval(heartbeatTimer); clearInterval(pollInterval); try { const data = JSON.parse(event.data); container.innerHTML += `
❌ Error: ${data.message}
`; eventSource.close(); currentEventSource = null; } catch (e) { console.error('Error handling failed:', e); } }); } container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); eventSource.onerror = (err) => { if (isTaskComplete) { return; } console.error('SSE connection error:', err); clearInterval(heartbeatTimer); clearInterval(pollInterval); eventSource.close(); if (retryCount < maxRetries) { retryCount++; container.innerHTML += `
⚠ Connection lost, retrying in ${retryDelay/1000} seconds (${retryCount}/${maxRetries})...
`; setTimeout(connect, retryDelay); } else { container.innerHTML += `
⚠ Connection lost, please try refreshing the page
`; } }; } connect(); } function getEventIcon(eventType) { switch(eventType) { case 'think': return '🤔'; case 'tool': return '🛠️'; case 'act': return '🚀'; case 'result': return '🏁'; case 'error': return '❌'; case 'complete': return '✅'; case 'log': return '📝'; case 'run': return '⚙️'; default: return 'ℹ️'; } } function getEventLabel(eventType) { switch(eventType) { case 'think': return 'Thinking'; case 'tool': return 'Using Tool'; case 'act': return 'Action'; case 'result': return 'Result'; case 'error': return 'Error'; case 'complete': return 'Complete'; case 'log': return 'Log'; case 'run': return 'Running'; default: return 'Info'; } } function updateTaskStatus(task) { const statusBar = document.getElementById('status-bar'); if (!statusBar) return; if (task.status === 'completed') { statusBar.innerHTML = `✅ Task completed`; } else if (task.status === 'failed') { statusBar.innerHTML = `❌ Task failed: ${task.error || 'Unknown error'}`; } else { statusBar.innerHTML = `⚙️ Task running: ${task.status}`; } } function loadHistory() { fetch('/tasks') .then(response => { if (!response.ok) { throw new Error('Failed to load history'); } return response.json(); }) .then(tasks => { const historyContainer = document.getElementById('history-container'); if (!historyContainer) return; historyContainer.innerHTML = ''; if (tasks.length === 0) { historyContainer.innerHTML = '
No recent tasks
'; return; } const historyList = document.createElement('div'); historyList.className = 'history-list'; tasks.forEach(task => { const taskItem = document.createElement('div'); taskItem.className = `history-item ${task.status}`; taskItem.innerHTML = `
${task.prompt}
${new Date(task.created_at).toLocaleString()} ${getStatusIcon(task.status)}
`; taskItem.addEventListener('click', () => { loadTask(task.id); }); historyList.appendChild(taskItem); }); historyContainer.appendChild(historyList); }) .catch(error => { console.error('Failed to load history:', error); const historyContainer = document.getElementById('history-container'); if (historyContainer) { historyContainer.innerHTML = `
Failed to load history: ${error.message}
`; } }); } function getStatusIcon(status) { switch(status) { case 'completed': return '✅'; case 'failed': return '❌'; case 'running': return '⚙️'; default: return '⏳'; } } function loadTask(taskId) { if (currentEventSource) { currentEventSource.close(); currentEventSource = null; } const container = document.getElementById('task-container'); container.innerHTML = '
Loading task...
'; document.getElementById('input-container').classList.add('bottom'); fetch(`/tasks/${taskId}`) .then(response => { if (!response.ok) { throw new Error('Failed to load task'); } return response.json(); }) .then(task => { if (task.status === 'running') { setupSSE(taskId); } else { displayTask(task); } }) .catch(error => { console.error('Failed to load task:', error); container.innerHTML = `
Failed to load task: ${error.message}
`; }); } function displayTask(task) { const container = document.getElementById('task-container'); container.innerHTML = ''; container.classList.add('active'); const welcomeMessage = document.querySelector('.welcome-message'); if (welcomeMessage) { welcomeMessage.style.display = 'none'; } const stepContainer = document.createElement('div'); stepContainer.className = 'step-container'; if (task.steps && task.steps.length > 0) { task.steps.forEach(step => { const stepItem = document.createElement('div'); stepItem.className = `step-item ${step.type || 'step'}`; const content = step.result; const timestamp = new Date(step.timestamp || task.created_at).toLocaleTimeString(); stepItem.innerHTML = `
${getEventIcon(step.type)} [${timestamp}] ${getEventLabel(step.type)}:
${content}
`; stepContainer.appendChild(stepItem); }); } else { stepContainer.innerHTML = '
No steps recorded for this task
'; } container.appendChild(stepContainer); if (task.status === 'completed') { let lastResultContent = ''; if (task.steps && task.steps.length > 0) { for (let i = task.steps.length - 1; i >= 0; i--) { if (task.steps[i].type === 'result') { lastResultContent = task.steps[i].result; break; } } } container.innerHTML += `
✅ Task completed
${lastResultContent}
`; } else if (task.status === 'failed') { container.innerHTML += `
❌ Error: ${task.error || 'Unknown error'}
`; } updateTaskStatus(task); } // Initialize the app when the DOM is loaded document.addEventListener('DOMContentLoaded', () => { loadHistory(); // Set up event listeners document.getElementById('create-task-btn').addEventListener('click', createTask); document.getElementById('prompt-input').addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); createTask(); } }); // Show history button functionality const historyToggle = document.getElementById('history-toggle'); if (historyToggle) { historyToggle.addEventListener('click', () => { const historyPanel = document.getElementById('history-panel'); if (historyPanel) { historyPanel.classList.toggle('open'); historyToggle.classList.toggle('active'); } }); } // Clear button functionality const clearButton = document.getElementById('clear-btn'); if (clearButton) { clearButton.addEventListener('click', () => { document.getElementById('prompt-input').value = ''; document.getElementById('prompt-input').focus(); }); } });