rahul7star commited on
Commit
f971045
·
verified ·
1 Parent(s): b4391bf

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +483 -0
app.py ADDED
@@ -0,0 +1,483 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import yaml
4
+ import json
5
+ import shutil
6
+ import torch
7
+ from pathlib import Path
8
+ from PIL import Image
9
+ from fastapi import FastAPI
10
+ from fastapi.responses import JSONResponse
11
+ from huggingface_hub import hf_hub_download, whoami
12
+
13
+
14
+ import os
15
+ os.environ["HF_HOME"] = "/tmp/hf_cache"
16
+ os.makedirs("/tmp/hf_cache", exist_ok=True)
17
+
18
+ from fastapi import FastAPI, Query
19
+ from huggingface_hub import list_repo_files, hf_hub_download, upload_file
20
+ import io
21
+ import requests
22
+ from fastapi import BackgroundTasks
23
+ from fastapi import FastAPI, UploadFile, File
24
+ from fastapi.middleware.cors import CORSMiddleware
25
+
26
+
27
+ import os
28
+ import os
29
+ import zipfile
30
+ import tempfile # ✅ Add this!
31
+
32
+
33
+
34
+
35
+ app = FastAPI()
36
+
37
+ # CORS setup to allow requests from your frontend
38
+ app.add_middleware(
39
+ CORSMiddleware,
40
+ allow_origins=["*"], # Replace "*" with your frontend domain in production
41
+ allow_credentials=True,
42
+ allow_methods=["*"],
43
+ allow_headers=["*"],
44
+ )
45
+
46
+ @app.get("/")
47
+ def health_check():
48
+ return {"status": "✅ FastAPI running on Hugging Face Spaces!"}
49
+
50
+ @app.get("/healthz")
51
+ def healthz():
52
+ return {"ok": True}
53
+
54
+ @app.get("/docs", include_in_schema=False)
55
+ def custom_docs():
56
+ return JSONResponse(get_openapi(title="LoRA Autorun API", version="1.0.0", routes=app.routes))
57
+
58
+
59
+
60
+ REPO_ID = "rahul7star/ohamlab"
61
+ FOLDER = "demo"
62
+ BASE_URL = f"https://huggingface.co/{REPO_ID}/resolve/main/"
63
+
64
+ #show all images in a DIR at UI FE
65
+ @app.get("/images")
66
+ def list_images():
67
+ try:
68
+ all_files = list_repo_files(REPO_ID)
69
+
70
+ folder_prefix = FOLDER.rstrip("/") + "/"
71
+
72
+ files_in_folder = [
73
+ f for f in all_files
74
+ if f.startswith(folder_prefix)
75
+ and "/" not in f[len(folder_prefix):] # no subfolder files
76
+ and f.lower().endswith((".png", ".jpg", ".jpeg", ".webp"))
77
+ ]
78
+
79
+ urls = [BASE_URL + f for f in files_in_folder]
80
+
81
+ return {"images": urls}
82
+
83
+ except Exception as e:
84
+ return {"error": str(e)}
85
+
86
+ from datetime import datetime
87
+ import tempfile
88
+ import uuid
89
+
90
+ # upload zip from UI
91
+ @app.post("/upload-zip")
92
+ async def upload_zip(file: UploadFile = File(...)):
93
+ if not file.filename.endswith(".zip"):
94
+ return {"error": "Please upload a .zip file"}
95
+
96
+ # Save the ZIP to /tmp
97
+ temp_zip_path = f"/tmp/{file.filename}"
98
+ with open(temp_zip_path, "wb") as f:
99
+ f.write(await file.read())
100
+
101
+ # Create a unique subfolder name inside 'demo/'
102
+ timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
103
+ unique_id = uuid.uuid4().hex[:6]
104
+ folder_name = f"upload_{timestamp}_{unique_id}"
105
+ hf_folder_prefix = f"demo/{folder_name}"
106
+
107
+ try:
108
+ with tempfile.TemporaryDirectory() as extract_dir:
109
+ # Extract zip
110
+ with zipfile.ZipFile(temp_zip_path, 'r') as zip_ref:
111
+ zip_ref.extractall(extract_dir)
112
+
113
+ uploaded_files = []
114
+
115
+ # Upload all extracted files
116
+ for root_dir, _, files in os.walk(extract_dir):
117
+ for name in files:
118
+ file_path = os.path.join(root_dir, name)
119
+ relative_path = os.path.relpath(file_path, extract_dir)
120
+ repo_path = f"{hf_folder_prefix}/{relative_path}".replace("\\", "/")
121
+
122
+ upload_file(
123
+ path_or_fileobj=file_path,
124
+ path_in_repo=repo_path,
125
+ repo_id="rahul7star/ohamlab",
126
+ repo_type="model",
127
+ commit_message=f"Upload {relative_path} to {folder_name}",
128
+ token=True,
129
+ )
130
+ uploaded_files.append(repo_path)
131
+
132
+ return {
133
+ "message": f"✅ Uploaded {len(uploaded_files)} files",
134
+ "folder": folder_name,
135
+ "files": uploaded_files,
136
+ }
137
+
138
+ except Exception as e:
139
+ return {"error": f"❌ Failed to process zip: {str(e)}"}
140
+
141
+
142
+ # upload a single file from UI
143
+ from typing import List
144
+ from fastapi import UploadFile, File, APIRouter
145
+ import os
146
+ from fastapi import UploadFile, File, APIRouter
147
+ from typing import List
148
+ from datetime import datetime
149
+ import uuid, os
150
+
151
+
152
+ @app.post("/upload")
153
+ async def upload_images(
154
+ background_tasks: BackgroundTasks,
155
+ files: List[UploadFile] = File(...)
156
+ ):
157
+ # Step 1: Generate dynamic folder name
158
+ timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
159
+ unique_id = uuid.uuid4().hex[:6]
160
+ folder_name = f"upload_{timestamp}_{unique_id}"
161
+ hf_folder_prefix = f"demo/{folder_name}"
162
+
163
+ responses = []
164
+
165
+ # Step 2: Save and upload each image
166
+ for file in files:
167
+ filename = file.filename
168
+ contents = await file.read()
169
+ temp_path = f"/tmp/{filename}"
170
+ with open(temp_path, "wb") as f:
171
+ f.write(contents)
172
+
173
+ try:
174
+ upload_file(
175
+ path_or_fileobj=temp_path,
176
+ path_in_repo=f"{hf_folder_prefix}/{filename}",
177
+ repo_id=T_REPO_ID,
178
+ repo_type="model",
179
+ commit_message=f"Upload {filename} to {hf_folder_prefix}",
180
+ token=True,
181
+ )
182
+ responses.append({
183
+ "filename": filename,
184
+ "status": "✅ uploaded",
185
+ "path": f"{hf_folder_prefix}/{filename}"
186
+ })
187
+ except Exception as e:
188
+ responses.append({
189
+ "filename": filename,
190
+ "status": f"❌ failed: {str(e)}"
191
+ })
192
+
193
+ os.remove(temp_path)
194
+
195
+ # Step 3: Add filter job to background
196
+ def run_filter():
197
+ try:
198
+ result = filter_and_rename_images(folder=hf_folder_prefix)
199
+ print(f"🧼 Filter result: {result}")
200
+ except Exception as e:
201
+ print(f"❌ Filter failed: {str(e)}")
202
+
203
+ background_tasks.add_task(run_filter)
204
+
205
+ return {
206
+ "message": f"{len(files)} file(s) uploaded",
207
+ "upload_folder": hf_folder_prefix,
208
+ "results": responses,
209
+ "note": "Filtering started in background"
210
+ }
211
+
212
+
213
+
214
+
215
+
216
+
217
+ #Tranining Data set start fitering data for traninig
218
+
219
+
220
+ T_REPO_ID = "rahul7star/ohamlab"
221
+ DESCRIPTION_TEXT = (
222
+ "Ra3hul is wearing a black jacket over a striped white t-shirt with blue jeans. "
223
+ "He is standing near a lake with his arms spread wide open, with mountains and cloudy skies in the background."
224
+ )
225
+
226
+ def is_image_file(filename: str) -> bool:
227
+ return filename.lower().endswith((".png", ".jpg", ".jpeg", ".webp"))
228
+
229
+ @app.post("/filter-images")
230
+ def filter_and_rename_images(folder: str = Query("demo", description="Folder path in repo to scan")):
231
+ try:
232
+ all_files = list_repo_files(T_REPO_ID)
233
+ folder_prefix = folder.rstrip("/") + "/"
234
+ filter_folder = f"filter-{folder.rstrip('/')}"
235
+ filter_prefix = filter_folder + "/"
236
+
237
+ # Filter images only directly in the folder (no subfolders)
238
+ image_files = [
239
+ f for f in all_files
240
+ if f.startswith(folder_prefix)
241
+ and "/" not in f[len(folder_prefix):] # no deeper path
242
+ and is_image_file(f)
243
+ ]
244
+
245
+ if not image_files:
246
+ return {"error": f"No images found in folder '{folder}'"}
247
+
248
+ uploaded_files = []
249
+
250
+ for idx, orig_path in enumerate(image_files, start=1):
251
+ # Download image content bytes (uses local cache)
252
+ local_path = hf_hub_download(repo_id=T_REPO_ID, filename=orig_path)
253
+ with open(local_path, "rb") as f:
254
+ file_bytes = f.read()
255
+
256
+ # Rename images as image1.jpeg, image2.jpeg, ...
257
+ new_image_name = f"image{idx}.jpeg"
258
+
259
+ # Upload renamed image from memory
260
+ upload_file(
261
+ path_or_fileobj=io.BytesIO(file_bytes),
262
+ path_in_repo=filter_prefix + new_image_name,
263
+ repo_id=T_REPO_ID,
264
+ repo_type="model",
265
+ commit_message=f"Upload renamed image {new_image_name} to {filter_folder}",
266
+ token=True,
267
+ )
268
+ uploaded_files.append(filter_prefix + new_image_name)
269
+
270
+ # Create and upload text file for each image
271
+ txt_filename = f"image{idx}.txt"
272
+ upload_file(
273
+ path_or_fileobj=io.BytesIO(DESCRIPTION_TEXT.encode("utf-8")),
274
+ path_in_repo=filter_prefix + txt_filename,
275
+ repo_id=T_REPO_ID,
276
+ repo_type="model",
277
+ commit_message=f"Upload text file {txt_filename} to {filter_folder}",
278
+ token=True,
279
+ )
280
+ uploaded_files.append(filter_prefix + txt_filename)
281
+
282
+ return {
283
+ "message": f"Processed and uploaded {len(image_files)} images and text files.",
284
+ "files": uploaded_files,
285
+ }
286
+
287
+ except Exception as e:
288
+ return {"error": str(e)}
289
+
290
+
291
+
292
+ # ========== CONFIGURATION ==========
293
+ REPO_ID = "rahul7star/ohamlab"
294
+ FOLDER_IN_REPO = "filter-demo/upload_20250708_041329_9c5c81"
295
+ CONCEPT_SENTENCE = "ohamlab style"
296
+ LORA_NAME = "ohami_filter_autorun"
297
+
298
+ # ========== FASTAPI APP ==========
299
+ app = FastAPI()
300
+
301
+ # ========== HELPERS ==========
302
+ def create_dataset(images, *captions):
303
+ destination_folder = f"datasets_{uuid.uuid4()}"
304
+ os.makedirs(destination_folder, exist_ok=True)
305
+
306
+ jsonl_file_path = os.path.join(destination_folder, "metadata.jsonl")
307
+ with open(jsonl_file_path, "a") as jsonl_file:
308
+ for index, image in enumerate(images):
309
+ new_image_path = shutil.copy(str(image), destination_folder)
310
+ caption = captions[index]
311
+ file_name = os.path.basename(new_image_path)
312
+ data = {"file_name": file_name, "prompt": caption}
313
+ jsonl_file.write(json.dumps(data) + "\n")
314
+
315
+ return destination_folder
316
+
317
+ def recursive_update(d, u):
318
+ for k, v in u.items():
319
+ if isinstance(v, dict) and v:
320
+ d[k] = recursive_update(d.get(k, {}), v)
321
+ else:
322
+ d[k] = v
323
+ return d
324
+
325
+ def start_training(
326
+ lora_name,
327
+ concept_sentence,
328
+ steps,
329
+ lr,
330
+ rank,
331
+ model_to_train,
332
+ low_vram,
333
+ dataset_folder,
334
+ sample_1,
335
+ sample_2,
336
+ sample_3,
337
+ use_more_advanced_options,
338
+ more_advanced_options,
339
+ ):
340
+ try:
341
+ user = whoami()
342
+ username = user.get("name", "anonymous")
343
+ push_to_hub = True
344
+ except:
345
+ username = "anonymous"
346
+ push_to_hub = False
347
+
348
+ slugged_lora_name = lora_name.replace(" ", "_").lower()
349
+
350
+ # Load base config
351
+ config = {
352
+ "config": {
353
+ "name": slugged_lora_name,
354
+ "process": [
355
+ {
356
+ "model": {
357
+ "low_vram": low_vram,
358
+ "is_flux": True,
359
+ "quantize": True,
360
+ "name_or_path": "black-forest-labs/FLUX.1-dev"
361
+ },
362
+ "network": {
363
+ "linear": rank,
364
+ "linear_alpha": rank,
365
+ "type": "lora"
366
+ },
367
+ "train": {
368
+ "steps": steps,
369
+ "lr": lr,
370
+ "skip_first_sample": True,
371
+ "batch_size": 1,
372
+ "dtype": "bf16",
373
+ "gradient_accumulation_steps": 1,
374
+ "gradient_checkpointing": True,
375
+ "noise_scheduler": "flowmatch",
376
+ "optimizer": "adamw8bit",
377
+ "ema_config": {
378
+ "use_ema": True,
379
+ "ema_decay": 0.99
380
+ }
381
+ },
382
+ "datasets": [
383
+ {"folder_path": dataset_folder}
384
+ ],
385
+ "save": {
386
+ "dtype": "float16",
387
+ "save_every": 10000,
388
+ "push_to_hub": push_to_hub,
389
+ "hf_repo_id": f"{username}/{slugged_lora_name}",
390
+ "hf_private": True,
391
+ "max_step_saves_to_keep": 4
392
+ },
393
+ "sample": {
394
+ "guidance_scale": 3.5,
395
+ "sample_every": steps,
396
+ "sample_steps": 28,
397
+ "width": 1024,
398
+ "height": 1024,
399
+ "walk_seed": True,
400
+ "seed": 42,
401
+ "sampler": "flowmatch",
402
+ "prompts": [p for p in [sample_1, sample_2, sample_3] if p]
403
+ },
404
+ "trigger_word": concept_sentence
405
+ }
406
+ ]
407
+ }
408
+ }
409
+
410
+ # Apply advanced YAML overrides if any
411
+ if use_more_advanced_options and more_advanced_options:
412
+ advanced_config = yaml.safe_load(more_advanced_options)
413
+ config["config"]["process"][0] = recursive_update(config["config"]["process"][0], advanced_config)
414
+
415
+ # Save YAML config
416
+ os.makedirs("tmp_configs", exist_ok=True)
417
+ config_path = f"tmp_configs/{uuid.uuid4()}_{slugged_lora_name}.yaml"
418
+ with open(config_path, "w") as f:
419
+ yaml.dump(config, f)
420
+
421
+ # Simulate training
422
+ print(f"[INFO] Starting training with config: {config_path}")
423
+ print(json.dumps(config, indent=2))
424
+ return f"Training started successfully with config: {config_path}"
425
+
426
+ # ========== MAIN ENDPOINT ==========
427
+ @app.post("/train-from-hf")
428
+ def auto_run_lora_from_repo():
429
+ try:
430
+ local_dir = Path(f"/tmp/{LORA_NAME}-{uuid.uuid4()}")
431
+ os.makedirs(local_dir, exist_ok=True)
432
+
433
+ hf_hub_download(
434
+ repo_id=REPO_ID,
435
+ repo_type="dataset",
436
+ subfolder=FOLDER_IN_REPO,
437
+ local_dir=local_dir,
438
+ local_dir_use_symlinks=False,
439
+ force_download=False,
440
+ etag_timeout=10,
441
+ allow_patterns=["*.jpg", "*.png", "*.jpeg"],
442
+ )
443
+
444
+ image_dir = local_dir / FOLDER_IN_REPO
445
+ image_paths = list(image_dir.rglob("*.jpg")) + list(image_dir.rglob("*.jpeg")) + list(image_dir.rglob("*.png"))
446
+
447
+ if not image_paths:
448
+ return JSONResponse(status_code=400, content={"error": "No images found in the HF repo folder."})
449
+
450
+ captions = [
451
+ f"Autogenerated caption for {img.stem} in the {CONCEPT_SENTENCE} [trigger]" for img in image_paths
452
+ ]
453
+
454
+ dataset_path = create_dataset(image_paths, *captions)
455
+
456
+ result = start_training(
457
+ lora_name=LORA_NAME,
458
+ concept_sentence=CONCEPT_SENTENCE,
459
+ steps=1000,
460
+ lr=4e-4,
461
+ rank=16,
462
+ model_to_train="dev",
463
+ low_vram=True,
464
+ dataset_folder=dataset_path,
465
+ sample_1=f"A stylized portrait using {CONCEPT_SENTENCE}",
466
+ sample_2=f"A cat in the {CONCEPT_SENTENCE}",
467
+ sample_3=f"A selfie processed in {CONCEPT_SENTENCE}",
468
+ use_more_advanced_options=True,
469
+ more_advanced_options="""
470
+ training:
471
+ seed: 42
472
+ precision: bf16
473
+ batch_size: 2
474
+ augmentation:
475
+ flip: true
476
+ color_jitter: true
477
+ """
478
+ )
479
+
480
+ return {"message": result}
481
+
482
+ except Exception as e:
483
+ return JSONResponse(status_code=500, content={"error": str(e)})