From 7b790b1bc331a0155d75224d94d319a720ec9406 Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Mon, 11 May 2026 22:21:04 +0200 Subject: [PATCH] feat(dashboard): progress bar live + top fitness sulla pagina Overview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aggiunto blocco "Progresso run" sopra le metriche statiche con: - progress bar generazioni (gens_done / n_generations) - progress bar evaluations (evals_done / pop × gen) con percentuale - metric top fitness / median fitness / cost so far - pulsante Refresh manuale + timestamp ultimo update - emoji status (🟢 running / ✅ completed / ❌ failed) Niente nuove dipendenze: solo st.progress + st.rerun standard Streamlit. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dashboard/pages/01_overview.py | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/multi_swarm/dashboard/pages/01_overview.py b/src/multi_swarm/dashboard/pages/01_overview.py index 42296f5..558506d 100644 --- a/src/multi_swarm/dashboard/pages/01_overview.py +++ b/src/multi_swarm/dashboard/pages/01_overview.py @@ -1,9 +1,12 @@ from __future__ import annotations +from datetime import datetime + import streamlit as st from multi_swarm.dashboard.data import ( evaluations_df, + generations_df, get_repo, get_run_overview, list_runs_df, @@ -25,6 +28,41 @@ st.dataframe(runs[["id", "name", "started_at", "completed_at", "status", "total_ selected = st.selectbox("Seleziona run per dettaglio", runs["id"].tolist()) overview = get_run_overview(repo, selected) +# --- Progress live --- +cfg = overview["config"] +pop_size = int(cfg.get("population_size", 0)) +n_gens = int(cfg.get("n_generations", 0)) +evals = evaluations_df(repo, selected) +gens = generations_df(repo, selected) + +evals_done = len(evals) +evals_total = max(pop_size * n_gens, 1) +gens_done = int(gens["completed_at"].notna().sum()) if not gens.empty else 0 + +status_emoji = {"running": "🟢", "completed": "✅", "failed": "❌"}.get(overview["status"], "⚪") +top_fit = float(evals["fitness"].max()) if not evals.empty else float("nan") + +st.subheader(f"{status_emoji} Progresso run") +st.progress( + min(gens_done / max(n_gens, 1), 1.0), + text=f"Generations: {gens_done}/{n_gens}", +) +st.progress( + min(evals_done / evals_total, 1.0), + text=f"Evaluations: {evals_done}/{evals_total} ({100*evals_done/evals_total:.1f}%)", +) +pcol1, pcol2, pcol3 = st.columns(3) +pcol1.metric("Top fitness", f"{top_fit:.4f}" if evals_done else "—") +pcol2.metric("Median fitness", f"{evals['fitness'].median():.4f}" if evals_done else "—") +pcol3.metric("Cost so far", f"${overview['total_cost_usd']:.4f}") + +ref_col1, ref_col2 = st.columns([1, 4]) +if ref_col1.button("🔄 Refresh"): + st.rerun() +ref_col2.caption(f"Last update: {datetime.now().strftime('%H:%M:%S')}") + +st.divider() + col1, col2, col3, col4 = st.columns(4) col1.metric("Status", overview["status"]) col2.metric("Cost (USD)", f"{overview['total_cost_usd']:.4f}") @@ -32,7 +70,6 @@ col3.metric("Started", overview["started_at"]) col4.metric("Completed", overview["completed_at"] or "—") st.subheader("Statistiche evaluations") -evals = evaluations_df(repo, selected) col5, col6, col7, col8 = st.columns(4) if not evals.empty: parse_success = 100 * (evals["parse_error"].isna().sum() / len(evals))