Files
2025-05-24 20:04:02 +08:00

272 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Task Monitor</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.status-section {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
}
.queue-section {
margin-top: 20px;
}
.task-item {
padding: 10px;
margin: 5px 0;
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
}
.progress-container {
width: 100%;
background-color: #e9ecef;
border-radius: 4px;
margin: 10px 0;
}
.progress-bar {
height: 20px;
background-color: #007bff;
border-radius: 4px;
transition: width 0.3s ease;
}
.progress-text {
text-align: center;
margin-top: 5px;
font-size: 14px;
color: #6c757d;
}
.error-message {
color: #dc3545;
padding: 10px;
margin: 10px 0;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 4px;
}
.completed-task {
background-color: #d4edda;
border-color: #c3e6cb;
}
.running-task {
background-color: #cce5ff;
border-color: #b8daff;
}
.queued-task {
background-color: #fff3cd;
border-color: #ffeeba;
}
.total-progress {
margin: 20px 0;
padding: 15px;
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
}
.total-progress h3 {
margin-top: 0;
color: #495057;
}
.execution-time-container {
font-size: 14px;
color: #666;
padding: 5px 10px;
background-color: #f5f5f5;
border-radius: 4px;
display: inline-block;
}
</style>
</head>
<body>
<div class="container">
<h1>Task Monitor</h1>
<div class="status-section">
<h2>Current Status</h2>
<p>Task ID: <span id="current-task-id">-</span></p>
<p>Status: <span id="current-status">idle</span></p>
<div class="total-progress">
<h3>Workflow Progress</h3>
<div class="progress-container">
<div id="total-progress-bar" class="progress-bar" style="width: 0%"></div>
</div>
<div id="total-progress-text" class="progress-text">0/0 nodes executed</div>
</div>
<div class="execution-time-container">
<span>Execution Time: </span>
<span id="execution-time">00:00:00</span>
</div>
<div id="current-progress" style="display: none;">
<h3>Current Node Progress</h3>
<p>Node Type: <span id="current-node-type">-</span></p>
<div class="progress-container">
<div id="progress-bar" class="progress-bar" style="width: 0%"></div>
</div>
<div id="progress-text" class="progress-text">0/0 steps</div>
<p id="progress-message"></p>
</div>
</div>
<div id="error-section" class="error-message" style="display: none;"></div>
<div class="queue-section">
<h2>Queue</h2>
<div id="running-tasks">
<h3>Running Tasks</h3>
<div id="running-tasks-list"></div>
</div>
<div id="pending-tasks">
<h3>Pending Tasks</h3>
<div id="pending-tasks-list"></div>
</div>
</div>
</div>
<script>
let currentTaskId = null;
let lastStatus = null;
function updateProgressBar(progress, total, elementId) {
const percentage = total > 0 ? (progress / total) * 100 : 0;
const progressBar = document.getElementById(elementId);
progressBar.style.width = `${percentage}%`;
}
function updateTotalProgress(executed, total) {
updateProgressBar(executed, total, 'total-progress-bar');
document.getElementById('total-progress-text').textContent = `${executed}/${total} nodes executed`;
}
function updateNodeProgress(progress, total) {
updateProgressBar(progress, total, 'progress-bar');
document.getElementById('progress-text').textContent = `${progress}/${total} steps`;
}
function updateTaskList(tasks, containerId, status) {
const container = document.getElementById(containerId);
container.innerHTML = '';
tasks.forEach(task => {
const taskElement = document.createElement('div');
taskElement.className = `task-item ${status}-task`;
taskElement.innerHTML = `
<p>Task ID: ${task.prompt_id}</p>
<p>Nodes: ${task.nodes_in_prompt}</p>
${task.client_id ? `<p>Client ID: ${task.client_id}</p>` : ''}
`;
container.appendChild(taskElement);
});
}
function updateStatus(data) {
// Update current task ID and status
document.getElementById('current-task-id').textContent = data.task_id || '-';
document.getElementById('current-status').textContent = data.status;
// Update total progress
const workflowProgress = data.workflow_progress || {};
updateTotalProgress(workflowProgress.executed_nodes || 0, workflowProgress.total_nodes || 0);
// Update current node progress if available
const progressSection = document.getElementById('current-progress');
if (data.current_task_progress) {
progressSection.style.display = 'block';
document.getElementById('current-node-type').textContent = data.current_task_progress.node_type || '-';
updateNodeProgress(
data.current_task_progress.step || 0,
data.current_task_progress.total_steps || 0
);
document.getElementById('progress-message').textContent = data.current_task_progress.text_message || '';
} else {
progressSection.style.display = 'none';
}
// Update execution time
if (data.execution_time !== undefined) {
const executionTime = data.execution_time || 0;
const date = new Date(executionTime * 1000);
const hours = String(date.getUTCHours()).padStart(2, '0');
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
const seconds = String(date.getUTCSeconds()).padStart(2, '0');
const fullMilliseconds = Math.round((executionTime % 1) * 1000);
const milliseconds = String(Math.round(fullMilliseconds / 10)).padStart(2, '0');
const formattedTime = `${hours}h:${minutes}m:${seconds}s:${milliseconds}ms`;
document.getElementById('execution-time').textContent = formattedTime;
}
// Update error section
const errorSection = document.getElementById('error-section');
if (data.error_info && data.error_info.length > 0) {
errorSection.style.display = 'block';
// Find the execution error in the error info array
const errorEntry = data.error_info.find(entry => entry[0] === 'execution_error');
if (errorEntry && errorEntry[1]) {
const errorData = errorEntry[1];
errorSection.innerHTML = `
<h3>Error in ${errorData.node_type || 'node'}: ${errorData.exception_type || 'Error'}</h3>
<p>${errorData.exception_message || 'An unknown error occurred'}</p>
<details>
<summary>Details</summary>
<pre>${JSON.stringify(errorData, null, 2)}</pre>
</details>
`;
} else {
// Fallback for other error formats
errorSection.innerHTML = `
<h3>Error</h3>
<pre>${JSON.stringify(data.error_info, null, 2)}</pre>
`;
}
} else {
errorSection.style.display = 'none';
}
// Update queue information
updateTaskList(data.queue.running || [], 'running-tasks-list', 'running');
updateTaskList(data.queue.pending || [], 'pending-tasks-list', 'queued');
// Update document title with status
document.title = `Task Monitor - ${data.status.charAt(0).toUpperCase() + data.status.slice(1)}`;
}
async function fetchStatus() {
try {
const response = await fetch('/task_monitor/status');
const data = await response.json();
// Only update if the status has changed
if (JSON.stringify(data) !== lastStatus) {
updateStatus(data);
lastStatus = JSON.stringify(data);
}
} catch (error) {
console.error('Error fetching status:', error);
}
}
// Initial fetch
fetchStatus();
// Poll for updates every second
setInterval(fetchStatus, 1000);
</script>
</body>
</html>