Spaces:
Sleeping
Sleeping
Download app/static/index.html from prathvi9607/SupportOps: direct link, hf CLI and curl.
- Browser
- Download file 16 kB
-
https://huggingface.co/spaces/prathvi9607/SupportOps/resolve/main/app/static/index.html
- Command line
-
hf download hf://spaces/prathvi9607/SupportOps/app/static/index.html
-
curl -L -o index.html https://huggingface.co/spaces/prathvi9607/SupportOps/resolve/main/app/static/index.html
16 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>SupportOps Arena - IT Incident Triage RL Environment</title> | |
| <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500;600;700&family=DM+Mono:wght@400;500&family=Sora:wght@600;700;800&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg-primary: #FFFFFF; | |
| --bg-secondary: #F0F8FF; | |
| --bg-card: #FFFFFF; | |
| --bg-glass: rgba(255,255,255,0.8); | |
| --gradient-main: linear-gradient(135deg, #FFFFFF 0%, #E0F4FF 40%, #B8E4FF 100%); | |
| --gradient-card: linear-gradient(145deg, rgba(255,255,255,0.95), rgba(224,244,255,0.6)); | |
| --sky-300: #87CEEB; | |
| --sky-500: #2196F3; | |
| --sky-600: #1976D2; | |
| --text-primary: #0D1B2A; | |
| --text-secondary: #3A5068; | |
| --text-muted: #7B9BB5; | |
| --success: #10B981; | |
| --warning: #F59E0B; | |
| --danger: #EF4444; | |
| --info: #3B82F6; | |
| --border-light: rgba(135, 206, 235, 0.3); | |
| --shadow-md: 0 4px 20px rgba(33, 150, 243, 0.12); | |
| --radius-md: 12px; | |
| --transition-med: 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'DM Sans', sans-serif; | |
| background: var(--gradient-main); | |
| color: var(--text-primary); | |
| min-height: 100vh; | |
| } | |
| h1, h2, h3 { | |
| font-family: 'Sora', sans-serif; | |
| } | |
| .container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| } | |
| .header { | |
| background: var(--gradient-card); | |
| backdrop-filter: blur(12px); | |
| padding: 20px 30px; | |
| border-radius: var(--radius-md); | |
| box-shadow: var(--shadow-md); | |
| margin-bottom: 20px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .header h1 { | |
| font-size: 28px; | |
| color: var(--sky-600); | |
| } | |
| .status-badge { | |
| padding: 8px 16px; | |
| background: var(--success); | |
| color: white; | |
| border-radius: 20px; | |
| font-weight: 600; | |
| font-size: 14px; | |
| } | |
| .grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 20px; | |
| margin-bottom: 20px; | |
| } | |
| .card { | |
| background: var(--gradient-card); | |
| backdrop-filter: blur(12px); | |
| border-radius: var(--radius-md); | |
| padding: 20px; | |
| box-shadow: var(--shadow-md); | |
| border: 1px solid var(--border-light); | |
| } | |
| .card h2 { | |
| font-size: 20px; | |
| margin-bottom: 15px; | |
| color: var(--sky-600); | |
| } | |
| .button { | |
| padding: 10px 20px; | |
| background: var(--sky-500); | |
| color: white; | |
| border: none; | |
| border-radius: var(--radius-md); | |
| font-family: 'DM Sans', sans-serif; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: var(--transition-med); | |
| margin-right: 10px; | |
| margin-bottom: 10px; | |
| } | |
| .button:hover { | |
| background: var(--sky-600); | |
| transform: translateY(-2px); | |
| box-shadow: var(--shadow-md); | |
| } | |
| .button-secondary { | |
| background: var(--text-secondary); | |
| } | |
| .button-secondary:hover { | |
| background: var(--text-primary); | |
| } | |
| select, input { | |
| padding: 10px; | |
| border: 2px solid var(--border-light); | |
| border-radius: var(--radius-md); | |
| font-family: 'DM Sans', sans-serif; | |
| margin-right: 10px; | |
| background: white; | |
| } | |
| .log-entry { | |
| padding: 10px; | |
| margin-bottom: 8px; | |
| background: white; | |
| border-left: 4px solid var(--info); | |
| border-radius: 6px; | |
| font-family: 'DM Mono', monospace; | |
| font-size: 13px; | |
| } | |
| .log-entry.error { | |
| border-left-color: var(--danger); | |
| } | |
| .log-entry.warn { | |
| border-left-color: var(--warning); | |
| } | |
| .log-entry.info { | |
| border-left-color: var(--success); | |
| } | |
| .action-history { | |
| max-height: 300px; | |
| overflow-y: auto; | |
| } | |
| .action-item { | |
| padding: 10px; | |
| margin-bottom: 8px; | |
| background: white; | |
| border-radius: 6px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .reward { | |
| font-weight: 600; | |
| padding: 4px 8px; | |
| border-radius: 4px; | |
| } | |
| .reward.positive { | |
| background: var(--success); | |
| color: white; | |
| } | |
| .reward.negative { | |
| background: var(--danger); | |
| color: white; | |
| } | |
| .metrics { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 15px; | |
| margin-top: 15px; | |
| } | |
| .metric { | |
| text-align: center; | |
| padding: 15px; | |
| background: white; | |
| border-radius: var(--radius-md); | |
| } | |
| .metric-value { | |
| font-size: 32px; | |
| font-weight: 700; | |
| color: var(--sky-600); | |
| } | |
| .metric-label { | |
| font-size: 14px; | |
| color: var(--text-muted); | |
| margin-top: 5px; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| .loading { | |
| color: var(--text-muted); | |
| font-style: italic; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>🎯 SupportOps Arena</h1> | |
| <div class="status-badge" id="statusBadge">Ready</div> | |
| </div> | |
| <div class="card"> | |
| <h2>Environment Controls</h2> | |
| <div> | |
| <select id="taskLevel"> | |
| <option value="easy">Easy - Wi-Fi Connectivity</option> | |
| <option value="medium">Medium - VPN Access</option> | |
| <option value="hard">Hard - Cross-System Failure</option> | |
| </select> | |
| <input type="number" id="seedInput" placeholder="Seed (optional)"> | |
| <button class="button" onclick="resetEnvironment()">Reset Environment</button> | |
| <button class="button button-secondary" onclick="runBaseline()">Run Baseline Agent</button> | |
| </div> | |
| </div> | |
| <div class="grid"> | |
| <div class="card"> | |
| <h2>Current Ticket</h2> | |
| <div id="ticketInfo"> | |
| <p class="loading">Reset environment to start...</p> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>Observations</h2> | |
| <div id="observations"> | |
| <p class="loading">No active episode</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>Manual Actions</h2> | |
| <div id="actionButtons"> | |
| <!-- Action buttons will be populated --> | |
| </div> | |
| </div> | |
| <div class="grid"> | |
| <div class="card"> | |
| <h2>System Logs</h2> | |
| <div id="systemLogs" style="max-height: 300px; overflow-y: auto;"> | |
| <p class="loading">No logs yet</p> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>Action History</h2> | |
| <div id="actionHistory" class="action-history"> | |
| <p class="loading">No actions yet</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>Episode Metrics</h2> | |
| <div class="metrics" id="metrics"> | |
| <div class="metric"> | |
| <div class="metric-value" id="stepsValue">0</div> | |
| <div class="metric-label">Steps</div> | |
| </div> | |
| <div class="metric"> | |
| <div class="metric-value" id="rewardValue">0.00</div> | |
| <div class="metric-label">Cumulative Reward</div> | |
| </div> | |
| <div class="metric"> | |
| <div class="metric-value" id="diagnosticsValue">0</div> | |
| <div class="metric-label">Diagnostics Taken</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let sessionId = null; | |
| let currentObservation = null; | |
| let cumulativeReward = 0; | |
| const actions = [ | |
| 'inspect_network', 'inspect_logs', 'check_authentication', | |
| 'check_permissions', 'query_device_status', 'search_internal_kb', | |
| 'contact_user_for_info', 'run_diagnostic_script', 'flush_dns', | |
| 'reconfigure_client', 'restart_service', 'reset_credentials', | |
| 'escalate_ticket', 'resolve_ticket', 'close_without_fix' | |
| ]; | |
| function updateStatus(text, type = 'success') { | |
| const badge = document.getElementById('statusBadge'); | |
| badge.textContent = text; | |
| badge.style.background = type === 'success' ? 'var(--success)' : | |
| type === 'warning' ? 'var(--warning)' : | |
| 'var(--danger)'; | |
| } | |
| async function resetEnvironment() { | |
| const taskLevel = document.getElementById('taskLevel').value; | |
| const seed = document.getElementById('seedInput').value || null; | |
| updateStatus('Resetting...', 'warning'); | |
| try { | |
| const response = await fetch('/reset', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| ...(sessionId ? { 'X-Session-ID': sessionId } : {}) | |
| }, | |
| body: JSON.stringify({ task_level: taskLevel, seed: seed ? parseInt(seed) : null }) | |
| }); | |
| const data = await response.json(); | |
| sessionId = data.session_id; | |
| currentObservation = data.observation; | |
| cumulativeReward = 0; | |
| updateUI(); | |
| createActionButtons(); | |
| updateStatus('Episode Active', 'success'); | |
| } catch (error) { | |
| console.error('Reset failed:', error); | |
| updateStatus('Error', 'error'); | |
| } | |
| } | |
| async function takeAction(actionName) { | |
| if (!sessionId) { | |
| alert('Please reset environment first'); | |
| return; | |
| } | |
| try { | |
| const response = await fetch('/step', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'X-Session-ID': sessionId | |
| }, | |
| body: JSON.stringify({ action_name: actionName }) | |
| }); | |
| const data = await response.json(); | |
| currentObservation = data.observation; | |
| cumulativeReward += data.reward; | |
| updateUI(); | |
| if (data.done) { | |
| updateStatus('Episode Complete', 'info'); | |
| alert(`Episode finished! Final reward: ${cumulativeReward.toFixed(2)}`); | |
| } | |
| } catch (error) { | |
| console.error('Step failed:', error); | |
| updateStatus('Error', 'error'); | |
| } | |
| } | |
| async function runBaseline() { | |
| updateStatus('Running Baseline...', 'warning'); | |
| try { | |
| const response = await fetch('/baseline/run', { method: 'POST' }); | |
| const data = await response.json(); | |
| let message = 'Baseline Complete!\n\n'; | |
| for (const [level, result] of Object.entries(data.results)) { | |
| message += `${level}: score=${result.grade_score.toFixed(3)}, steps=${result.steps}\n`; | |
| } | |
| alert(message); | |
| updateStatus('Ready', 'success'); | |
| } catch (error) { | |
| console.error('Baseline failed:', error); | |
| updateStatus('Error', 'error'); | |
| } | |
| } | |
| function updateUI() { | |
| if (!currentObservation) return; | |
| // Update ticket info | |
| document.getElementById('ticketInfo').innerHTML = ` | |
| <p><strong>Ticket ID:</strong> ${currentObservation.ticket_id}</p> | |
| <p><strong>Summary:</strong> ${currentObservation.ticket_summary}</p> | |
| <p><strong>Task Level:</strong> ${currentObservation.task_level}</p> | |
| `; | |
| // Update observations | |
| document.getElementById('observations').innerHTML = ` | |
| <p><strong>Network:</strong> ${currentObservation.network_status}</p> | |
| <p><strong>VPN:</strong> ${currentObservation.vpn_status}</p> | |
| <p><strong>Auth:</strong> ${currentObservation.auth_status}</p> | |
| <p><strong>Escalation Allowed:</strong> ${currentObservation.escalation_allowed ? 'Yes' : 'No'}</p> | |
| `; | |
| // Update logs | |
| const logsDiv = document.getElementById('systemLogs'); | |
| logsDiv.innerHTML = ''; | |
| currentObservation.system_logs.forEach(log => { | |
| const entry = document.createElement('div'); | |
| entry.className = `log-entry ${log.level.toLowerCase()}`; | |
| entry.textContent = `[${log.timestamp.split('T')[1].split('.')[0]}] ${log.level} ${log.service}: ${log.message}`; | |
| logsDiv.appendChild(entry); | |
| }); | |
| // Update action history | |
| const historyDiv = document.getElementById('actionHistory'); | |
| historyDiv.innerHTML = ''; | |
| currentObservation.action_history.forEach(action => { | |
| const item = document.createElement('div'); | |
| item.className = 'action-item'; | |
| item.innerHTML = ` | |
| <span>${action.step}. ${action.action_name}</span> | |
| <span class="reward ${action.reward_received >= 0 ? 'positive' : 'negative'}"> | |
| ${action.reward_received >= 0 ? '+' : ''}${action.reward_received.toFixed(2)} | |
| </span> | |
| `; | |
| historyDiv.appendChild(item); | |
| }); | |
| // Update metrics | |
| document.getElementById('stepsValue').textContent = currentObservation.step_count; | |
| document.getElementById('rewardValue').textContent = cumulativeReward.toFixed(2); | |
| document.getElementById('diagnosticsValue').textContent = currentObservation.action_history.length; | |
| } | |
| function createActionButtons() { | |
| const container = document.getElementById('actionButtons'); | |
| container.innerHTML = ''; | |
| actions.forEach(action => { | |
| const btn = document.createElement('button'); | |
| btn.className = 'button button-secondary'; | |
| btn.textContent = action.replace(/_/g, ' ').toUpperCase(); | |
| btn.onclick = () => takeAction(action); | |
| container.appendChild(btn); | |
| }); | |
| } | |
| // Initialize | |
| updateStatus('Ready', 'success'); | |
| </script> | |
| </body> | |
| </html> | |