3morixd commited on
Commit
153626c
Β·
verified Β·
1 Parent(s): 8957d62

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +268 -54
app.py CHANGED
@@ -1,80 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
- import pandas as pd
3
-
4
- MODELS = [
5
- {"Model": "SmolLM2-135M-Instruct-mobile", "Params": "135M", "Size_MB": 270, "RAM_MB": 400, "Task": "Chat", "Quant": "FP16", "Speed_tps": 25.5},
6
- {"Model": "SmolLM2-360M-Instruct-mobile", "Params": "360M", "Size_MB": 720, "RAM_MB": 700, "Task": "Chat", "Quant": "FP16", "Speed_tps": 21.0},
7
- {"Model": "Qwen2.5-0.5B-Instruct-mobile-int4", "Params": "500M", "Size_MB": 350, "RAM_MB": 550, "Task": "Chat", "Quant": "INT4", "Speed_tps": 20.0},
8
- {"Model": "Llama-3.2-1B-Instruct-Q4-mobile", "Params": "1B", "Size_MB": 700, "RAM_MB": 1100, "Task": "Chat", "Quant": "Q4", "Speed_tps": 18.2},
9
- {"Model": "Llama-3.2-1B-Instruct-Q6-mobile", "Params": "1B", "Size_MB": 1100, "RAM_MB": 1300, "Task": "Chat", "Quant": "Q6", "Speed_tps": 16.8},
10
- {"Model": "TinyLlama-1.1B-Chat-Q5-mobile", "Params": "1.1B", "Size_MB": 800, "RAM_MB": 1200, "Task": "Chat", "Quant": "Q5", "Speed_tps": 17.5},
11
- {"Model": "Qwen2.5-0.5B-Coder-mobile", "Params": "500M", "Size_MB": 1000, "RAM_MB": 1500, "Task": "Code", "Quant": "FP16", "Speed_tps": 20.0},
12
- {"Model": "Qwen2.5-Coder-1.5B-mobile", "Params": "1.5B", "Size_MB": 3000, "RAM_MB": 4000, "Task": "Code", "Quant": "FP16", "Speed_tps": 10.5},
13
- {"Model": "Qwen2.5-Math-1.5B-mobile", "Params": "1.5B", "Size_MB": 3000, "RAM_MB": 4000, "Task": "Math", "Quant": "FP16", "Speed_tps": 10.5},
14
- {"Model": "Gemma-2B-Arabic-mobile", "Params": "2B", "Size_MB": 5000, "RAM_MB": 5500, "Task": "Arabic", "Quant": "FP16", "Speed_tps": 8.0},
15
- {"Model": "Gemma-2-2B-IT-Q5-mobile", "Params": "2B", "Size_MB": 1500, "RAM_MB": 2200, "Task": "Chat", "Quant": "Q5", "Speed_tps": 12.0},
16
- {"Model": "Llama-3.2-3B-Instruct-Q5-mobile", "Params": "3B", "Size_MB": 2100, "RAM_MB": 2700, "Task": "Chat", "Quant": "Q5", "Speed_tps": 8.5},
17
- {"Model": "Llama-3.2-1B-FunctionCall-mobile", "Params": "1B", "Size_MB": 2500, "RAM_MB": 3000, "Task": "Function Call", "Quant": "FP16", "Speed_tps": 12.0},
18
- {"Model": "Moondream2-Vision-Q5-mobile", "Params": "1.9B", "Size_MB": 1400, "RAM_MB": 2000, "Task": "Vision", "Quant": "Q5", "Speed_tps": 8.5},
19
- {"Model": "EmbeddingGemma-300M-Q8-mobile", "Params": "300M", "Size_MB": 300, "RAM_MB": 500, "Task": "Embedding", "Quant": "Q8", "Speed_tps": 22.0},
20
- ]
21
 
22
- df = pd.DataFrame(MODELS)
23
 
24
- PHONE_PROFILES = {
25
- "Low-end (2GB RAM)": 2048,
26
- "Mid-range (4GB RAM)": 4096,
27
- "High-end (6GB RAM)": 6144,
28
- "Flagship (8GB+ RAM)": 8192,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
 
31
- TASKS = ["Chat", "Code", "Math", "Arabic", "Function Call", "Vision", "Embedding", "Any"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- def recommend(phone_profile, task, priority):
34
- ram = PHONE_PROFILES[phone_profile]
35
- filtered = df.copy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- if task != "Any":
38
- filtered = filtered[filtered["Task"] == task]
39
 
40
- filtered = filtered[filtered["RAM_MB"] <= ram]
 
 
 
 
41
 
42
- if len(filtered) == 0:
43
- return pd.DataFrame([{"Error": f"No models fit in {ram}MB RAM for task '{task}'. Try a different phone or task."}])
 
44
 
45
- if priority == "Smallest size":
46
- filtered = filtered.sort_values("Size_MB")
47
- elif priority == "Fastest":
48
- filtered = filtered.sort_values("Speed_tps", ascending=False)
49
- elif priority == "Best quality":
50
- # Quality roughly correlates with params and quant level
51
- filtered = filtered.sort_values(["Params"], ascending=False)
52
 
53
- return filtered.head(5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), title="dispatchAI Model Recommender") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  gr.Markdown("""
57
- # πŸ“± dispatchAI Mobile Model Recommender
58
 
59
- Find the perfect dispatchAI model for your phone and use case.
 
 
60
  """)
61
 
62
  with gr.Row():
63
- phone = gr.Dropdown(choices=list(PHONE_PROFILES.keys()), value="Mid-range (4GB RAM)", label="Your Phone")
64
- task = gr.Dropdown(choices=TASKS, value="Chat", label="Primary Task")
65
- priority = gr.Radio(["Smallest size", "Fastest", "Best quality"], value="Smallest size", label="Priority")
 
 
 
 
 
 
 
 
 
66
 
67
- btn = gr.Button("Find My Model", variant="primary", size="lg")
68
- table = gr.DataFrame(label="Recommended Models")
69
 
70
- btn.click(fn=recommend, inputs=[phone, task, priority], outputs=table)
71
- demo.load(fn=recommend, inputs=[phone, task, priority], outputs=table)
 
 
 
72
 
73
  gr.Markdown("""
74
  ---
75
- All benchmarks measured on **Snapdragon 865 (Samsung S20 FE)**.
 
 
 
 
 
 
76
 
77
- πŸš€ [dispatchAI](https://huggingface.co/dispatchAI) β€” Small. Mobile. Free. UAE-built.
 
 
 
 
 
 
 
 
78
  """)
79
 
80
  if __name__ == "__main__":
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ #301: On-device readiness checker β€” a Gradio Space that evaluates whether a
4
+ given model will run on a mobile device.
5
+
6
+ Paste a HuggingFace model ID or upload a config, get a "will it run on a phone?" report:
7
+ - Parameter count vs memory budget
8
+ - Architecture compatibility
9
+ - Quantization recommendations
10
+ - Estimated phone farm performance
11
+ - Recommended dispatchAI model alternatives
12
+ """
13
  import gradio as gr
14
+ import json
15
+ import requests
16
+ from huggingface_hub import hf_hub_download, HfApi
17
+ import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ token = os.environ.get("HF_TOKEN", "")
20
 
21
+ # Phone farm specs
22
+ PHONE_SPECS = {
23
+ "Samsung S20 FE (Snapdragon 865, 8GB)": {
24
+ "chipset": "Snapdragon 865",
25
+ "ram_gb": 8,
26
+ "usable_ram_gb": 6, # After OS overhead
27
+ "cpu_cores": 8,
28
+ "max_model_size_gb": 4, # Safe limit for 8GB phone
29
+ },
30
+ "Samsung S23 (Snapdragon 8 Gen 2, 8GB)": {
31
+ "chipset": "Snapdragon 8 Gen 2",
32
+ "ram_gb": 8,
33
+ "usable_ram_gb": 6,
34
+ "cpu_cores": 8,
35
+ "max_model_size_gb": 4,
36
+ },
37
+ "iPhone 15 Pro (A17 Pro, 8GB)": {
38
+ "chipset": "Apple A17 Pro",
39
+ "ram_gb": 8,
40
+ "usable_ram_gb": 6,
41
+ "cpu_cores": 6,
42
+ "max_model_size_gb": 4,
43
+ },
44
+ "Budget Android (4GB RAM)": {
45
+ "chipset": "Mid-range",
46
+ "ram_gb": 4,
47
+ "usable_ram_gb": 3,
48
+ "cpu_cores": 8,
49
+ "max_model_size_gb": 2,
50
+ },
51
  }
52
 
53
+ # dispatchAI model catalog for recommendations
54
+ DISPATCHAI_MODELS = [
55
+ {"id": "dispatchAI/SmolLM2-135M-Instruct-mobile", "params_m": 135, "size_mb": 270, "task": "chat"},
56
+ {"id": "dispatchAI/SmolLM2-360M-Instruct-mobile", "params_m": 360, "size_mb": 720, "task": "chat"},
57
+ {"id": "dispatchAI/Qwen2.5-0.5B-Instruct-mobile-int4", "params_m": 500, "size_mb": 350, "task": "chat"},
58
+ {"id": "dispatchAI/Qwen2.5-0.5B-Coder-mobile", "params_m": 500, "size_mb": 350, "task": "code"},
59
+ {"id": "dispatchAI/Llama-3.2-1B-Instruct-mobile", "params_m": 1000, "size_mb": 2000, "task": "chat"},
60
+ {"id": "dispatchAI/TinyLlama-1.1B-Chat-Q5-mobile", "params_m": 1100, "size_mb": 450, "task": "chat"},
61
+ {"id": "dispatchAI/Qwen2.5-1.5B-Instruct-Q5-mobile", "params_m": 1500, "size_mb": 900, "task": "chat"},
62
+ {"id": "dispatchAI/Gemma-2-2B-IT-Q5-mobile", "params_m": 2000, "size_mb": 1300, "task": "chat"},
63
+ {"id": "dispatchAI/Phi-3.5-mini-instruct-Q5-mobile", "params_m": 2000, "size_mb": 1300, "task": "chat"},
64
+ {"id": "dispatchAI/Gemma-2B-Arabic-mobile", "params_m": 2000, "size_mb": 1300, "task": "arabic"},
65
+ ]
66
+
67
+ def fetch_model_info(model_id):
68
+ """Fetch config.json from HuggingFace."""
69
+ try:
70
+ config_path = hf_hub_download(model_id, "config.json", token=token)
71
+ with open(config_path, "r") as f:
72
+ config = json.load(f)
73
+
74
+ # Try to get model size from safetensors
75
+ api = HfApi(token=token)
76
+ files = api.list_repo_files(model_id, token=token)
77
+
78
+ size_mb = 0
79
+ for f in files:
80
+ if f.endswith(".safetensors") or f.endswith(".bin") or f.endswith(".gguf"):
81
+ try:
82
+ info = api.get_paths_info(model_id, [f], repo_type="model", token=token)
83
+ if info and hasattr(info[0], 'size'):
84
+ size_mb += info[0].size / 1e6
85
+ except:
86
+ pass
87
+
88
+ return config, size_mb
89
+ except Exception as e:
90
+ return None, str(e)
91
 
92
+ def estimate_params(config):
93
+ """Estimate parameter count from config."""
94
+ try:
95
+ hidden = config.get("hidden_size", 0)
96
+ layers = config.get("num_hidden_layers", config.get("num_layers", 0))
97
+ vocab = config.get("vocab_size", 0)
98
+ intermediate = config.get("intermediate_size", hidden * 4)
99
+
100
+ # Rough estimate: transformers params
101
+ # Attention: 4 * hidden^2 per layer (Q, K, V, O)
102
+ # MLP: 2 * hidden * intermediate per layer
103
+ # Embeddings: vocab * hidden
104
+ attention_params = 4 * hidden * hidden * layers
105
+ mlp_params = 2 * hidden * intermediate * layers
106
+ embed_params = vocab * hidden
107
+
108
+ total = attention_params + mlp_params + embed_params
109
+ return total / 1e6 # in millions
110
+ except:
111
+ return 0
112
+
113
+ def check_readiness(model_id, target_device):
114
+ """Check if a model will run on the target device."""
115
+ if not model_id.strip():
116
+ return "Please enter a HuggingFace model ID."
117
+
118
+ config_result = fetch_model_info(model_id.strip())
119
+
120
+ if isinstance(config_result[1], str) and not config_result[0]:
121
+ return f"❌ **Error fetching model info**: {config_result[1]}\n\nCheck the model ID and try again."
122
+
123
+ config, size_mb = config_result
124
+ if not config:
125
+ return f"❌ Could not fetch config for `{model_id}`"
126
 
127
+ specs = PHONE_SPECS.get(target_device, PHONE_SPECS["Samsung S20 FE (Snapdragon 865, 8GB)"])
 
128
 
129
+ # Estimate parameters
130
+ params_m = estimate_params(config)
131
+ model_type = config.get("model_type", "unknown")
132
+ hidden_size = config.get("hidden_size", 0)
133
+ num_layers = config.get("num_hidden_layers", 0)
134
 
135
+ # If we couldn't get size from API, estimate it
136
+ if size_mb == 0:
137
+ size_mb = params_m * 2 # fp16: 2 bytes per param
138
 
139
+ # Estimates for different quantizations
140
+ size_fp16_mb = params_m * 2
141
+ size_q8_mb = params_m * 1
142
+ size_q5_mb = params_m * 0.625
143
+ size_q4_mb = params_m * 0.5
 
 
144
 
145
+ # Phone farm performance estimate (based on real benchmarks)
146
+ # S20 FE: ~18 t/s for 135M, ~10 t/s for 500M, ~6 t/s for 1B, ~3 t/s for 2B
147
+ if params_m < 200:
148
+ est_tps = "15-20 t/s"
149
+ rating = "🟒 Excellent"
150
+ elif params_m < 600:
151
+ est_tps = "8-12 t/s"
152
+ rating = "🟒 Good"
153
+ elif params_m < 1200:
154
+ est_tps = "5-7 t/s"
155
+ rating = "🟑 Usable"
156
+ elif params_m < 2500:
157
+ est_tps = "2-4 t/s"
158
+ rating = "🟠 Slow"
159
+ else:
160
+ est_tps = "< 2 t/s"
161
+ rating = "πŸ”΄ Too large"
162
+
163
+ # Memory check
164
+ fits_fp16 = size_fp16_mb < specs["max_model_size_gb"] * 1024
165
+ fits_q5 = size_q5_mb < specs["max_model_size_gb"] * 1024
166
+ fits_q4 = size_q4_mb < specs["max_model_size_gb"] * 1024
167
+
168
+ # Find recommended dispatchAI alternatives
169
+ recommendations = []
170
+ for m in DISPATCHAI_MODELS:
171
+ if m["params_m"] <= params_m * 1.2 and m["params_m"] >= params_m * 0.5:
172
+ recommendations.append(m)
173
+ if not recommendations:
174
+ # Find closest smaller model
175
+ smaller = [m for m in DISPATCHAI_MODELS if m["params_m"] < params_m]
176
+ if smaller:
177
+ recommendations = sorted(smaller, key=lambda x: x["params_m"], reverse=True)[:3]
178
+
179
+ rec_text = "\n".join([f"- [`{m['id']}`](https://huggingface.co/{m['id']}) β€” {m['params_m']}M params, {m['size_mb']}MB"
180
+ for m in recommendations[:5]])
181
+
182
+ report = f"""## πŸ“± On-Device Readiness Report
183
+
184
+ ### Model: `{model_id}`
185
+
186
+ | Property | Value |
187
+ |----------|-------|
188
+ | Architecture | {model_type} |
189
+ | Hidden size | {hidden_size} |
190
+ | Layers | {num_layers} |
191
+ | Estimated params | ~{params_m:.0f}M |
192
+
193
+ ### Size estimates by quantization
194
+
195
+ | Format | Size | Fits {target_device.split('(')[0].strip()}? |
196
+ |--------|------|------|
197
+ | FP16 | {size_fp16_mb:.0f}MB | {"βœ…" if fits_fp16 else "❌"} |
198
+ | Q8 | {size_q8_mb:.0f}MB | {"βœ…" if fits_q8 else "❌"} |
199
+ | Q5_K_M | {size_q5_mb:.0f}MB | {"βœ…" if fits_q5 else "❌"} |
200
+ | Q4_K_M | {size_q4_mb:.0f}MB | {"βœ…" if fits_q4 else "❌"} |
201
 
202
+ ### Performance estimate (Snapdragon 865)
203
+
204
+ | Metric | Value |
205
+ |--------|-------|
206
+ | Estimated speed | {est_tps} |
207
+ | Readiness | {rating} |
208
+
209
+ ### Target device: {target_device}
210
+
211
+ | Property | Value |
212
+ |----------|-------|
213
+ | Chipset | {specs['chipset']} |
214
+ | RAM | {specs['ram_gb']}GB |
215
+ | Max model size | {specs['max_model_size_gb']}GB |
216
+
217
+ ### Recommended dispatchAI alternatives
218
+
219
+ {rec_text if rec_text else "No close matches found."}
220
+
221
+ ### Recommendation
222
+
223
+ """
224
+ if "🟒" in rating:
225
+ report += "βœ… **This model is ready for mobile deployment.** Use Q4_K_M or Q5_K_M GGUF for best size/quality balance."
226
+ elif "🟑" in rating:
227
+ report += "⚠️ **This model is usable but may be slow.** Consider Q4_K_M quantization and test on target hardware."
228
+ elif "🟠" in rating:
229
+ report += "⚠️ **This model will be slow on mobile.** Consider a smaller alternative from dispatchAI."
230
+ else:
231
+ report += "❌ **This model is too large for mobile deployment.** Use a dispatchAI alternative above."
232
+
233
+ return report
234
+
235
+ # Custom CSS
236
+ custom_css = """
237
+ .gradio-container { background: #0A0F1A !important; color: #F5F7FA !important; }
238
+ h1, h2, h3 { color: #1FE0E6 !important; }
239
+ .gr-button { background: linear-gradient(135deg, #2E6BFF, #1FE0E6) !important; color: #0A0F1A !important; }
240
+ """
241
+
242
+ with gr.Blocks(css=custom_css, title="On-Device Readiness Checker") as demo:
243
  gr.Markdown("""
244
+ # πŸ“± On-Device Readiness Checker
245
 
246
+ **Will your model run on a phone?** Paste a HuggingFace model ID and find out.
247
+
248
+ Powered by [dispatchAI](https://huggingface.co/dispatchAI) β€” mobile AI that runs.
249
  """)
250
 
251
  with gr.Row():
252
+ model_input = gr.Textbox(
253
+ label="HuggingFace Model ID",
254
+ placeholder="e.g., Qwen/Qwen2.5-0.5B-Instruct",
255
+ scale=3
256
+ )
257
+ device_input = gr.Dropdown(
258
+ choices=list(PHONE_SPECS.keys()),
259
+ value="Samsung S20 FE (Snapdragon 865, 8GB)",
260
+ label="Target Device",
261
+ scale=2
262
+ )
263
+ check_btn = gr.Button("Check Readiness", variant="primary", scale=1)
264
 
265
+ report_output = gr.Markdown(label="Readiness Report")
 
266
 
267
+ check_btn.click(
268
+ fn=check_readiness,
269
+ inputs=[model_input, device_input],
270
+ outputs=report_output
271
+ )
272
 
273
  gr.Markdown("""
274
  ---
275
+ ### How it works
276
+
277
+ 1. Fetches the model's `config.json` from HuggingFace
278
+ 2. Estimates parameter count and size for each quantization level
279
+ 3. Compares against the target device's memory budget
280
+ 4. Estimates inference speed based on real phone farm benchmarks
281
+ 5. Recommends dispatchAI mobile-optimized alternatives
282
 
283
+ ### Try these models
284
+
285
+ - `Qwen/Qwen2.5-0.5B-Instruct` β€” small model, should pass
286
+ - `Qwen/Qwen2.5-7B-Instruct` β€” large model, should fail
287
+ - `meta-llama/Llama-3.2-1B-Instruct` β€” borderline
288
+ - `HuggingFaceTB/SmolLM2-135M-Instruct` β€” tiny, excellent
289
+
290
+ ---
291
+ *Dispatch AI (FZE), Sharjah SRTI Free Zone, License No. 10818.*
292
  """)
293
 
294
  if __name__ == "__main__":