3morixd's picture
Upload app.py with huggingface_hub
3ff9bfb verified
Raw
History Blame Contribute Delete
7.84 kB
"""
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()