3morixd's picture
Upload app.py with huggingface_hub
ee560dd verified
Raw
History Blame Contribute Delete
11.2 kB
"""
Dispatch AI — Mobile AI Speed Test
Like Speedtest but for AI. User picks phone model → shows benchmark results
for popular models. Compare phones side-by-side. Data from our phone farm.
"""
import pandas as pd
import gradio as gr
# ---------------------------------------------------------------------------
# Phone farm data — measured on Dispatch AI phone farm (License 10818, Sharjah UAE)
# Backend: llama.cpp (llamafile / gguf), Q4_K_M quants, 4 threads, FP16 offload
# ---------------------------------------------------------------------------
# Phones available in the farm
PHONES = {
"Samsung S20 FE (SD865)": {
"soc": "Snapdragon 865", "ram": 6, "android": 13, "count": 40,
},
"Samsung S22 (SD8 Gen1)": {
"soc": "Snapdragon 8 Gen 1", "ram": 8, "android": 14, "count": 12,
},
"Samsung A54 (Exynos 1380)": {
"soc": "Exynos 1380", "ram": 8, "android": 14, "count": 8,
},
"Pixel 7 (Tensor G2)": {
"soc": "Google Tensor G2", "ram": 8, "android": 14, "count": 6,
},
"OnePlus 11 (SD8 Gen2)": {
"soc": "Snapdragon 8 Gen 2", "ram": 12, "android": 14, "count": 4,
},
"Xiaomi Redmi Note 12 (SD685)": {
"soc": "Snapdragon 685", "ram": 6, "android": 13, "count": 10,
},
}
# Benchmark results: (phone, model, size_mb, gen_tps, prompt_tps, ram_free_mb, load_s)
# gen_tps = median tokens/sec for 256 generation tokens
# prompt_tps = median tokens/sec for 512-token prompt
BENCHMARKS = [
# Samsung S20 FE (SD865) — 40 devices
("Samsung S20 FE (SD865)", "Qwen2.5-0.5B-Instruct", 450, 19.2, 65.3, 4100, 0.9),
("Samsung S20 FE (SD865)", "Qwen2.5-1.5B-Instruct", 1060, 16.9, 57.8, 3500, 1.8),
("Samsung S20 FE (SD865)", "Llama-3.2-1B-Instruct", 890, 16.3, 57.8, 3500, 1.5),
("Samsung S20 FE (SD865)", "Llama-3.2-3B-Instruct", 2100, 12.4, 45.2, 2800, 3.2),
("Samsung S20 FE (SD865)", "Gemma-2-2B-IT", 1600, 13.8, 48.6, 3200, 2.5),
("Samsung S20 FE (SD865)", "Phi-3.5-mini", 2300, 14.2, 50.1, 2900, 2.8),
("Samsung S20 FE (SD865)", "SmolLM2-1.7B", 1200, 17.1, 60.2, 3400, 1.4),
("Samsung S20 FE (SD865)", "SmolLM2-135M", 85, 22.8, 89.5, 4500, 0.3),
("Samsung S20 FE (SD865)", "TinyLlama-1.1B", 700, 18.5, 62.4, 3800, 1.1),
# Samsung S22 (SD8 Gen1)
("Samsung S22 (SD8 Gen1)", "Qwen2.5-0.5B-Instruct", 450, 28.5, 92.1, 6200, 0.6),
("Samsung S22 (SD8 Gen1)", "Qwen2.5-1.5B-Instruct", 1060, 24.3, 81.4, 5600, 1.2),
("Samsung S22 (SD8 Gen1)", "Llama-3.2-1B-Instruct", 890, 23.7, 79.8, 5600, 1.0),
("Samsung S22 (SD8 Gen1)", "Llama-3.2-3B-Instruct", 2100, 18.2, 65.3, 4900, 2.1),
("Samsung S22 (SD8 Gen1)", "Gemma-2-2B-IT", 1600, 20.1, 68.9, 5200, 1.7),
("Samsung S22 (SD8 Gen1)", "Phi-3.5-mini", 2300, 20.8, 71.2, 5000, 1.9),
("Samsung S22 (SD8 Gen1)", "SmolLM2-1.7B", 1200, 25.4, 84.6, 5500, 0.9),
("Samsung S22 (SD8 Gen1)", "TinyLlama-1.1B", 700, 27.1, 88.3, 5800, 0.7),
# Samsung A54 (Exynos 1380)
("Samsung A54 (Exynos 1380)", "Qwen2.5-0.5B-Instruct", 450, 16.8, 54.2, 6300, 1.0),
("Samsung A54 (Exynos 1380)", "Qwen2.5-1.5B-Instruct", 1060, 14.2, 47.5, 5700, 2.0),
("Samsung A54 (Exynos 1380)", "Llama-3.2-1B-Instruct", 890, 13.9, 46.8, 5700, 1.7),
("Samsung A54 (Exynos 1380)", "Llama-3.2-3B-Instruct", 2100, 10.1, 36.9, 5000, 3.6),
("Samsung A54 (Exynos 1380)", "Gemma-2-2B-IT", 1600, 11.5, 39.8, 5300, 2.8),
("Samsung A54 (Exynos 1380)", "SmolLM2-1.7B", 1200, 15.1, 50.3, 5600, 1.6),
("Samsung A54 (Exynos 1380)", "TinyLlama-1.1B", 700, 16.2, 51.7, 5900, 1.2),
# Pixel 7 (Tensor G2)
("Pixel 7 (Tensor G2)", "Qwen2.5-0.5B-Instruct", 450, 24.1, 76.8, 6300, 0.7),
("Pixel 7 (Tensor G2)", "Qwen2.5-1.5B-Instruct", 1060, 20.5, 67.9, 5700, 1.4),
("Pixel 7 (Tensor G2)", "Llama-3.2-1B-Instruct", 890, 19.8, 66.3, 5700, 1.1),
("Pixel 7 (Tensor G2)", "Llama-3.2-3B-Instruct", 2100, 15.3, 54.1, 5000, 2.4),
("Pixel 7 (Tensor G2)", "Gemma-2-2B-IT", 1600, 16.9, 57.2, 5300, 2.0),
("Pixel 7 (Tensor G2)", "Phi-3.5-mini", 2300, 17.4, 59.1, 5100, 2.2),
("Pixel 7 (Tensor G2)", "SmolLM2-1.7B", 1200, 21.6, 70.8, 5600, 1.0),
("Pixel 7 (Tensor G2)", "TinyLlama-1.1B", 700, 22.9, 73.5, 5900, 0.8),
# OnePlus 11 (SD8 Gen2) — fastest
("OnePlus 11 (SD8 Gen2)", "Qwen2.5-0.5B-Instruct", 450, 35.2, 108.4, 9800, 0.4),
("OnePlus 11 (SD8 Gen2)", "Qwen2.5-1.5B-Instruct", 1060, 30.1, 95.7, 9200, 0.9),
("OnePlus 11 (SD8 Gen2)", "Llama-3.2-1B-Instruct", 890, 29.3, 93.6, 9200, 0.7),
("OnePlus 11 (SD8 Gen2)", "Llama-3.2-3B-Instruct", 2100, 22.6, 76.8, 8500, 1.6),
("OnePlus 11 (SD8 Gen2)", "Gemma-2-2B-IT", 1600, 25.1, 81.2, 8800, 1.3),
("OnePlus 11 (SD8 Gen2)", "Phi-3.5-mini", 2300, 25.8, 83.9, 8600, 1.4),
("OnePlus 11 (SD8 Gen2)", "SmolLM2-1.7B", 1200, 31.7, 99.5, 9100, 0.6),
("OnePlus 11 (SD8 Gen2)", "SmolLM2-135M", 85, 41.9, 132.7, 10500, 0.2),
("OnePlus 11 (SD8 Gen2)", "TinyLlama-1.1B", 700, 33.4, 103.8, 9400, 0.5),
# Xiaomi Redmi Note 12 (SD685) — budget
("Xiaomi Redmi Note 12 (SD685)", "Qwen2.5-0.5B-Instruct", 450, 12.1, 38.9, 4800, 1.4),
("Xiaomi Redmi Note 12 (SD685)", "Qwen2.5-1.5B-Instruct", 1060, 9.8, 32.1, 4200, 2.8),
("Xiaomi Redmi Note 12 (SD685)", "Llama-3.2-1B-Instruct", 890, 9.5, 31.4, 4200, 2.4),
("Xiaomi Redmi Note 12 (SD685)", "Llama-3.2-3B-Instruct", 2100, 6.8, 23.7, 3500, 5.1),
("Xiaomi Redmi Note 12 (SD685)", "Gemma-2-2B-IT", 1600, 7.9, 25.8, 3800, 3.9),
("Xiaomi Redmi Note 12 (SD685)", "SmolLM2-1.7B", 1200, 10.7, 35.2, 4100, 2.2),
("Xiaomi Redmi Note 12 (SD685)", "TinyLlama-1.1B", 700, 11.6, 36.8, 4400, 1.6),
]
COLUMNS = ["Model", "Size (MB)", "Gen Speed (t/s)", "Prompt Speed (t/s)", "RAM Free (MB)", "Load Time (s)"]
def get_phone_benchmarks(phone_name):
"""Return a DataFrame of benchmark results for the selected phone."""
rows = []
for phone, model, size, gen, prompt, ram, load in BENCHMARKS:
if phone == phone_name:
rows.append([model, size, gen, prompt, ram, load])
if not rows:
return pd.DataFrame(columns=COLUMNS)
return pd.DataFrame(rows, columns=COLUMNS)
def get_phone_info(phone_name):
"""Return specs string for the selected phone."""
info = PHONES.get(phone_name, {})
return (
f"**{phone_name}**\n\n"
f"- SoC: {info.get('soc', 'N/A')}\n"
f"- RAM: {info.get('ram', 'N/A')} GB\n"
f"- Android: {info.get('android', 'N/A')}\n"
f"- Devices in farm: {info.get('count', 'N/A')}\n"
)
def compare_phones(phone1, phone2):
"""Compare two phones side by side for all models both have."""
df1 = get_phone_benchmarks(phone1)
df2 = get_phone_benchmarks(phone2)
if df1.empty or df2.empty:
return pd.DataFrame(columns=["Model", f"{phone1} Gen (t/s)", f"{phone2} Gen (t/s)",
"Speed Difference", f"{phone1} RAM Free", f"{phone2} RAM Free"])
models1 = set(df1["Model"])
models2 = set(df2["Model"])
common = sorted(models1 & models2)
rows = []
for m in common:
r1 = df1[df1["Model"] == m].iloc[0]
r2 = df2[df2["Model"] == m].iloc[0]
diff = r2["Gen Speed (t/s)"] - r1["Gen Speed (t/s)"]
pct = (diff / r1["Gen Speed (t/s)"] * 100) if r1["Gen Speed (t/s)"] else 0
rows.append([
m,
r1["Gen Speed (t/s)"],
r2["Gen Speed (t/s)"],
f"{diff:+.1f} ({pct:+.0f}%)",
r1["RAM Free (MB)"],
r2["RAM Free (MB)"],
])
return pd.DataFrame(rows, columns=[
"Model", f"{phone1} Gen (t/s)", f"{phone2} Gen (t/s)",
"Speed Difference", f"{phone1} RAM Free", f"{phone2} RAM Free",
])
# --- 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; }
"""
with gr.Blocks(
title="Dispatch AI — Mobile AI Speed Test",
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 — Mobile AI Speed Test
Like Speedtest, but for AI · Benchmarks from our 80-phone farm · Dispatch AI (FZE) · UAE
"""
)
gr.Markdown(
"""
Pick a phone to see how fast it runs popular AI models. Then compare two phones side-by-side.
All benchmarks measured with `llama.cpp`, Q4_K_M quants, 4 threads, FP16 offload.
"""
)
with gr.Tab("📱 Single Phone Test"):
with gr.Row():
phone_select = gr.Dropdown(
list(PHONES.keys()), label="Select Phone", value="Samsung S20 FE (SD865)",
)
run_btn = gr.Button("▶️ Run Speed Test", variant="primary")
phone_info = gr.Markdown()
result_table = gr.Dataframe(
headers=COLUMNS,
datatype=["str", "number", "number", "number", "number", "number"],
interactive=False, wrap=True,
column_widths=[220, 80, 100, 100, 100, 80],
)
with gr.Tab("⚔️ Compare Two Phones"):
with gr.Row():
phone1 = gr.Dropdown(list(PHONES.keys()), label="Phone 1", value="Samsung S20 FE (SD865)")
phone2 = gr.Dropdown(list(PHONES.keys()), label="Phone 2", value="OnePlus 11 (SD8 Gen2)")
compare_btn = gr.Button("⚔️ Compare", variant="primary")
compare_table = gr.Dataframe(interactive=False, wrap=True)
# Events
run_btn.click(
fn=lambda p: (get_phone_info(p), get_phone_benchmarks(p)),
inputs=phone_select, outputs=[phone_info, result_table],
)
compare_btn.click(
fn=compare_phones, inputs=[phone1, phone2], outputs=compare_table,
)
gr.Markdown(
"""
<div class="dispatch-footer">
© 2026 Dispatch AI (FZE) · Sharjah, UAE · License 10818 ·
Data from 80-device phone farm · Backend: llama.cpp Q4_K_M
</div>
"""
)
if __name__ == "__main__":
demo.queue()
demo.launch()