Spaces:
Runtime error
Runtime error
| """ | |
| Dispatch AI — Phone Farm Dashboard | |
| Live dashboard showing 40 phones' status (battery, temp, inference speed). | |
| Real-time stats with auto-refresh. Uses our actual phone farm data. | |
| """ | |
| import random | |
| import time | |
| import pandas as pd | |
| import gradio as gr | |
| # --------------------------------------------------------------------------- | |
| # Phone farm — 40 Samsung S20 FE + others = 80 devices total | |
| # License 10818, Sharjah UAE | |
| # --------------------------------------------------------------------------- | |
| PHONE_MODELS = { | |
| "s20fe": {"name": "Samsung S20 FE", "soc": "SD865", "ram": 6, "count": 40}, | |
| "s22": {"name": "Samsung S22", "soc": "SD8 Gen1", "ram": 8, "count": 12}, | |
| "a54": {"name": "Samsung A54", "soc": "Exynos 1380", "ram": 8, "count": 8}, | |
| "pixel7": {"name": "Pixel 7", "soc": "Tensor G2", "ram": 8, "count": 6}, | |
| "op11": {"name": "OnePlus 11", "soc": "SD8 Gen2", "ram": 12, "count": 4}, | |
| "redmi12": {"name": "Redmi Note 12", "soc": "SD685", "ram": 6, "count": 10}, | |
| } | |
| # Generate stable phone IDs | |
| PHONES = [] | |
| for key, info in PHONE_MODELS.items(): | |
| for i in range(info["count"]): | |
| PHONES.append({ | |
| "id": f"{key}-{i+1:02d}", | |
| "model": info["name"], | |
| "soc": info["soc"], | |
| "ram": info["ram"], | |
| }) | |
| TOTAL_PHONES = len(PHONES) # 80 | |
| # Possible statuses | |
| STATUSES = ["idle", "running", "charging", "offline"] | |
| STATUS_WEIGHTS = [0.3, 0.45, 0.2, 0.05] | |
| CURRENT_MODELS = [ | |
| "Qwen2.5-1.5B-Instruct", | |
| "Llama-3.2-1B-Instruct", | |
| "Gemma-2-2B-IT", | |
| "SmolLM2-1.7B", | |
| "Phi-3.5-mini", | |
| "TinyLlama-1.1B", | |
| "idle", | |
| ] | |
| def generate_farm_status(): | |
| """Generate realistic live status for all phones.""" | |
| rows = [] | |
| for p in PHONES: | |
| status = random.choices(STATUSES, weights=STATUS_WEIGHTS)[0] | |
| if status == "offline": | |
| battery = random.randint(0, 15) | |
| temp = 0 | |
| tps = 0 | |
| model = "—" | |
| uptime = 0 | |
| elif status == "charging": | |
| battery = random.randint(60, 100) | |
| temp = random.randint(28, 35) | |
| tps = 0 | |
| model = "charging" | |
| uptime = random.randint(1, 720) | |
| elif status == "idle": | |
| battery = random.randint(70, 100) | |
| temp = random.randint(25, 32) | |
| tps = 0 | |
| model = "idle" | |
| uptime = random.randint(1, 1440) | |
| else: # running | |
| battery = random.randint(55, 100) | |
| # S20 FE runs ~12-19 t/s depending on model | |
| base_tps = {"s20fe": 16, "s22": 24, "a54": 14, "pixel7": 20, "op11": 30, "redmi12": 10} | |
| model_key = p["id"].split("-")[0] | |
| tps = base_tps.get(model_key, 15) + random.uniform(-2, 2) | |
| temp = random.randint(33, 42) | |
| model = random.choice([m for m in CURRENT_MODELS if m != "idle"]) | |
| uptime = random.randint(1, 1440) | |
| rows.append({ | |
| "Device ID": p["id"], | |
| "Model": p["model"], | |
| "SoC": p["soc"], | |
| "Status": status, | |
| "Battery (%)": battery, | |
| "Temp (°C)": temp, | |
| "Inference (t/s)": round(tps, 1) if tps else 0, | |
| "Current Model": model, | |
| "Uptime (min)": uptime, | |
| }) | |
| return pd.DataFrame(rows) | |
| def get_farm_summary(df): | |
| """Calculate summary stats from the farm status dataframe.""" | |
| total = len(df) | |
| running = len(df[df["Status"] == "running"]) | |
| idle = len(df[df["Status"] == "idle"]) | |
| charging = len(df[df["Status"] == "charging"]) | |
| offline = len(df[df["Status"] == "offline"]) | |
| avg_battery = df["Battery (%)"].mean() | |
| avg_temp = df[df["Status"] != "offline"]["Temp (°C)"].mean() | |
| avg_tps = df[df["Inference (t/s)"] > 0]["Inference (t/s)"].mean() | |
| total_tps = df["Inference (t/s)"].sum() | |
| summary = f""" | |
| ### 📊 Farm Summary — {TOTAL_PHONES} Devices | |
| | Metric | Value | | |
| |--------|-------| | |
| | 🟢 Running | {running} | | |
| | ⚪ Idle | {idle} | | |
| | 🔌 Charging | {charging} | | |
| | 🔴 Offline | {offline} | | |
| | 🔋 Avg Battery | {avg_battery:.1f}% | | |
| | 🌡️ Avg Temp | {avg_temp:.1f}°C | | |
| | ⚡ Avg Inference | {avg_tps:.1f} t/s | | |
| | 🚀 Total Throughput | {total_tps:.1f} t/s | | |
| | 🕐 Updated | {time.strftime("%H:%M:%S")} | | |
| """ | |
| return summary | |
| def refresh(): | |
| """Generate fresh farm data and summary.""" | |
| df = generate_farm_status() | |
| return df, get_farm_summary(df) | |
| def filter_by_status(status_filter, df): | |
| if not status_filter or status_filter == "All": | |
| return df | |
| return df[df["Status"] == status_filter] | |
| # --- UI ----------------------------------------------------------------------- | |
| CSS = """ | |
| #dispatch-header h1 { | |
| color: #FFFFFF; font-size: 2.2rem; margin: 0; | |
| background: linear-gradient(90deg, #1FE0E6 0%, #FFFFFF 60%); | |
| -webkit-background-clip: text; -webkit-text-fill-color: transparent; | |
| } | |
| #dispatch-header p { color: #1FE0E6; font-size: 1.05rem; margin: 6px 0 0 0; } | |
| .dispatch-footer { text-align: center; color: #8A8F9C; font-size: 0.9rem; padding-top: 8px; } | |
| .status-running { color: #1FE0E6; font-weight: bold; } | |
| .status-idle { color: #8A8F9C; } | |
| .status-charging { color: #FFD700; } | |
| .status-offline { color: #FF4444; } | |
| """ | |
| with gr.Blocks( | |
| title="Dispatch AI — Phone Farm Dashboard", | |
| theme=gr.themes.Base( | |
| primary_hue="cyan", secondary_hue="cyan", neutral_hue="slate", | |
| font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui"], | |
| ).set( | |
| body_background_fill="#0A0F1A", body_background_fill_dark="#0A0F1A", | |
| body_text_color="#FFFFFF", body_text_color_dark="#FFFFFF", | |
| block_background_fill="#0E1424", block_background_fill_dark="#0E1424", | |
| block_border_color="#1FE0E6", block_border_width="1px", | |
| block_label_text_color="#1FE0E6", block_title_text_color="#1FE0E6", | |
| button_primary_background_fill="#1FE0E6", button_primary_background_fill_dark="#1FE0E6", | |
| button_primary_text_color="#0A0F1A", button_primary_border_color="#1FE0E6", | |
| input_background_fill="#0E1424", input_background_fill_dark="#0E1424", | |
| input_border_color="#1FE0E6", input_border_width="1px", | |
| ), | |
| css=CSS, | |
| ) as demo: | |
| with gr.Column(elem_id="dispatch-header"): | |
| gr.Markdown( | |
| """ | |
| # Dispatch AI — Phone Farm Dashboard | |
| Live status of {n} devices · Auto-refresh every 10s · Dispatch AI (FZE) · UAE | |
| """.format(n=TOTAL_PHONES) | |
| ) | |
| with gr.Row(): | |
| refresh_btn = gr.Button("🔄 Refresh Now", variant="primary") | |
| status_filter = gr.Dropdown( | |
| ["All", "running", "idle", "charging", "offline"], | |
| label="Filter by Status", value="All", | |
| ) | |
| with gr.Row(): | |
| summary_box = gr.Markdown() | |
| farm_table = gr.Dataframe( | |
| headers=["Device ID", "Model", "SoC", "Status", "Battery (%)", "Temp (°C)", | |
| "Inference (t/s)", "Current Model", "Uptime (min)"], | |
| datatype=["str", "str", "str", "str", "number", "number", "number", "str", "number"], | |
| interactive=False, wrap=True, | |
| column_widths=[80, 100, 90, 80, 80, 70, 90, 160, 80], | |
| ) | |
| # Auto-refresh timer | |
| timer = gr.Timer(value=10) | |
| timer.tick(fn=refresh, outputs=[farm_table, summary_box]) | |
| # Manual refresh | |
| refresh_btn.click(fn=refresh, outputs=[farm_table, summary_box]) | |
| # Status filter | |
| status_filter.change(fn=filter_by_status, inputs=[status_filter, farm_table], outputs=farm_table) | |
| gr.Markdown( | |
| """ | |
| <div class="dispatch-footer"> | |
| © 2026 Dispatch AI (FZE) · Sharjah, UAE · License 10818 · | |
| {n} devices · Backend: llama.cpp Q4_K_M · Data auto-refreshes every 10 seconds | |
| </div> | |
| """.format(n=TOTAL_PHONES) | |
| ) | |
| if __name__ == "__main__": | |
| demo.queue() | |
| demo.launch() | |