HomePilot Bot commited on
Commit
4a4286e
ยท
unverified ยท
1 Parent(s): 7787cf9

feat: HomePilot Installer wizard

Browse files
Files changed (4) hide show
  1. .gitattributes +0 -35
  2. README.md +27 -5
  3. app.py +455 -0
  4. requirements.txt +2 -0
.gitattributes DELETED
@@ -1,35 +0,0 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,12 +1,34 @@
1
  ---
2
  title: HomePilot Installer
3
- emoji: ๐Ÿข
4
  colorFrom: indigo
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 6.12.0
8
  app_file: app.py
9
- pinned: false
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: HomePilot Installer
3
+ emoji: ๐Ÿ 
4
  colorFrom: indigo
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 4.44.1
8
  app_file: app.py
9
+ pinned: true
10
+ short_description: Install HomePilot into your own HF Space
11
  ---
12
 
13
+ # HomePilot Installer
14
+
15
+ Install your own private HomePilot AI assistant on Hugging Face Spaces.
16
+
17
+ ## What it does
18
+
19
+ 1. Authenticates with your HF token
20
+ 2. Creates a new private Docker Space in your account
21
+ 3. Pushes the HomePilot template (backend + frontend + Ollama + 14 Chata personas)
22
+ 4. Guides you to enable GPU hardware
23
+
24
+ ## Requirements
25
+
26
+ - A [Hugging Face account](https://huggingface.co/join)
27
+ - A [write-access token](https://huggingface.co/settings/tokens)
28
+ - (Optional) GPU hardware on the target Space for better performance
29
+
30
+ ## Pre-installed content
31
+
32
+ - **14 Chata social personas** (Starter + Retro packs)
33
+ - **Ollama** with qwen2.5:1.5b (auto-pulled on first start)
34
+ - **HomePilot** full-stack (FastAPI backend + React frontend)
app.py ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HomePilot Installer โ€” Hugging Face Space
3
+
4
+ A Gradio-based wizard that helps users install HomePilot into their own
5
+ private HF Space. Uses the Two-Space Architecture:
6
+
7
+ 1. THIS Space (Installer) โ€” public, lightweight Gradio UI
8
+ 2. User's Space (Builder) โ€” private, Docker + Ollama + GPU
9
+
10
+ The installer:
11
+ - Authenticates via HF token
12
+ - Creates a new private Docker Space in the user's account
13
+ - Pushes the HomePilot template (Dockerfile + backend + frontend + personas)
14
+ - Guides the user to enable GPU hardware
15
+ """
16
+
17
+ import json
18
+ import os
19
+ import subprocess
20
+ import tempfile
21
+ import time
22
+ from pathlib import Path
23
+
24
+ import gradio as gr
25
+
26
+ # โ”€โ”€ Constants โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
27
+
28
+ TEMPLATE_REPO = os.environ.get("TEMPLATE_REPO", "ruslanmv/HomePilot")
29
+ DEFAULT_SPACE_NAME = "HomePilot"
30
+ BUILDER_DIR = Path(__file__).parent.parent / "builder"
31
+
32
+ # If running standalone (not inside the full repo), check local dir
33
+ if not BUILDER_DIR.exists():
34
+ BUILDER_DIR = Path(__file__).parent / "builder"
35
+
36
+ INSTALL_STEPS = [
37
+ {"id": "auth", "label": "Autenticaciรณn", "icon": "๐Ÿ”‘"},
38
+ {"id": "configure", "label": "Configuraciรณn", "icon": "โš™๏ธ"},
39
+ {"id": "install", "label": "Instalaciรณn", "icon": "๐Ÿš€"},
40
+ {"id": "done", "label": "Listo", "icon": "โœ…"},
41
+ ]
42
+
43
+ # โ”€โ”€ CSS (HomePilot dark theme) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
44
+
45
+ CUSTOM_CSS = """
46
+ .gradio-container {
47
+ background: linear-gradient(135deg, #0f0a1a 0%, #1a1030 50%, #0d1117 100%) !important;
48
+ min-height: 100vh;
49
+ }
50
+ .main-header {
51
+ text-align: center;
52
+ padding: 2rem 1rem;
53
+ }
54
+ .main-header h1 {
55
+ background: linear-gradient(135deg, #a855f7, #6366f1, #22d3ee);
56
+ -webkit-background-clip: text;
57
+ -webkit-text-fill-color: transparent;
58
+ font-size: 2.5rem;
59
+ font-weight: 900;
60
+ letter-spacing: -0.02em;
61
+ }
62
+ .main-header p {
63
+ color: #94a3b8;
64
+ font-size: 0.95rem;
65
+ margin-top: 0.5rem;
66
+ }
67
+ .step-card {
68
+ background: rgba(255,255,255,0.03) !important;
69
+ border: 1px solid rgba(255,255,255,0.08) !important;
70
+ border-radius: 12px !important;
71
+ padding: 1.5rem !important;
72
+ }
73
+ .status-badge {
74
+ display: inline-block;
75
+ padding: 4px 12px;
76
+ border-radius: 999px;
77
+ font-size: 0.75rem;
78
+ font-weight: 700;
79
+ text-transform: uppercase;
80
+ letter-spacing: 0.05em;
81
+ }
82
+ .persona-grid {
83
+ display: grid;
84
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
85
+ gap: 8px;
86
+ margin-top: 12px;
87
+ }
88
+ .persona-chip {
89
+ background: rgba(168, 85, 247, 0.1);
90
+ border: 1px solid rgba(168, 85, 247, 0.2);
91
+ border-radius: 8px;
92
+ padding: 8px 12px;
93
+ color: #c084fc;
94
+ font-size: 0.8rem;
95
+ font-weight: 600;
96
+ }
97
+ """
98
+
99
+ # โ”€โ”€ Persona list โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
100
+
101
+ CHATA_PERSONAS = {
102
+ "starter_pack": [
103
+ "Lunalite Greeter", "Chillbro Regular",
104
+ "Curiosa Driver", "Hypekid Reactions",
105
+ ],
106
+ "retro_pack": [
107
+ "Volt Buddy", "Ronin Zero", "Rival Kaiju",
108
+ "Glitchbyte", "Questkid 99", "Sigma Sage",
109
+ "Wildcard Loki", "Oldroot Oracle", "Morphling X",
110
+ "Nova Void",
111
+ ],
112
+ }
113
+
114
+ # โ”€โ”€ Core Functions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
115
+
116
+
117
+ def validate_token(token: str) -> tuple[str, str, str]:
118
+ """Validate HF token and return (status, username, message)."""
119
+ if not token or len(token) < 10:
120
+ return "error", "", "โŒ Token vacรญo o muy corto"
121
+
122
+ try:
123
+ result = subprocess.run(
124
+ ["python3", "-c", f"""
125
+ import requests
126
+ r = requests.get("https://huggingface.co/api/whoami-v2",
127
+ headers={{"Authorization": "Bearer {token}"}}, timeout=10)
128
+ if r.ok:
129
+ d = r.json()
130
+ print(d.get("name", "unknown"))
131
+ else:
132
+ print(f"ERROR:{{r.status_code}}")
133
+ """],
134
+ capture_output=True, text=True, timeout=15
135
+ )
136
+ username = result.stdout.strip()
137
+ if username.startswith("ERROR:"):
138
+ return "error", "", f"โŒ Token invรกlido ({username})"
139
+ if username:
140
+ return "ok", username, f"โœ… Autenticado como **{username}**"
141
+ return "error", "", "โŒ No pude verificar el token"
142
+ except Exception as e:
143
+ return "error", "", f"โŒ Error: {e}"
144
+
145
+
146
+ def create_user_space(token: str, username: str, space_name: str,
147
+ private: bool, model: str) -> tuple[str, str]:
148
+ """Create a new Space in the user's account and push the template."""
149
+ repo_id = f"{username}/{space_name}"
150
+ log_lines = []
151
+
152
+ def log(msg):
153
+ log_lines.append(msg)
154
+ return "\n".join(log_lines)
155
+
156
+ yield log(f"๐Ÿ“ฆ Creando Space **{repo_id}**...")
157
+
158
+ try:
159
+ # Step 1: Create the Space
160
+ import requests
161
+
162
+ r = requests.post(
163
+ "https://huggingface.co/api/repos/create",
164
+ headers={
165
+ "Authorization": f"Bearer {token}",
166
+ "Content-Type": "application/json",
167
+ },
168
+ json={
169
+ "type": "space",
170
+ "name": space_name,
171
+ "private": private,
172
+ "space_sdk": "docker",
173
+ },
174
+ timeout=30,
175
+ )
176
+ if r.ok:
177
+ yield log("โœ… Space creado")
178
+ elif r.status_code == 409:
179
+ yield log("โš ๏ธ Space ya existe โ€” actualizando")
180
+ else:
181
+ yield log(f"โŒ Error creando Space: {r.status_code} {r.text[:200]}")
182
+ return
183
+
184
+ # Step 2: Clone the template
185
+ yield log("๐Ÿ“ฅ Clonando template de HomePilot...")
186
+
187
+ with tempfile.TemporaryDirectory() as tmpdir:
188
+ remote = f"https://user:{token}@huggingface.co/spaces/{repo_id}"
189
+
190
+ # Try to clone existing, or init new
191
+ clone_result = subprocess.run(
192
+ ["git", "-c", "credential.helper=", "clone", "--depth", "1",
193
+ remote, tmpdir + "/space"],
194
+ capture_output=True, text=True, timeout=30,
195
+ )
196
+ space_dir = tmpdir + "/space"
197
+ if clone_result.returncode != 0:
198
+ os.makedirs(space_dir, exist_ok=True)
199
+ subprocess.run(["git", "init", "-b", "main", space_dir],
200
+ capture_output=True, timeout=10)
201
+ subprocess.run(
202
+ ["git", "-C", space_dir, "remote", "add", "origin", remote],
203
+ capture_output=True, timeout=10,
204
+ )
205
+
206
+ # Wipe and copy template
207
+ for item in Path(space_dir).iterdir():
208
+ if item.name != ".git":
209
+ if item.is_dir():
210
+ import shutil
211
+ shutil.rmtree(item)
212
+ else:
213
+ item.unlink()
214
+
215
+ yield log("๐Ÿ“ Copiando archivos del template...")
216
+
217
+ # Copy from the template repo (clone it first)
218
+ template_remote = f"https://user:{token}@huggingface.co/spaces/{TEMPLATE_REPO}"
219
+ template_dir = tmpdir + "/template"
220
+ subprocess.run(
221
+ ["git", "-c", "credential.helper=", "clone", "--depth", "1",
222
+ template_remote, template_dir],
223
+ capture_output=True, text=True, timeout=60,
224
+ )
225
+
226
+ # Copy template files to user space
227
+ import shutil
228
+ for item in Path(template_dir).iterdir():
229
+ if item.name == ".git":
230
+ continue
231
+ dest = Path(space_dir) / item.name
232
+ if item.is_dir():
233
+ shutil.copytree(item, dest)
234
+ else:
235
+ shutil.copy2(item, dest)
236
+
237
+ # Customize README with user's settings
238
+ readme_path = Path(space_dir) / "README.md"
239
+ if readme_path.exists():
240
+ content = readme_path.read_text()
241
+ content = content.replace("ruslanmv/HomePilot", repo_id)
242
+ readme_path.write_text(content)
243
+
244
+ yield log(f"๐Ÿค– Configurando modelo: **{model}**...")
245
+
246
+ # Patch start.sh with custom model
247
+ start_path = Path(space_dir) / "start.sh"
248
+ if start_path.exists():
249
+ content = start_path.read_text()
250
+ content = content.replace(
251
+ 'OLLAMA_MODEL=${OLLAMA_MODEL:-qwen2.5:1.5b}',
252
+ f'OLLAMA_MODEL=${{OLLAMA_MODEL:-{model}}}',
253
+ )
254
+ start_path.write_text(content)
255
+
256
+ yield log("๐Ÿ“ค Subiendo a Hugging Face...")
257
+
258
+ # Setup LFS
259
+ subprocess.run(["git", "lfs", "install", "--local"],
260
+ capture_output=True, cwd=space_dir, timeout=10)
261
+ subprocess.run(
262
+ ["git", "lfs", "track", "*.hpersona", "*.png", "*.jpg",
263
+ "*.webp", "*.svg", "*.woff", "*.woff2"],
264
+ capture_output=True, cwd=space_dir, timeout=10,
265
+ )
266
+
267
+ # Commit and push
268
+ subprocess.run(
269
+ ["git", "-C", space_dir, "-c", "user.email=installer@homepilot.dev",
270
+ "-c", "user.name=HomePilot Installer", "add", "-A"],
271
+ capture_output=True, timeout=30,
272
+ )
273
+ subprocess.run(
274
+ ["git", "-C", space_dir, "-c", "user.email=installer@homepilot.dev",
275
+ "-c", "user.name=HomePilot Installer", "commit", "-m",
276
+ "feat: HomePilot installed via installer wizard"],
277
+ capture_output=True, timeout=30,
278
+ )
279
+
280
+ push_result = subprocess.run(
281
+ ["git", "-C", space_dir, "push", "--force", remote, "HEAD:main"],
282
+ capture_output=True, text=True, timeout=120,
283
+ )
284
+
285
+ if push_result.returncode == 0:
286
+ yield log("โœ… Template subido exitosamente")
287
+ else:
288
+ yield log(f"โŒ Error en push: {push_result.stderr[:300]}")
289
+ return
290
+
291
+ space_url = f"https://huggingface.co/spaces/{repo_id}"
292
+ yield log(f"""
293
+ ๐ŸŽ‰ **ยกInstalaciรณn completa!**
294
+
295
+ Tu HomePilot estรก en: [{repo_id}]({space_url})
296
+
297
+ **Prรณximos pasos:**
298
+ 1. Ve a **Settings โ†’ Hardware** en tu Space
299
+ 2. Selecciona **GPU (T4 small)** para mejor rendimiento
300
+ 3. O dรฉjalo en **CPU basic** (funciona pero mรกs lento)
301
+ 4. Espera ~2 min para el primer arranque (descarga modelo)
302
+
303
+ **Personas pre-instaladas:** 14 Chata personas listas para usar
304
+ """)
305
+
306
+ except Exception as e:
307
+ yield log(f"โŒ Error inesperado: {e}")
308
+
309
+
310
+ # โ”€โ”€ Gradio UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
311
+
312
+ def build_ui():
313
+ with gr.Blocks(
314
+ title="HomePilot Installer",
315
+ css=CUSTOM_CSS,
316
+ theme=gr.themes.Base(
317
+ primary_hue="purple",
318
+ secondary_hue="blue",
319
+ neutral_hue="slate",
320
+ font=gr.themes.GoogleFont("Inter"),
321
+ ),
322
+ ) as app:
323
+
324
+ # โ”€โ”€ Header โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
325
+ gr.HTML("""
326
+ <div class="main-header">
327
+ <h1>๐Ÿ  HomePilot Installer</h1>
328
+ <p>Instala tu propio HomePilot con IA privada en Hugging Face Spaces</p>
329
+ <div style="margin-top: 1rem; display: flex; justify-content: center; gap: 8px;">
330
+ <span class="persona-chip">14 Chata Personas</span>
331
+ <span class="persona-chip">Ollama Built-in</span>
332
+ <span class="persona-chip">GPU Ready</span>
333
+ </div>
334
+ </div>
335
+ """)
336
+
337
+ # โ”€โ”€ Step 1: Auth โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
338
+ with gr.Group(elem_classes="step-card"):
339
+ gr.Markdown("### ๐Ÿ”‘ Paso 1 โ€” Autenticaciรณn")
340
+ gr.Markdown(
341
+ "Necesitas un [token de Hugging Face](https://huggingface.co/settings/tokens) "
342
+ "con permisos de **write**."
343
+ )
344
+ with gr.Row():
345
+ token_input = gr.Textbox(
346
+ label="HF Token",
347
+ placeholder="hf_...",
348
+ type="password",
349
+ scale=3,
350
+ )
351
+ verify_btn = gr.Button("Verificar", variant="primary", scale=1)
352
+ auth_status = gr.Markdown("")
353
+ username_state = gr.State("")
354
+
355
+ # โ”€โ”€ Step 2: Configure โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
356
+ with gr.Group(elem_classes="step-card"):
357
+ gr.Markdown("### โš™๏ธ Paso 2 โ€” Configuraciรณn")
358
+ with gr.Row():
359
+ space_name = gr.Textbox(
360
+ label="Nombre del Space",
361
+ value="HomePilot",
362
+ placeholder="HomePilot",
363
+ scale=2,
364
+ )
365
+ private_toggle = gr.Checkbox(
366
+ label="Privado",
367
+ value=True,
368
+ scale=1,
369
+ )
370
+ model_choice = gr.Dropdown(
371
+ label="Modelo LLM",
372
+ choices=[
373
+ ("Qwen 2.5 1.5B (rรกpido, ligero)", "qwen2.5:1.5b"),
374
+ ("Qwen 2.5 3B (mejor calidad)", "qwen2.5:3b"),
375
+ ("Llama 3 8B (poderoso, necesita GPU)", "llama3:8b"),
376
+ ("Gemma 2B (equilibrado)", "gemma:2b"),
377
+ ("Phi 3 Mini (Microsoft, compacto)", "phi3:mini"),
378
+ ],
379
+ value="qwen2.5:1.5b",
380
+ )
381
+
382
+ # Persona preview
383
+ gr.Markdown("#### ๐ŸŽญ Personas pre-instaladas")
384
+ gr.HTML("""
385
+ <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 6px; margin: 8px 0;">
386
+ <div class="persona-chip">๐ŸŒ™ Lunalite</div>
387
+ <div class="persona-chip">๐Ÿ˜Ž Chillbro</div>
388
+ <div class="persona-chip">๐Ÿ” Curiosa</div>
389
+ <div class="persona-chip">โšก Hypekid</div>
390
+ <div class="persona-chip">๐Ÿ”‹ Volt Buddy</div>
391
+ <div class="persona-chip">โš”๏ธ Ronin Zero</div>
392
+ <div class="persona-chip">๐Ÿฆ– Rival Kaiju</div>
393
+ <div class="persona-chip">๐Ÿ’พ Glitchbyte</div>
394
+ <div class="persona-chip">๐Ÿ—บ๏ธ Questkid 99</div>
395
+ <div class="persona-chip">๐Ÿง  Sigma Sage</div>
396
+ <div class="persona-chip">๐Ÿƒ Wildcard Loki</div>
397
+ <div class="persona-chip">๐ŸŒณ Oldroot Oracle</div>
398
+ <div class="persona-chip">๐Ÿ”ฎ Morphling X</div>
399
+ <div class="persona-chip">๐ŸŒŒ Nova Void</div>
400
+ </div>
401
+ """)
402
+
403
+ # โ”€โ”€ Step 3: Install โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
404
+ with gr.Group(elem_classes="step-card"):
405
+ gr.Markdown("### ๐Ÿš€ Paso 3 โ€” Instalaciรณn")
406
+ install_btn = gr.Button(
407
+ "Instalar HomePilot โ†’",
408
+ variant="primary",
409
+ size="lg",
410
+ )
411
+ install_log = gr.Markdown("")
412
+
413
+ # โ”€โ”€ Footer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
414
+ gr.HTML("""
415
+ <div style="text-align: center; padding: 2rem 0; color: #64748b; font-size: 0.8rem;">
416
+ <p>HomePilot Installer ยท Tu IA, tu mรกquina, tu privacidad</p>
417
+ <p style="margin-top: 4px;">
418
+ <a href="https://github.com/ruslanmv/HomePilot" style="color: #a78bfa;">GitHub</a> ยท
419
+ <a href="https://huggingface.co/spaces/ruslanmv/HomePilot" style="color: #a78bfa;">Template Space</a> ยท
420
+ <a href="https://huggingface.co/spaces/ruslanmv/Chata" style="color: #a78bfa;">Chata</a>
421
+ </p>
422
+ </div>
423
+ """)
424
+
425
+ # โ”€โ”€ Events โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
426
+
427
+ def on_verify(token):
428
+ status, username, message = validate_token(token)
429
+ return message, username
430
+
431
+ verify_btn.click(
432
+ fn=on_verify,
433
+ inputs=[token_input],
434
+ outputs=[auth_status, username_state],
435
+ )
436
+
437
+ install_btn.click(
438
+ fn=create_user_space,
439
+ inputs=[token_input, username_state, space_name,
440
+ private_toggle, model_choice],
441
+ outputs=[install_log],
442
+ )
443
+
444
+ return app
445
+
446
+
447
+ # โ”€โ”€ Entry point โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
448
+
449
+ if __name__ == "__main__":
450
+ app = build_ui()
451
+ app.launch(
452
+ server_name="0.0.0.0",
453
+ server_port=int(os.environ.get("PORT", "7860")),
454
+ share=False,
455
+ )
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio>=4.0
2
+ requests>=2.31