Hacktrix-121 commited on
Commit
dd2851f
·
1 Parent(s): f480f98

Unified dashboard for training and testing

Browse files
dashboard.html CHANGED
@@ -242,6 +242,17 @@
242
  .toast-info { background: rgba(59,130,246,0.9); color: white; }
243
  @keyframes toastIn { from{opacity:0;transform:translateY(16px)} to{opacity:1;transform:translateY(0)} }
244
 
 
 
 
 
 
 
 
 
 
 
 
245
  @media (max-width:1200px) {
246
  .grid-stats { grid-template-columns: repeat(3, 1fr); }
247
  .grid-main, .grid-bottom { grid-template-columns: 1fr; }
@@ -269,6 +280,25 @@
269
  </div>
270
  </div>
271
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  <!-- ══════ Action Guide ══════ -->
273
  <div class="help-bar">
274
  <div class="help-item">
@@ -437,13 +467,14 @@
437
  </div>
438
  </div>
439
  </div>
 
440
  </div>
441
 
442
  <script>
443
  // ═══════════════════════════════════════════════════════════════════
444
  // State
445
  // ═══════════════════════════════════════════════════════════════════
446
- const SERVER = 'https://tusharp2006-scaler-deployment.hf.space/';
447
  let currentAlerts = [];
448
  let episodeScores = [];
449
  let episodeRewards = [];
@@ -772,11 +803,61 @@ function showToast(msg, type='success') {
772
  setTimeout(() => el.remove(), 3500);
773
  }
774
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775
  // ═══════════════════════════════════════════════════════════════════
776
  // Init
777
  // ═══════════════════════════════════════════════════════════════════
778
  pollHealth();
 
779
  setInterval(pollHealth, 5000);
 
780
  </script>
781
  </body>
782
  </html>
 
242
  .toast-info { background: rgba(59,130,246,0.9); color: white; }
243
  @keyframes toastIn { from{opacity:0;transform:translateY(16px)} to{opacity:1;transform:translateY(0)} }
244
 
245
+ /* ── Tabs & Training ────────────────────────────────────── */
246
+ .tabs { display: flex; gap: 14px; margin-bottom: 24px; border-bottom: 1px solid var(--border); padding-bottom: 10px; }
247
+ .tab { font-size: 14px; font-weight: 600; color: var(--text-muted); cursor: pointer; padding: 10px 16px; border-radius: 8px; transition: 0.2s; }
248
+ .tab:hover { background: rgba(255,255,255,0.05); color: var(--text-primary); }
249
+ .tab.active { background: var(--bg-input); color: var(--accent-blue); border: 1px solid var(--border); }
250
+ .training-container { display: none; background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 24px; }
251
+ .training-container.active { display: block; }
252
+ .testing-container { display: none; }
253
+ .testing-container.active { display: block; }
254
+ .terminal { background: #000; color: #10b981; font-family: 'Courier New', monospace; padding: 16px; border-radius: 8px; height: 400px; overflow-y: auto; margin-top: 16px; font-size: 12px; border: 1px solid var(--border); line-height: 1.5; white-space: pre-wrap; }
255
+
256
  @media (max-width:1200px) {
257
  .grid-stats { grid-template-columns: repeat(3, 1fr); }
258
  .grid-main, .grid-bottom { grid-template-columns: 1fr; }
 
280
  </div>
281
  </div>
282
 
283
+ <!-- ══════ Tabs ══════ -->
284
+ <div class="tabs">
285
+ <div class="tab active" id="tabLive" onclick="switchTab('live')">Live Testing</div>
286
+ <div class="tab" id="tabTrain" onclick="switchTab('train')">Agent Training</div>
287
+ </div>
288
+
289
+ <!-- ══════ Training View ══════ -->
290
+ <div class="training-container" id="trainingView">
291
+ <h3>Train RL Agent (PPO)</h3>
292
+ <p style="color:var(--text-muted); font-size:12px; margin-top:8px; margin-bottom:16px;">Launch background training across varying difficulty tasks.</p>
293
+ <div class="controls">
294
+ <span style="font-size:12px">Easy Episodes:</span>
295
+ <input type="number" id="trainEpisodes" value="300" style="padding:6px; border-radius:6px; border:1px solid var(--border); background:var(--bg-input); color:var(--text-primary); width:100px;">
296
+ <button class="btn btn-primary" onclick="startTraining()" id="trainBtn">▶ Start Training</button>
297
+ </div>
298
+ <div class="terminal" id="trainTerminal">Ready to train. Run PPO to update weights.</div>
299
+ </div>
300
+
301
+ <div class="testing-container active" id="testingView">
302
  <!-- ══════ Action Guide ══════ -->
303
  <div class="help-bar">
304
  <div class="help-item">
 
467
  </div>
468
  </div>
469
  </div>
470
+ </div> <!-- end testing-container -->
471
  </div>
472
 
473
  <script>
474
  // ═══════════════════════════════════════════════════════════════════
475
  // State
476
  // ═══════════════════════════════════════════════════════════════════
477
+ const SERVER = 'https://tusharp2006-scaler-deployment.hf.space';
478
  let currentAlerts = [];
479
  let episodeScores = [];
480
  let episodeRewards = [];
 
803
  setTimeout(() => el.remove(), 3500);
804
  }
805
 
806
+ // ═══════════════════════════════════════════════════════════════════
807
+ // UI & Tabs
808
+ // ═══════════════════════════════════════════════════════════════════
809
+ function switchTab(tab) {
810
+ document.getElementById('tabLive').className = tab === 'live' ? 'tab active' : 'tab';
811
+ document.getElementById('tabTrain').className = tab === 'train' ? 'tab active' : 'tab';
812
+ document.getElementById('testingView').className = tab === 'live' ? 'testing-container active' : 'testing-container';
813
+ document.getElementById('trainingView').className = tab === 'train' ? 'training-container active' : 'training-container';
814
+ }
815
+
816
+ // ═══════════════════════════════════════════════════════════════════
817
+ // Training Webhooks
818
+ // ═══════════════════════════════════════════════════════════════════
819
+ let trainingInterval = null;
820
+
821
+ async function startTraining() {
822
+ const ep = document.getElementById('trainEpisodes').value || 300;
823
+ try {
824
+ const r = await api('POST', `/train?episodes=${ep}`);
825
+ if(r.error) showToast(r.error, 'error');
826
+ else {
827
+ showToast('Training started in background!', 'success');
828
+ if(!trainingInterval) trainingInterval = setInterval(pollTraining, 2000);
829
+ document.getElementById('trainBtn').disabled = true;
830
+ }
831
+ } catch(e) { showToast('Trainer error', 'error'); }
832
+ }
833
+
834
+ async function pollTraining() {
835
+ try {
836
+ const r = await api('GET', '/train/status');
837
+ if (r.error) return;
838
+
839
+ const term = document.getElementById('trainTerminal');
840
+ if (r.logs && r.logs.length) {
841
+ term.textContent = r.logs.join('\n');
842
+ term.scrollTop = term.scrollHeight; // Auto-scroll
843
+ }
844
+
845
+ if (!r.is_running) {
846
+ if(trainingInterval) { clearInterval(trainingInterval); trainingInterval = null; }
847
+ document.getElementById('trainBtn').disabled = false;
848
+ } else {
849
+ document.getElementById('trainBtn').disabled = true;
850
+ }
851
+ } catch(e) {}
852
+ }
853
+
854
  // ═══════════════════════════════════════════════════════════════════
855
  // Init
856
  // ═══════════════════════════════════════════════════════════════════
857
  pollHealth();
858
+ pollTraining();
859
  setInterval(pollHealth, 5000);
860
+ if(document.getElementById('tabTrain').className.includes('active')) setInterval(pollTraining, 2000);
861
  </script>
862
  </body>
863
  </html>
requirements.txt CHANGED
@@ -20,4 +20,7 @@ openai>=1.0.0
20
  requests>=2.31.0
21
 
22
  # ── YAML parsing ─────────────────────────────────────────────────────────────
23
- pyyaml>=6.0
 
 
 
 
20
  requests>=2.31.0
21
 
22
  # ── YAML parsing ─────────────────────────────────────────────────────────────
23
+ pyyaml>=6.0
24
+
25
+ # ── Auto-commit back to Space ─────────────────────────────────────────────────
26
+ huggingface_hub>=0.20.0
src/adaptive_alert_triage/server.py CHANGED
@@ -100,7 +100,7 @@ def _norm(raw: str) -> str:
100
 
101
  app = FastAPI(title="Adaptive Alert Triage RL Server", version="0.3.0")
102
  app.add_middleware(CORSMiddleware, allow_origins=["*"],
103
- allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
104
  #Changes
105
  @app.middleware("http")
106
  async def log_requests(request, call_next):
@@ -551,6 +551,84 @@ async def root():
551
  }
552
 
553
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  @app.get("/web")
555
  async def web_ui():
556
  """
 
100
 
101
  app = FastAPI(title="Adaptive Alert Triage RL Server", version="0.3.0")
102
  app.add_middleware(CORSMiddleware, allow_origins=["*"],
103
+ allow_credentials=False, allow_methods=["*"], allow_headers=["*"])
104
  #Changes
105
  @app.middleware("http")
106
  async def log_requests(request, call_next):
 
551
  }
552
 
553
 
554
+ import threading
555
+ import subprocess
556
+
557
+ _training_proc = None
558
+ _training_logs = []
559
+
560
+ def _run_training(episodes: int):
561
+ global _training_proc, _training_logs, _ppo_agents
562
+ _training_logs = [f"Starting training with --episodes {episodes}..."]
563
+ try:
564
+ _training_proc = subprocess.Popen(
565
+ [sys.executable, "train_rl.py", "--episodes", str(episodes)],
566
+ stdout=subprocess.PIPE,
567
+ stderr=subprocess.STDOUT,
568
+ text=True,
569
+ bufsize=1,
570
+ cwd=_project_root if _project_root else os.getcwd()
571
+ )
572
+ for line in iter(_training_proc.stdout.readline, ''):
573
+ if line:
574
+ _training_logs.append(line.rstrip('\n'))
575
+ if len(_training_logs) > 1000:
576
+ _training_logs.pop(0)
577
+ _training_proc.wait()
578
+ _training_logs.append(f"Training finished with exit code {- _training_proc.returncode if _training_proc.returncode < 0 else _training_proc.returncode}")
579
+
580
+ # Auto-reload PPO weights if training succeeded
581
+ if _training_proc.returncode == 0:
582
+ for tid in ("easy", "medium", "hard"):
583
+ agent = _load_ppo(tid)
584
+ if agent:
585
+ _ppo_agents[tid] = agent
586
+ _training_logs.append("Successfully reloaded PPO weights for all tasks.")
587
+
588
+ # Auto-save weights back to Hugging Face
589
+ try:
590
+ from huggingface_hub import HfApi
591
+ hf_token = os.environ.get("HF_TOKEN")
592
+ repo_id = os.environ.get("SPACE_ID", "tusharp2006/scaler-deployment")
593
+
594
+ if hf_token:
595
+ _training_logs.append(f"Pushing updated weights back to HF Hub ({repo_id})...")
596
+ api = HfApi(token=hf_token)
597
+ weights_dir = os.path.join(_project_root if _project_root else os.getcwd(), "weights")
598
+
599
+ if os.path.exists(weights_dir):
600
+ api.upload_folder(
601
+ repo_id=repo_id,
602
+ folder_path=weights_dir,
603
+ path_in_repo="weights",
604
+ repo_type="space",
605
+ commit_message="Auto-sync weights after RL training"
606
+ )
607
+ _training_logs.append("Weights successfully pushed and persisted to Hugging Face!")
608
+ else:
609
+ _training_logs.append("No HF_TOKEN found in environment. Skipping weight cloud-persistence.")
610
+ except ImportError:
611
+ _training_logs.append("huggingface_hub not installed. Skipping cloud backup.")
612
+ except Exception as e:
613
+ _training_logs.append(f"Failed to push weights to Hub: {e}")
614
+
615
+ except Exception as e:
616
+ _training_logs.append(f"Error starting training: {e}")
617
+
618
+ @app.post("/train")
619
+ async def start_training(episodes: int = 300):
620
+ global _training_proc
621
+ if _training_proc is not None and _training_proc.poll() is None:
622
+ return {"status": "already running"}
623
+ threading.Thread(target=_run_training, args=(episodes,), daemon=True).start()
624
+ return {"status": "started"}
625
+
626
+ @app.get("/train/status")
627
+ async def get_training_status():
628
+ global _training_proc, _training_logs
629
+ is_running = _training_proc is not None and _training_proc.poll() is None
630
+ return {"is_running": is_running, "logs": _training_logs}
631
+
632
  @app.get("/web")
633
  async def web_ui():
634
  """