awacke1 commited on
Commit
26f4cb2
·
verified ·
1 Parent(s): fe1d350

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +383 -524
app.py CHANGED
@@ -5,20 +5,21 @@ import base64
5
  import streamlit as st
6
  import pandas as pd
7
  import torch
 
8
  from torch.utils.data import Dataset, DataLoader
9
  import csv
10
  import time
11
  from dataclasses import dataclass
12
- from typing import Optional
13
  import zipfile
14
  import math
15
  from PIL import Image
16
  import random
17
  import logging
18
  import numpy as np
19
- import cv2
20
- from diffusers import DiffusionPipeline
21
 
 
22
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
23
  logger = logging.getLogger(__name__)
24
  log_records = []
@@ -29,6 +30,7 @@ class LogCaptureHandler(logging.Handler):
29
 
30
  logger.addHandler(LogCaptureHandler())
31
 
 
32
  st.set_page_config(
33
  page_title="SFT Tiny Titans 🚀",
34
  page_icon="🤖",
@@ -37,22 +39,32 @@ st.set_page_config(
37
  menu_items={
38
  'Get Help': 'https://huggingface.co/awacke1',
39
  'Report a Bug': 'https://huggingface.co/spaces/awacke1',
40
- 'About': "Tiny Titans: Small diffusion models, big CV dreams! 🌌"
41
  }
42
  )
43
 
 
44
  if 'captured_images' not in st.session_state:
45
  st.session_state['captured_images'] = []
46
- if 'cv_builder' not in st.session_state:
47
- st.session_state['cv_builder'] = None
48
- if 'cv_loaded' not in st.session_state:
49
- st.session_state['cv_loaded'] = False
50
- if 'active_tab' not in st.session_state:
51
- st.session_state['active_tab'] = "Build Titan 🌱"
 
 
 
 
 
 
 
 
 
 
52
 
53
  @dataclass
54
  class DiffusionConfig:
55
- """Config for our diffusion heroes 🦸‍♂️ - Keeps the blueprint snappy!"""
56
  name: str
57
  base_model: str
58
  size: str
@@ -60,8 +72,29 @@ class DiffusionConfig:
60
  def model_path(self):
61
  return f"diffusion_models/{self.name}"
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  class DiffusionDataset(Dataset):
64
- """Pixel party platter 🍕 - Images and text for diffusion delight!"""
65
  def __init__(self, images, texts):
66
  self.images = images
67
  self.texts = texts
@@ -70,591 +103,417 @@ class DiffusionDataset(Dataset):
70
  def __getitem__(self, idx):
71
  return {"image": self.images[idx], "text": self.texts[idx]}
72
 
73
- class MicroDiffusionBuilder:
74
- """Tiny titan of diffusion 🐣 - Small but mighty for quick demos!"""
75
  def __init__(self):
76
  self.config = None
77
- self.pipeline = None
78
- self.jokes = ["Micro but mighty! 💪", "Small pixels, big dreams! 🌟"]
79
- def load_model(self, model_path: str, config: Optional[DiffusionConfig] = None):
80
- try:
81
- with st.spinner(f"Loading {model_path}... (Tiny titan powering up!)"):
82
- self.pipeline = DiffusionPipeline.from_pretrained(model_path, low_cpu_mem_usage=True)
83
- self.pipeline.to("cuda" if torch.cuda.is_available() else "cpu")
84
- if config:
85
- self.config = config
86
- st.success(f"Model loaded! 🎉 {random.choice(self.jokes)}")
87
- logger.info(f"Loaded Micro Diffusion: {model_path}")
88
- except Exception as e:
89
- st.error(f"Failed to load {model_path}: {str(e)} 💥 (Tiny titan tripped!)")
90
- logger.error(f"Failed to load {model_path}: {str(e)}")
91
- raise
92
  return self
93
- def fine_tune_sft(self, images, texts, epochs=3):
94
- try:
95
- dataset = DiffusionDataset(images, texts)
96
- dataloader = DataLoader(dataset, batch_size=1, shuffle=True)
97
- optimizer = torch.optim.AdamW(self.pipeline.unet.parameters(), lr=1e-5)
98
- self.pipeline.unet.train()
99
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
100
- for epoch in range(epochs):
101
- with st.spinner(f"Epoch {epoch + 1}/{epochs}... ⚙️ (Micro titan flexing!)"):
102
- total_loss = 0
103
- for batch in dataloader:
104
- optimizer.zero_grad()
105
- image = batch["image"][0].to(device)
106
- text = batch["text"][0]
107
- latents = self.pipeline.vae.encode(torch.tensor(np.array(image)).permute(2, 0, 1).unsqueeze(0).float().to(device)).latent_dist.sample()
108
- noise = torch.randn_like(latents)
109
- timesteps = torch.randint(0, self.pipeline.scheduler.num_train_timesteps, (latents.shape[0],), device=latents.device)
110
- noisy_latents = self.pipeline.scheduler.add_noise(latents, noise, timesteps)
111
- text_embeddings = self.pipeline.text_encoder(self.pipeline.tokenizer(text, return_tensors="pt").input_ids.to(device))[0]
112
- pred_noise = self.pipeline.unet(noisy_latents, timesteps, encoder_hidden_states=text_embeddings).sample
113
- loss = torch.nn.functional.mse_loss(pred_noise, noise)
114
- loss.backward()
115
- optimizer.step()
116
- total_loss += loss.item()
117
- st.write(f"Epoch {epoch + 1} done! Loss: {total_loss / len(dataloader):.4f}")
118
- st.success(f"Micro Diffusion tuned! 🎉 {random.choice(self.jokes)}")
119
- logger.info(f"Fine-tuned Micro Diffusion: {self.config.name}")
120
- except Exception as e:
121
- st.error(f"Tuning failed: {str(e)} 💥 (Micro snag!)")
122
- logger.error(f"Tuning failed: {str(e)}")
123
- raise
124
  return self
125
  def save_model(self, path: str):
 
 
 
 
 
 
 
 
 
126
  try:
127
- with st.spinner("Saving model... 💾 (Packing tiny pixels!)"):
128
- os.makedirs(os.path.dirname(path), exist_ok=True)
129
- self.pipeline.save_pretrained(path)
130
- st.success(f"Saved at {path}! ✅ Tiny titan secured!")
131
- logger.info(f"Saved at {path}")
132
- except Exception as e:
133
- st.error(f"Save failed: {str(e)} 💥 (Packing mishap!)")
134
- logger.error(f"Save failed: {str(e)}")
135
- raise
136
- def generate(self, prompt: str):
137
- try:
138
- return self.pipeline(prompt, num_inference_steps=20).images[0]
139
  except Exception as e:
140
- st.error(f"Generation failed: {str(e)} 💥 (Pixel oopsie!)")
141
- logger.error(f"Generation failed: {str(e)}")
142
- raise
143
 
144
- class LatentDiffusionBuilder:
145
- """Scaled-down dreamer 🌙 - Latent magic for efficient artistry!"""
146
  def __init__(self):
147
  self.config = None
148
  self.pipeline = None
149
- self.jokes = ["Latent vibes only! 🌀", "Small scale, big style! 🎨"]
150
- def load_model(self, model_path: str, config: Optional[DiffusionConfig] = None):
151
- try:
152
- with st.spinner(f"Loading {model_path}... ⏳ (Latent titan rising!)"):
153
- self.pipeline = DiffusionPipeline.from_pretrained(model_path, low_cpu_mem_usage=True)
154
- self.pipeline.unet = torch.nn.Sequential(*list(self.pipeline.unet.children())[:2]) # Scale down U-Net
155
- self.pipeline.to("cuda" if torch.cuda.is_available() else "cpu")
156
- if config:
157
- self.config = config
158
- st.success(f"Model loaded! 🎉 {random.choice(self.jokes)}")
159
- logger.info(f"Loaded Latent Diffusion: {model_path}")
160
- except Exception as e:
161
- st.error(f"Failed to load {model_path}: {str(e)} 💥 (Latent hiccup!)")
162
- logger.error(f"Failed to load {model_path}: {str(e)}")
163
- raise
164
  return self
165
  def fine_tune_sft(self, images, texts, epochs=3):
166
- try:
167
- dataset = DiffusionDataset(images, texts)
168
- dataloader = DataLoader(dataset, batch_size=1, shuffle=True)
169
- optimizer = torch.optim.AdamW(self.pipeline.unet.parameters(), lr=1e-5)
170
- self.pipeline.unet.train()
171
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
172
- for epoch in range(epochs):
173
- with st.spinner(f"Epoch {epoch + 1}/{epochs}... ⚙️ (Latent titan shaping up!)"):
174
- total_loss = 0
175
- for batch in dataloader:
176
- optimizer.zero_grad()
177
- image = batch["image"][0].to(device)
178
- text = batch["text"][0]
179
- latents = self.pipeline.vae.encode(torch.tensor(np.array(image)).permute(2, 0, 1).unsqueeze(0).float().to(device)).latent_dist.sample()
180
- noise = torch.randn_like(latents)
181
- timesteps = torch.randint(0, self.pipeline.scheduler.num_train_timesteps, (latents.shape[0],), device=latents.device)
182
- noisy_latents = self.pipeline.scheduler.add_noise(latents, noise, timesteps)
183
- text_embeddings = self.pipeline.text_encoder(self.pipeline.tokenizer(text, return_tensors="pt").input_ids.to(device))[0]
184
- pred_noise = self.pipeline.unet(noisy_latents, timesteps, encoder_hidden_states=text_embeddings).sample
185
- loss = torch.nn.functional.mse_loss(pred_noise, noise)
186
- loss.backward()
187
- optimizer.step()
188
- total_loss += loss.item()
189
- st.write(f"Epoch {epoch + 1} done! Loss: {total_loss / len(dataloader):.4f}")
190
- st.success(f"Latent Diffusion tuned! 🎉 {random.choice(self.jokes)}")
191
- logger.info(f"Fine-tuned Latent Diffusion: {self.config.name}")
192
- except Exception as e:
193
- st.error(f"Tuning failed: {str(e)} 💥 (Latent snag!)")
194
- logger.error(f"Tuning failed: {str(e)}")
195
- raise
196
  return self
197
  def save_model(self, path: str):
198
- try:
199
- with st.spinner("Saving model... 💾 (Packing latent dreams!)"):
200
- os.makedirs(os.path.dirname(path), exist_ok=True)
201
- self.pipeline.save_pretrained(path)
202
- st.success(f"Saved at {path}! ✅ Latent titan stashed!")
203
- logger.info(f"Saved at {path}")
204
- except Exception as e:
205
- st.error(f"Save failed: {str(e)} 💥 (Dreamy mishap!)")
206
- logger.error(f"Save failed: {str(e)}")
207
- raise
208
- def generate(self, prompt: str):
209
- try:
210
- return self.pipeline(prompt, num_inference_steps=30).images[0]
211
- except Exception as e:
212
- st.error(f"Generation failed: {str(e)} 💥 (Latent oopsie!)")
213
- logger.error(f"Generation failed: {str(e)}")
214
- raise
215
-
216
- class FluxDiffusionBuilder:
217
- """Distilled dynamo ⚡ - High-quality pixels in a small package!"""
218
- def __init__(self):
219
- self.config = None
220
- self.pipeline = None
221
- self.jokes = ["Flux-tastic! ✨", "Small size, big wow! 🎇"]
222
- def load_model(self, model_path: str, config: Optional[DiffusionConfig] = None):
223
- try:
224
- with st.spinner(f"Loading {model_path}... ⏳ (Flux titan charging!)"):
225
- self.pipeline = DiffusionPipeline.from_pretrained(model_path, low_cpu_mem_usage=True)
226
- self.pipeline.to("cuda" if torch.cuda.is_available() else "cpu")
227
- if config:
228
- self.config = config
229
- st.success(f"Model loaded! 🎉 {random.choice(self.jokes)}")
230
- logger.info(f"Loaded FLUX.1 Distilled: {model_path}")
231
- except Exception as e:
232
- st.error(f"Failed to load {model_path}: {str(e)} 💥 (Flux fizzle!)")
233
- logger.error(f"Failed to load {model_path}: {str(e)}")
234
- raise
235
- return self
236
- def fine_tune_sft(self, images, texts, epochs=3):
237
- try:
238
- dataset = DiffusionDataset(images, texts)
239
- dataloader = DataLoader(dataset, batch_size=1, shuffle=True)
240
- optimizer = torch.optim.AdamW(self.pipeline.unet.parameters(), lr=1e-5)
241
- self.pipeline.unet.train()
242
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
243
- for epoch in range(epochs):
244
- with st.spinner(f"Epoch {epoch + 1}/{epochs}... ⚙️ (Flux titan powering up!)"):
245
- total_loss = 0
246
- for batch in dataloader:
247
- optimizer.zero_grad()
248
- image = batch["image"][0].to(device)
249
- text = batch["text"][0]
250
- latents = self.pipeline.vae.encode(torch.tensor(np.array(image)).permute(2, 0, 1).unsqueeze(0).float().to(device)).latent_dist.sample()
251
- noise = torch.randn_like(latents)
252
- timesteps = torch.randint(0, self.pipeline.scheduler.num_train_timesteps, (latents.shape[0],), device=latents.device)
253
- noisy_latents = self.pipeline.scheduler.add_noise(latents, noise, timesteps)
254
- text_embeddings = self.pipeline.text_encoder(self.pipeline.tokenizer(text, return_tensors="pt").input_ids.to(device))[0]
255
- pred_noise = self.pipeline.unet(noisy_latents, timesteps, encoder_hidden_states=text_embeddings).sample
256
- loss = torch.nn.functional.mse_loss(pred_noise, noise)
257
- loss.backward()
258
- optimizer.step()
259
- total_loss += loss.item()
260
- st.write(f"Epoch {epoch + 1} done! Loss: {total_loss / len(dataloader):.4f}")
261
- st.success(f"FLUX Diffusion tuned! 🎉 {random.choice(self.jokes)}")
262
- logger.info(f"Fine-tuned FLUX.1 Distilled: {self.config.name}")
263
- except Exception as e:
264
- st.error(f"Tuning failed: {str(e)} 💥 (Flux snag!)")
265
- logger.error(f"Tuning failed: {str(e)}")
266
- raise
267
- return self
268
- def save_model(self, path: str):
269
- try:
270
- with st.spinner("Saving model... 💾 (Packing flux magic!)"):
271
- os.makedirs(os.path.dirname(path), exist_ok=True)
272
- self.pipeline.save_pretrained(path)
273
- st.success(f"Saved at {path}! ✅ Flux titan secured!")
274
- logger.info(f"Saved at {path}")
275
- except Exception as e:
276
- st.error(f"Save failed: {str(e)} 💥 (Fluxy mishap!)")
277
- logger.error(f"Save failed: {str(e)}")
278
- raise
279
- def generate(self, prompt: str):
280
- try:
281
  return self.pipeline(prompt, num_inference_steps=50).images[0]
282
- except Exception as e:
283
- st.error(f"Generation failed: {str(e)} 💥 (Flux oopsie!)")
284
- logger.error(f"Generation failed: {str(e)}")
285
- raise
286
 
 
287
  def generate_filename(sequence, ext="png"):
288
- """Time-stamped snapshots ⏰ - Keeps our pics organized with cam flair!"""
289
  from datetime import datetime
290
  import pytz
291
  central = pytz.timezone('US/Central')
292
- dt = datetime.now(central)
293
- return f"{dt.strftime('%m-%d-%Y-%I-%M-%S-%p')}-{sequence}.{ext}"
294
 
295
  def get_download_link(file_path, mime_type="text/plain", label="Download"):
296
- """Magic link maker 🔗 - Snag your files with a click!"""
297
- try:
298
- with open(file_path, 'rb') as f:
299
- data = f.read()
300
- b64 = base64.b64encode(data).decode()
301
- return f'<a href="data:{mime_type};base64,{b64}" download="{os.path.basename(file_path)}">{label} 📥</a>'
302
- except Exception as e:
303
- logger.error(f"Failed to generate link for {file_path}: {str(e)}")
304
- return f"Error: Could not generate link for {file_path}"
305
-
306
- def zip_files(files, zip_path):
307
- """Zip zap zoo 🎒 - Bundle up your goodies!"""
308
- try:
309
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
310
  for file in files:
311
- zipf.write(file, os.path.basename(file))
312
- logger.info(f"Created ZIP file: {zip_path}")
313
- except Exception as e:
314
- logger.error(f"Failed to create ZIP {zip_path}: {str(e)}")
315
- raise
316
-
317
- def delete_files(files):
318
- """Trash titan 🗑️ - Clear the stage for new stars!"""
319
- try:
320
- for file in files:
321
- os.remove(file)
322
- logger.info(f"Deleted file: {file}")
323
- st.session_state['captured_images'] = [f for f in st.session_state['captured_images'] if f not in files]
324
- except Exception as e:
325
- logger.error(f"Failed to delete files: {str(e)}")
326
- raise
327
-
328
- def get_model_files():
329
- """Model treasure hunt 🗺️ - Find our diffusion gems!"""
330
- return [d for d in glob.glob("diffusion_models/*") if os.path.isdir(d)]
331
 
332
  def get_gallery_files(file_types):
333
- """Gallery curator 🖼️ - Showcase our pixel masterpieces!"""
334
- return sorted(list(set(f for ext in file_types for f in glob.glob(f"*.{ext}"))))
335
 
336
  def update_gallery():
337
- """Gallery refresh 🌟 - Keep the art flowing!"""
338
  media_files = get_gallery_files(["png"])
339
  if media_files:
340
  cols = st.sidebar.columns(2)
341
  for idx, file in enumerate(media_files[:gallery_size * 2]):
342
  with cols[idx % 2]:
343
  st.image(Image.open(file), caption=file, use_container_width=True)
344
- st.markdown(get_download_link(file, "image/png", "Download Snap 📸"), unsafe_allow_html=True)
345
-
346
- def get_available_video_devices():
347
- """Camera roll call 🎥 - Who’s ready to shine? Fallback if OpenCV flops!"""
348
- video_devices = [f"Camera {i} 🎥" for i in range(6)] # Default to 6 cams
349
- try:
350
- detected = []
351
- for i in range(6): # Limit to 6 as per your setup
352
- cap = cv2.VideoCapture(i, cv2.CAP_V4L2)
353
- if not cap.isOpened():
354
- cap = cv2.VideoCapture(i)
355
- if cap.isOpened():
356
- detected.append(f"Camera {i} 🎥")
357
- logger.info(f"Detected camera at index {i}")
358
- cap.release()
359
- if detected:
360
- video_devices = detected
361
- else:
362
- logger.warning("No cameras detected by OpenCV; using defaults")
363
- except Exception as e:
364
- logger.error(f"Error detecting cameras: {str(e)} - Falling back to defaults")
365
- return video_devices
366
-
367
- st.title("SFT Tiny Titans 🚀 (Small Diffusion Delight!)")
368
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  st.sidebar.header("Media Gallery 🎨")
370
- gallery_size = st.sidebar.slider("Gallery Size 📸", 1, 10, 4, help="How many snaps to flaunt? 🌟")
371
  update_gallery()
372
 
373
- col1, col2 = st.sidebar.columns(2)
374
- with col1:
375
- if st.button("Download All 📦"):
376
- media_files = get_gallery_files(["png"])
377
- if media_files:
378
- zip_path = f"snapshot_collection_{int(time.time())}.zip"
379
- zip_files(media_files, zip_path)
380
- st.sidebar.markdown(get_download_link(zip_path, "application/zip", "Download All Snaps 📦"), unsafe_allow_html=True)
381
- st.sidebar.success("Snaps zipped! 🎉 Grab your loot!")
382
- else:
383
- st.sidebar.warning("No snaps to zip! 📸 Snap some first!")
384
- with col2:
385
- if st.button("Delete All 🗑️"):
386
- media_files = get_gallery_files(["png"])
387
- if media_files:
388
- delete_files(media_files)
389
- st.sidebar.success("Snaps vanquished! 🧹 Gallery cleared!")
390
- update_gallery()
391
- else:
392
- st.sidebar.warning("Nothing to delete! 📸 Snap some pics!")
393
-
394
- uploaded_files = st.sidebar.file_uploader("Upload Goodies 🎵🎥🖼️📝📜", type=["mp3", "mp4", "png", "jpeg", "md", "pdf", "docx"], accept_multiple_files=True)
395
- if uploaded_files:
396
- for uploaded_file in uploaded_files:
397
- filename = uploaded_file.name
398
- with open(filename, "wb") as f:
399
- f.write(uploaded_file.getvalue())
400
- logger.info(f"Uploaded file: {filename}")
401
-
402
- st.sidebar.subheader("Image Gallery 🖼️")
403
- image_files = get_gallery_files(["png", "jpeg"])
404
- if image_files:
405
- cols = st.sidebar.columns(2)
406
- for idx, file in enumerate(image_files[:gallery_size * 2]):
407
- with cols[idx % 2]:
408
- st.image(Image.open(file), caption=file, use_container_width=True)
409
- st.markdown(get_download_link(file, "image/png" if file.endswith(".png") else "image/jpeg", f"Save Pic 🖼️"), unsafe_allow_html=True)
410
-
411
  st.sidebar.subheader("Model Management 🗂️")
412
- model_dirs = get_model_files()
 
413
  selected_model = st.sidebar.selectbox("Select Saved Model", ["None"] + model_dirs)
414
- model_type = st.sidebar.selectbox("Diffusion Type", ["Micro Diffusion", "Latent Diffusion", "FLUX.1 Distilled"])
415
  if selected_model != "None" and st.sidebar.button("Load Model 📂"):
416
- builder = {
417
- "Micro Diffusion": MicroDiffusionBuilder,
418
- "Latent Diffusion": LatentDiffusionBuilder,
419
- "FLUX.1 Distilled": FluxDiffusionBuilder
420
- }[model_type]()
421
- config = DiffusionConfig(name=os.path.basename(selected_model), base_model="unknown", size="small")
422
- try:
423
- builder.load_model(selected_model, config)
424
- st.session_state['cv_builder'] = builder
425
- st.session_state['cv_loaded'] = True
426
- st.rerun()
427
- except Exception as e:
428
- st.error(f"Model load failed: {str(e)} 💥 (Check logs for details!)")
429
-
430
- st.sidebar.subheader("Model Status 🚦")
431
- st.sidebar.write(f"**CV Model**: {'Loaded' if st.session_state['cv_loaded'] else 'Not Loaded'} {'(Active)' if st.session_state['cv_loaded'] and isinstance(st.session_state.get('cv_builder'), (MicroDiffusionBuilder, LatentDiffusionBuilder, FluxDiffusionBuilder)) else ''}")
432
-
433
- tabs = ["Build Titan 🌱", "Camera Snap 📷", "Fine-Tune Titan (CV) 🔧", "Test Titan (CV) 🧪", "Agentic RAG Party (CV) 🌐"]
434
- tab1, tab2, tab3, tab4, tab5 = st.tabs(tabs)
435
 
436
- for i, tab in enumerate(tabs):
437
- if st.session_state['active_tab'] != tab and st.session_state.get(f'tab{i}_active', False):
438
- logger.info(f"Switched to tab: {tab}")
439
- st.session_state['active_tab'] = tab
440
- st.session_state[f'tab{i}_active'] = (st.session_state['active_tab'] == tab)
441
 
442
  with tab1:
443
- st.header("Build Titan 🌱")
444
- model_type = st.selectbox("Diffusion Type", ["Micro Diffusion", "Latent Diffusion", "FLUX.1 Distilled"], key="build_type")
445
- base_model = st.selectbox("Select Tiny Model",
446
- ["CompVis/ldm-text2im-large-256" if model_type == "Micro Diffusion" else "runwayml/stable-diffusion-v1-5" if model_type == "Latent Diffusion" else "black-forest-labs/flux.1-distilled"])
447
- model_name = st.text_input("Model Name", f"tiny-titan-{int(time.time())}")
448
- if st.button("Download Model ⬇️"):
449
- config = DiffusionConfig(name=model_name, base_model=base_model, size="small")
450
- builder = {
451
- "Micro Diffusion": MicroDiffusionBuilder,
452
- "Latent Diffusion": LatentDiffusionBuilder,
453
- "FLUX.1 Distilled": FluxDiffusionBuilder
454
- }[model_type]()
455
- try:
456
- builder.load_model(base_model, config)
457
- builder.save_model(config.model_path)
458
- st.session_state['cv_builder'] = builder
459
- st.session_state['cv_loaded'] = True
460
- st.rerun()
461
- except Exception as e:
462
- st.error(f"Model build failed: {str(e)} 💥 (Check logs for details!)")
463
-
464
- with tab2:
465
- st.header("Camera Snap 📷 (Dual Capture Fiesta!)")
466
- video_devices = get_available_video_devices()
467
- st.write(f"🎉 Detected Cameras: {', '.join(video_devices)}")
468
- st.info("Switch cams in your browser settings (e.g., Chrome > Privacy > Camera) since I’m a browser star! 🌟")
469
-
470
- st.subheader("Camera 0 🎬 - Lights, Camera, Action!")
471
- cam0_cols = st.columns(4)
472
- with cam0_cols[0]:
473
- cam0_device = st.selectbox("Cam 📷", video_devices, index=0, key="cam0_device", help="Pick your star cam! 🌟")
474
- with cam0_cols[1]:
475
- cam0_label = st.text_input("Tag 🏷️", "Cam 0 Snap", key="cam0_label", help="Name your masterpiece! 🎨")
476
- with cam0_cols[2]:
477
- cam0_help = st.text_input("Hint 💡", "Snap a heroic moment! 🦸‍♂️", key="cam0_help", help="Give a fun tip!")
478
- with cam0_cols[3]:
479
- cam0_vis = st.selectbox("Show 🖼️", ["visible", "hidden", "collapsed"], index=0, key="cam0_vis", help="Label vibes: Visible, Sneaky, or Gone!")
480
-
481
- st.subheader("Camera 1 🎥 - Roll the Film!")
482
- cam1_cols = st.columns(4)
483
- with cam1_cols[0]:
484
- cam1_device = st.selectbox("Cam 📷", video_devices, index=1 if len(video_devices) > 1 else 0, key="cam1_device", help="Choose your blockbuster cam! 🎬")
485
- with cam1_cols[1]:
486
- cam1_label = st.text_input("Tag 🏷️", "Cam 1 Snap", key="cam1_label", help="Title your epic shot! 🌠")
487
- with cam1_cols[2]:
488
- cam1_help = st.text_input("Hint 💡", "Grab an epic frame! 🌟", key="cam1_help", help="Drop a cheeky hint!")
489
- with cam1_cols[3]:
490
- cam1_vis = st.selectbox("Show 🖼️", ["visible", "hidden", "collapsed"], index=0, key="cam1_vis", help="Label style: Show it, Hide it, Poof!")
491
-
492
  cols = st.columns(2)
493
  with cols[0]:
494
- st.subheader(f"Camera 0 ({cam0_device}) 🎬")
495
- cam0_img = st.camera_input(
496
- label=cam0_label,
497
- key="cam0",
498
- help=cam0_help,
499
- disabled=False,
500
- label_visibility=cam0_vis
501
- )
502
  if cam0_img:
503
- filename = generate_filename("cam0")
504
  with open(filename, "wb") as f:
505
  f.write(cam0_img.getvalue())
506
  st.image(Image.open(filename), caption=filename, use_container_width=True)
507
  logger.info(f"Saved snapshot from Camera 0: {filename}")
508
  st.session_state['captured_images'].append(filename)
509
  update_gallery()
510
- st.info("🚨 One snap at a time—your Titan’s too cool for bursts! 😎")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
511
  with cols[1]:
512
- st.subheader(f"Camera 1 ({cam1_device}) 🎥")
513
- cam1_img = st.camera_input(
514
- label=cam1_label,
515
- key="cam1",
516
- help=cam1_help,
517
- disabled=False,
518
- label_visibility=cam1_vis
519
- )
520
  if cam1_img:
521
- filename = generate_filename("cam1")
522
  with open(filename, "wb") as f:
523
  f.write(cam1_img.getvalue())
524
  st.image(Image.open(filename), caption=filename, use_container_width=True)
525
  logger.info(f"Saved snapshot from Camera 1: {filename}")
526
  st.session_state['captured_images'].append(filename)
527
  update_gallery()
528
- st.info("🚨 Single shots only—craft your masterpiece! 🎨")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
529
 
530
- with tab3:
531
- st.header("Fine-Tune Titan (CV) 🔧 (Sculpt Your Pixel Prodigy!)")
532
- if not st.session_state['cv_loaded'] or not isinstance(st.session_state['cv_builder'], (MicroDiffusionBuilder, LatentDiffusionBuilder, FluxDiffusionBuilder)):
533
- st.warning("Please build or load a CV Titan first! ⚠️ (No artist, no canvas!)")
534
  else:
535
- captured_images = get_gallery_files(["png"])
536
- if len(captured_images) >= 2:
537
- st.subheader("Use Case 1: Denoise Snapshots 🌟")
538
- denoising_data = [{"image": img, "text": f"Denoised {os.path.basename(img).split('-')[4]} snap"} for img in captured_images[:min(len(captured_images), 10)]]
539
- denoising_edited = st.data_editor(pd.DataFrame(denoising_data), num_rows="dynamic", help="Craft denoising pairs! 🌟")
540
- if st.button("Fine-Tune Denoising 🔄"):
541
- images = [Image.open(row["image"]) for _, row in denoising_edited.iterrows()]
542
- texts = [row["text"] for _, row in denoising_edited.iterrows()]
543
- new_model_name = f"{st.session_state['cv_builder'].config.name}-denoise-{int(time.time())}"
544
- new_config = DiffusionConfig(name=new_model_name, base_model=st.session_state['cv_builder'].config.base_model, size="small")
545
- st.session_state['cv_builder'].config = new_config
546
- with st.status("Fine-tuning for denoising... ⏳ (Polishing pixels!)", expanded=True) as status:
547
- st.session_state['cv_builder'].fine_tune_sft(images, texts)
548
- st.session_state['cv_builder'].save_model(new_config.model_path)
549
- status.update(label="Denoising tuned! 🎉 (Pixel shine unleashed!)", state="complete")
550
- zip_path = f"{new_config.model_path}.zip"
551
- zip_files([new_config.model_path], zip_path)
552
- st.markdown(get_download_link(zip_path, "application/zip", "Download Denoised Titan 📦"), unsafe_allow_html=True)
553
- denoising_csv = f"denoise_dataset_{int(time.time())}.csv"
554
- with open(denoising_csv, "w", newline="") as f:
555
- writer = csv.writer(f)
556
- writer.writerow(["image", "text"])
557
- for _, row in denoising_edited.iterrows():
558
- writer.writerow([row["image"], row["text"]])
559
- st.markdown(get_download_link(denoising_csv, "text/csv", "Download Denoising CSV 📜"), unsafe_allow_html=True)
560
-
561
- st.subheader("Use Case 2: Stylize Snapshots 🎨")
562
- stylize_data = [{"image": img, "text": f"Neon {os.path.basename(img).split('-')[4]} style"} for img in captured_images[:min(len(captured_images), 10)]]
563
- stylize_edited = st.data_editor(pd.DataFrame(stylize_data), num_rows="dynamic", help="Craft stylized pairs! 🎨")
564
- if st.button("Fine-Tune Stylization 🔄"):
565
- images = [Image.open(row["image"]) for _, row in stylize_edited.iterrows()]
566
- texts = [row["text"] for _, row in stylize_edited.iterrows()]
567
- new_model_name = f"{st.session_state['cv_builder'].config.name}-stylize-{int(time.time())}"
568
- new_config = DiffusionConfig(name=new_model_name, base_model=st.session_state['cv_builder'].config.base_model, size="small")
569
- st.session_state['cv_builder'].config = new_config
570
- with st.status("Fine-tuning for stylization... ⏳ (Painting pixels!)", expanded=True) as status:
571
- st.session_state['cv_builder'].fine_tune_sft(images, texts)
572
- st.session_state['cv_builder'].save_model(new_config.model_path)
573
- status.update(label="Stylization tuned! 🎉 (Pixel art unleashed!)", state="complete")
574
- zip_path = f"{new_config.model_path}.zip"
575
- zip_files([new_config.model_path], zip_path)
576
- st.markdown(get_download_link(zip_path, "application/zip", "Download Stylized Titan 📦"), unsafe_allow_html=True)
577
- stylize_md = f"stylize_dataset_{int(time.time())}.md"
578
- with open(stylize_md, "w") as f:
579
- f.write("# Stylization Dataset\n\n")
580
- for _, row in stylize_edited.iterrows():
581
- f.write(f"- `{row['image']}`: {row['text']}\n")
582
- st.markdown(get_download_link(stylize_md, "text/markdown", "Download Stylization MD 📝"), unsafe_allow_html=True)
583
-
584
- st.subheader("Use Case 3: Multi-Angle Snapshots 🌐")
585
- multiangle_data = [{"image": img, "text": f"View from {os.path.basename(img).split('-')[4]}"} for img in captured_images[:min(len(captured_images), 10)]]
586
- multiangle_edited = st.data_editor(pd.DataFrame(multiangle_data), num_rows="dynamic", help="Craft multi-angle pairs! 🌐")
587
- if st.button("Fine-Tune Multi-Angle 🔄"):
588
- images = [Image.open(row["image"]) for _, row in multiangle_edited.iterrows()]
589
- texts = [row["text"] for _, row in multiangle_edited.iterrows()]
590
- new_model_name = f"{st.session_state['cv_builder'].config.name}-multiangle-{int(time.time())}"
591
- new_config = DiffusionConfig(name=new_model_name, base_model=st.session_state['cv_builder'].config.base_model, size="small")
592
- st.session_state['cv_builder'].config = new_config
593
- with st.status("Fine-tuning for multi-angle... ⏳ (Spinning pixels!)", expanded=True) as status:
594
- st.session_state['cv_builder'].fine_tune_sft(images, texts)
595
- st.session_state['cv_builder'].save_model(new_config.model_path)
596
- status.update(label="Multi-angle tuned! 🎉 (Pixel views unleashed!)", state="complete")
597
  zip_path = f"{new_config.model_path}.zip"
598
- zip_files([new_config.model_path], zip_path)
599
- st.markdown(get_download_link(zip_path, "application/zip", "Download Multi-Angle Titan 📦"), unsafe_allow_html=True)
600
- multiangle_csv = f"multiangle_dataset_{int(time.time())}.csv"
601
- with open(multiangle_csv, "w", newline="") as f:
602
- writer = csv.writer(f)
603
- writer.writerow(["image", "text"])
604
- for _, row in multiangle_edited.iterrows():
605
- writer.writerow([row["image"], row["text"]])
606
- st.markdown(get_download_link(multiangle_csv, "text/csv", "Download Multi-Angle CSV 📜"), unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
 
608
  with tab4:
609
- st.header("Test Titan (CV) 🧪 (Unleash Your Pixel Power!)")
610
- if not st.session_state['cv_loaded'] or not isinstance(st.session_state['cv_builder'], (MicroDiffusionBuilder, LatentDiffusionBuilder, FluxDiffusionBuilder)):
611
- st.warning("Please build or load a CV Titan first! ⚠️ (No artist, no masterpiece!)")
612
  else:
613
- st.subheader("Test Your Titan 🎨")
614
- test_prompt = st.text_area("Prompt 🎤", "Neon glow from cam0", help="Dream up a wild image—your Titan’s ready to paint! 🖌️")
615
- if st.button("Generate ▶️"):
616
- with st.spinner("Crafting your masterpiece... (Titan’s mixing pixels!)"):
617
- image = st.session_state['cv_builder'].generate(test_prompt)
618
- st.image(image, caption=f"Generated: {test_prompt}", use_container_width=True)
 
 
 
 
 
 
 
 
 
 
619
 
620
  with tab5:
621
- st.header("Agentic RAG Party (CV) 🌐 (Pixel Party Extravaganza!)")
622
- st.write("Generate superhero party vibes from your tuned Titan! 🎉")
623
- if not st.session_state['cv_loaded'] or not isinstance(st.session_state['cv_builder'], (MicroDiffusionBuilder, LatentDiffusionBuilder, FluxDiffusionBuilder)):
624
- st.warning("Please build or load a CV Titan first! ⚠️ (No artist, no party!)")
625
  else:
626
- if st.button("Run RAG Demo 🎉"):
627
- with st.spinner("Loading your pixel party titan... ⏳ (Titan’s grabbing its brush!)"):
628
- class CVPartyAgent:
629
- def __init__(self, pipeline):
630
- self.pipeline = pipeline
631
- def generate(self, prompt: str) -> Image.Image:
632
- return self.pipeline(prompt, num_inference_steps=50).images[0]
633
- def plan_party(self):
634
- prompts = [
635
- "Gold-plated Batman statue from cam0",
636
- "VR superhero battle scene from cam1",
637
- "Neon-lit Avengers tower from cam2"
638
- ]
639
- data = [{"Theme": f"Scene {i+1}", "Image Idea": prompt} for i, prompt in enumerate(prompts)]
640
- return pd.DataFrame(data)
641
- agent = CVPartyAgent(st.session_state['cv_builder'].pipeline)
642
- st.write("Party agent ready! 🎨 (Time to paint an epic bash!)")
643
- with st.spinner("Crafting superhero party visuals... ⏳ (Pixels assemble!)"):
644
- try:
645
- plan_df = agent.plan_party()
646
- st.dataframe(plan_df)
647
- for _, row in plan_df.iterrows():
648
- image = agent.generate(row["Image Idea"])
649
- st.image(image, caption=f"{row['Theme']} - {row['Image Idea']}", use_container_width=True)
650
- except Exception as e:
651
- st.error(f"Party crashed: {str(e)} 💥 (Pixel oopsie!)")
652
- logger.error(f"RAG demo failed: {str(e)}")
653
-
654
  st.sidebar.subheader("Action Logs 📜")
655
  log_container = st.sidebar.empty()
656
  with log_container:
657
  for record in log_records:
658
  st.write(f"{record.asctime} - {record.levelname} - {record.message}")
659
 
 
660
  update_gallery()
 
5
  import streamlit as st
6
  import pandas as pd
7
  import torch
8
+ from transformers import AutoModelForCausalLM, AutoTokenizer
9
  from torch.utils.data import Dataset, DataLoader
10
  import csv
11
  import time
12
  from dataclasses import dataclass
13
+ from typing import Optional, Tuple
14
  import zipfile
15
  import math
16
  from PIL import Image
17
  import random
18
  import logging
19
  import numpy as np
20
+ from diffusers import StableDiffusionPipeline, DDPMPipeline, EulerAncestralDiscreteScheduler
 
21
 
22
+ # Logging setup with a custom buffer
23
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
24
  logger = logging.getLogger(__name__)
25
  log_records = []
 
30
 
31
  logger.addHandler(LogCaptureHandler())
32
 
33
+ # Page Configuration
34
  st.set_page_config(
35
  page_title="SFT Tiny Titans 🚀",
36
  page_icon="🤖",
 
39
  menu_items={
40
  'Get Help': 'https://huggingface.co/awacke1',
41
  'Report a Bug': 'https://huggingface.co/spaces/awacke1',
42
+ 'About': "Tiny Titans: Small models, big dreams, and a sprinkle of chaos! 🌌"
43
  }
44
  )
45
 
46
+ # Initialize st.session_state
47
  if 'captured_images' not in st.session_state:
48
  st.session_state['captured_images'] = []
49
+ if 'builder' not in st.session_state:
50
+ st.session_state['builder'] = None
51
+ if 'model_loaded' not in st.session_state:
52
+ st.session_state['model_loaded'] = False
53
+
54
+ # Model Configuration Classes
55
+ @dataclass
56
+ class ModelConfig:
57
+ name: str
58
+ base_model: str
59
+ size: str
60
+ domain: Optional[str] = None
61
+ model_type: str = "causal_lm"
62
+ @property
63
+ def model_path(self):
64
+ return f"models/{self.name}"
65
 
66
  @dataclass
67
  class DiffusionConfig:
 
68
  name: str
69
  base_model: str
70
  size: str
 
72
  def model_path(self):
73
  return f"diffusion_models/{self.name}"
74
 
75
+ # Datasets
76
+ class SFTDataset(Dataset):
77
+ def __init__(self, data, tokenizer, max_length=128):
78
+ self.data = data
79
+ self.tokenizer = tokenizer
80
+ self.max_length = max_length
81
+ def __len__(self):
82
+ return len(self.data)
83
+ def __getitem__(self, idx):
84
+ prompt = self.data[idx]["prompt"]
85
+ response = self.data[idx]["response"]
86
+ full_text = f"{prompt} {response}"
87
+ full_encoding = self.tokenizer(full_text, max_length=self.max_length, padding="max_length", truncation=True, return_tensors="pt")
88
+ prompt_encoding = self.tokenizer(prompt, max_length=self.max_length, padding=False, truncation=True, return_tensors="pt")
89
+ input_ids = full_encoding["input_ids"].squeeze()
90
+ attention_mask = full_encoding["attention_mask"].squeeze()
91
+ labels = input_ids.clone()
92
+ prompt_len = prompt_encoding["input_ids"].shape[1]
93
+ if prompt_len < self.max_length:
94
+ labels[:prompt_len] = -100
95
+ return {"input_ids": input_ids, "attention_mask": attention_mask, "labels": labels}
96
+
97
  class DiffusionDataset(Dataset):
 
98
  def __init__(self, images, texts):
99
  self.images = images
100
  self.texts = texts
 
103
  def __getitem__(self, idx):
104
  return {"image": self.images[idx], "text": self.texts[idx]}
105
 
106
+ # Model Builders
107
+ class ModelBuilder:
108
  def __init__(self):
109
  self.config = None
110
+ self.model = None
111
+ self.tokenizer = None
112
+ self.sft_data = None
113
+ self.jokes = ["Why did the AI go to therapy? Too many layers to unpack! 😂", "Training complete! Time for a binary coffee break. ☕"]
114
+ def load_model(self, model_path: str, config: Optional[ModelConfig] = None):
115
+ with st.spinner(f"Loading {model_path}... ⏳"):
116
+ self.model = AutoModelForCausalLM.from_pretrained(model_path)
117
+ self.tokenizer = AutoTokenizer.from_pretrained(model_path)
118
+ if self.tokenizer.pad_token is None:
119
+ self.tokenizer.pad_token = self.tokenizer.eos_token
120
+ if config:
121
+ self.config = config
122
+ self.model.to("cuda" if torch.cuda.is_available() else "cpu")
123
+ st.success(f"Model loaded! 🎉 {random.choice(self.jokes)}")
 
124
  return self
125
+ def fine_tune_sft(self, csv_path: str, epochs: int = 3, batch_size: int = 4):
126
+ self.sft_data = []
127
+ with open(csv_path, "r") as f:
128
+ reader = csv.DictReader(f)
129
+ for row in reader:
130
+ self.sft_data.append({"prompt": row["prompt"], "response": row["response"]})
131
+ dataset = SFTDataset(self.sft_data, self.tokenizer)
132
+ dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
133
+ optimizer = torch.optim.AdamW(self.model.parameters(), lr=2e-5)
134
+ self.model.train()
135
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
136
+ self.model.to(device)
137
+ for epoch in range(epochs):
138
+ with st.spinner(f"Training epoch {epoch + 1}/{epochs}... ⚙️"):
139
+ total_loss = 0
140
+ for batch in dataloader:
141
+ optimizer.zero_grad()
142
+ input_ids = batch["input_ids"].to(device)
143
+ attention_mask = batch["attention_mask"].to(device)
144
+ labels = batch["labels"].to(device)
145
+ outputs = self.model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
146
+ loss = outputs.loss
147
+ loss.backward()
148
+ optimizer.step()
149
+ total_loss += loss.item()
150
+ st.write(f"Epoch {epoch + 1} completed. Average loss: {total_loss / len(dataloader):.4f}")
151
+ st.success(f"SFT Fine-tuning completed! 🎉 {random.choice(self.jokes)}")
 
 
 
 
152
  return self
153
  def save_model(self, path: str):
154
+ with st.spinner("Saving model... 💾"):
155
+ os.makedirs(os.path.dirname(path), exist_ok=True)
156
+ self.model.save_pretrained(path)
157
+ self.tokenizer.save_pretrained(path)
158
+ st.success(f"Model saved at {path}! ✅")
159
+ def evaluate(self, prompt: str, status_container=None):
160
+ self.model.eval()
161
+ if status_container:
162
+ status_container.write("Preparing to evaluate... 🧠")
163
  try:
164
+ with torch.no_grad():
165
+ inputs = self.tokenizer(prompt, return_tensors="pt", max_length=128, truncation=True).to(self.model.device)
166
+ outputs = self.model.generate(**inputs, max_new_tokens=50, do_sample=True, top_p=0.95, temperature=0.7)
167
+ return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
 
 
 
 
 
 
 
 
168
  except Exception as e:
169
+ if status_container:
170
+ status_container.error(f"Oops! Something broke: {str(e)} 💥")
171
+ return f"Error: {str(e)}"
172
 
173
+ class DiffusionBuilder:
 
174
  def __init__(self):
175
  self.config = None
176
  self.pipeline = None
177
+ self.model_type = None
178
+ def load_model(self, model_path: str, config: Optional[DiffusionConfig] = None, model_type: str = "StableDiffusion"):
179
+ with st.spinner(f"Loading diffusion model {model_path}... ⏳"):
180
+ if model_type == "StableDiffusion":
181
+ self.pipeline = StableDiffusionPipeline.from_pretrained(model_path, torch_dtype=torch.float32).to("cpu")
182
+ elif model_type == "DDPM":
183
+ self.pipeline = DDPMPipeline.from_pretrained(model_path, torch_dtype=torch.float32).to("cpu")
184
+ self.pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(self.pipeline.scheduler.config)
185
+ if config:
186
+ self.config = config
187
+ self.model_type = model_type
188
+ st.success(f"Diffusion model loaded! 🎨")
 
 
 
189
  return self
190
  def fine_tune_sft(self, images, texts, epochs=3):
191
+ dataset = DiffusionDataset(images, texts)
192
+ dataloader = DataLoader(dataset, batch_size=1, shuffle=True)
193
+ optimizer = torch.optim.AdamW(self.pipeline.unet.parameters(), lr=1e-5)
194
+ self.pipeline.unet.train()
195
+ for epoch in range(epochs):
196
+ with st.spinner(f"Training diffusion epoch {epoch + 1}/{epochs}... ⚙️"):
197
+ total_loss = 0
198
+ for batch in dataloader:
199
+ optimizer.zero_grad()
200
+ image = batch["image"][0].to(self.pipeline.device)
201
+ text = batch["text"][0]
202
+ latents = self.pipeline.vae.encode(torch.tensor(np.array(image)).permute(2, 0, 1).unsqueeze(0).float().to(self.pipeline.device)).latent_dist.sample()
203
+ noise = torch.randn_like(latents)
204
+ timesteps = torch.randint(0, self.pipeline.scheduler.num_train_timesteps, (latents.shape[0],), device=latents.device)
205
+ noisy_latents = self.pipeline.scheduler.add_noise(latents, noise, timesteps)
206
+ text_embeddings = self.pipeline.text_encoder(self.pipeline.tokenizer(text, return_tensors="pt").input_ids.to(self.pipeline.device))[0]
207
+ pred_noise = self.pipeline.unet(noisy_latents, timesteps, encoder_hidden_states=text_embeddings).sample
208
+ loss = torch.nn.functional.mse_loss(pred_noise, noise)
209
+ loss.backward()
210
+ optimizer.step()
211
+ total_loss += loss.item()
212
+ st.write(f"Epoch {epoch + 1} completed. Average loss: {total_loss / len(dataloader):.4f}")
213
+ st.success("Diffusion SFT Fine-tuning completed! 🎨")
 
 
 
 
 
 
 
214
  return self
215
  def save_model(self, path: str):
216
+ with st.spinner("Saving diffusion model... 💾"):
217
+ os.makedirs(os.path.dirname(path), exist_ok=True)
218
+ self.pipeline.save_pretrained(path)
219
+ st.success(f"Diffusion model saved at {path}! ✅")
220
+ def generate(self, prompt: str, image=None):
221
+ if self.model_type == "StableDiffusion":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  return self.pipeline(prompt, num_inference_steps=50).images[0]
223
+ elif self.model_type == "DDPM":
224
+ return self.pipeline(num_inference_steps=50).images[0]
 
 
225
 
226
+ # Utility Functions
227
  def generate_filename(sequence, ext="png"):
 
228
  from datetime import datetime
229
  import pytz
230
  central = pytz.timezone('US/Central')
231
+ timestamp = datetime.now(central).strftime("%d%m%Y%H%M%S%p")
232
+ return f"{sequence}{timestamp}.{ext}"
233
 
234
  def get_download_link(file_path, mime_type="text/plain", label="Download"):
235
+ with open(file_path, 'rb') as f:
236
+ data = f.read()
237
+ b64 = base64.b64encode(data).decode()
238
+ return f'<a href="data:{mime_type};base64,{b64}" download="{os.path.basename(file_path)}">{label} 📥</a>'
239
+
240
+ def zip_directory(directory_path, zip_path):
241
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
242
+ for root, _, files in os.walk(directory_path):
 
 
 
 
 
 
243
  for file in files:
244
+ zipf.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), os.path.dirname(directory_path)))
245
+
246
+ def get_model_files(model_type="causal_lm"):
247
+ path = "models/*" if model_type == "causal_lm" else "diffusion_models/*"
248
+ return [d for d in glob.glob(path) if os.path.isdir(d)]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
  def get_gallery_files(file_types):
251
+ return sorted([f for ext in file_types for f in glob.glob(f"*.{ext}")])
 
252
 
253
  def update_gallery():
 
254
  media_files = get_gallery_files(["png"])
255
  if media_files:
256
  cols = st.sidebar.columns(2)
257
  for idx, file in enumerate(media_files[:gallery_size * 2]):
258
  with cols[idx % 2]:
259
  st.image(Image.open(file), caption=file, use_container_width=True)
260
+ st.markdown(get_download_link(file, "image/png", "Download Image"), unsafe_allow_html=True)
261
+
262
+ # Mock Search Tool for RAG
263
+ def mock_search(query: str) -> str:
264
+ if "superhero" in query.lower():
265
+ return "Latest trends: Gold-plated Batman statues, VR superhero battles."
266
+ return "No relevant results found."
267
+
268
+ class PartyPlannerAgent:
269
+ def __init__(self, model, tokenizer):
270
+ self.model = model
271
+ self.tokenizer = tokenizer
272
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
273
+ self.model.to(self.device)
274
+ def generate(self, prompt: str) -> str:
275
+ self.model.eval()
276
+ with torch.no_grad():
277
+ inputs = self.tokenizer(prompt, return_tensors="pt", max_length=128, truncation=True).to(self.device)
278
+ outputs = self.model.generate(**inputs, max_new_tokens=100, do_sample=True, top_p=0.95, temperature=0.7)
279
+ return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
280
+ def plan_party(self, task: str) -> pd.DataFrame:
281
+ search_result = mock_search("superhero party trends")
282
+ prompt = f"Given this context: '{search_result}'\n{task}"
283
+ plan_text = self.generate(prompt)
284
+ locations = {"Wayne Manor": (42.3601, -71.0589), "New York": (40.7128, -74.0060)}
285
+ wayne_coords = locations["Wayne Manor"]
286
+ travel_times = {loc: calculate_cargo_travel_time(coords, wayne_coords) for loc, coords in locations.items() if loc != "Wayne Manor"}
287
+ data = [
288
+ {"Location": "New York", "Travel Time (hrs)": travel_times["New York"], "Luxury Idea": "Gold-plated Batman statues"},
289
+ {"Location": "Wayne Manor", "Travel Time (hrs)": 0.0, "Luxury Idea": "VR superhero battles"}
290
+ ]
291
+ return pd.DataFrame(data)
292
+
293
+ class CVPartyPlannerAgent:
294
+ def __init__(self, pipeline):
295
+ self.pipeline = pipeline
296
+ def generate(self, prompt: str) -> Image.Image:
297
+ return self.pipeline(prompt, num_inference_steps=50).images[0]
298
+ def plan_party(self, task: str) -> pd.DataFrame:
299
+ search_result = mock_search("superhero party trends")
300
+ prompt = f"Given this context: '{search_result}'\n{task}"
301
+ data = [
302
+ {"Theme": "Batman", "Image Idea": "Gold-plated Batman statue"},
303
+ {"Theme": "Avengers", "Image Idea": "VR superhero battle scene"}
304
+ ]
305
+ return pd.DataFrame(data)
306
+
307
+ def calculate_cargo_travel_time(origin_coords: Tuple[float, float], destination_coords: Tuple[float, float], cruising_speed_kmh: float = 750.0) -> float:
308
+ def to_radians(degrees: float) -> float:
309
+ return degrees * (math.pi / 180)
310
+ lat1, lon1 = map(to_radians, origin_coords)
311
+ lat2, lon2 = map(to_radians, destination_coords)
312
+ EARTH_RADIUS_KM = 6371.0
313
+ dlon = lon2 - lon1
314
+ dlat = lat2 - lat1
315
+ a = (math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2)
316
+ c = 2 * math.asin(math.sqrt(a))
317
+ distance = EARTH_RADIUS_KM * c
318
+ actual_distance = distance * 1.1
319
+ flight_time = (actual_distance / cruising_speed_kmh) + 1.0
320
+ return round(flight_time, 2)
321
+
322
+ # Main App
323
+ st.title("SFT Tiny Titans 🚀 (Small but Mighty!)")
324
+
325
+ # Sidebar Galleries
326
  st.sidebar.header("Media Gallery 🎨")
327
+ gallery_size = st.sidebar.slider("Gallery Size", 1, 10, 4)
328
  update_gallery()
329
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  st.sidebar.subheader("Model Management 🗂️")
331
+ model_type = st.sidebar.selectbox("Model Type", ["Causal LM", "Diffusion"])
332
+ model_dirs = get_model_files("causal_lm" if model_type == "Causal LM" else "diffusion")
333
  selected_model = st.sidebar.selectbox("Select Saved Model", ["None"] + model_dirs)
 
334
  if selected_model != "None" and st.sidebar.button("Load Model 📂"):
335
+ builder = ModelBuilder() if model_type == "Causal LM" else DiffusionBuilder()
336
+ config = (ModelConfig if model_type == "Causal LM" else DiffusionConfig)(name=os.path.basename(selected_model), base_model="unknown", size="small")
337
+ builder.load_model(selected_model, config)
338
+ st.session_state['builder'] = builder
339
+ st.session_state['model_loaded'] = True
340
+ st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
+ # Tabs (Reordered: Camera Snap first)
343
+ tab1, tab2, tab3, tab4, tab5 = st.tabs(["Camera Snap 📷", "Fine-Tune Titan 🔧", "Build Titan 🌱", "Test Titan 🧪", "Agentic RAG Party 🌐"])
 
 
 
344
 
345
  with tab1:
346
+ st.header("Camera Snap 📷 (Dual Capture!)")
347
+ slice_count = st.number_input("Image Slice Count", min_value=1, max_value=20, value=10)
348
+ video_length = st.number_input("Video Length (seconds)", min_value=1, max_value=30, value=10)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  cols = st.columns(2)
350
  with cols[0]:
351
+ st.subheader("Camera 0")
352
+ cam0_img = st.camera_input("Take a picture - Cam 0", key="cam0")
 
 
 
 
 
 
353
  if cam0_img:
354
+ filename = generate_filename(0)
355
  with open(filename, "wb") as f:
356
  f.write(cam0_img.getvalue())
357
  st.image(Image.open(filename), caption=filename, use_container_width=True)
358
  logger.info(f"Saved snapshot from Camera 0: {filename}")
359
  st.session_state['captured_images'].append(filename)
360
  update_gallery()
361
+ if st.button(f"Capture {slice_count} Frames - Cam 0 📸"):
362
+ st.session_state['cam0_frames'] = []
363
+ for i in range(slice_count):
364
+ img = st.camera_input(f"Frame {i} - Cam 0", key=f"cam0_frame_{i}_{time.time()}")
365
+ if img:
366
+ filename = generate_filename(f"0_{i}")
367
+ with open(filename, "wb") as f:
368
+ f.write(img.getvalue())
369
+ st.session_state['cam0_frames'].append(filename)
370
+ logger.info(f"Saved frame {i} from Camera 0: {filename}")
371
+ time.sleep(1.0 / slice_count)
372
+ st.session_state['captured_images'].extend(st.session_state['cam0_frames'])
373
+ update_gallery()
374
+ for frame in st.session_state['cam0_frames']:
375
+ st.image(Image.open(frame), caption=frame, use_container_width=True)
376
  with cols[1]:
377
+ st.subheader("Camera 1")
378
+ cam1_img = st.camera_input("Take a picture - Cam 1", key="cam1")
 
 
 
 
 
 
379
  if cam1_img:
380
+ filename = generate_filename(1)
381
  with open(filename, "wb") as f:
382
  f.write(cam1_img.getvalue())
383
  st.image(Image.open(filename), caption=filename, use_container_width=True)
384
  logger.info(f"Saved snapshot from Camera 1: {filename}")
385
  st.session_state['captured_images'].append(filename)
386
  update_gallery()
387
+ if st.button(f"Capture {slice_count} Frames - Cam 1 📸"):
388
+ st.session_state['cam1_frames'] = []
389
+ for i in range(slice_count):
390
+ img = st.camera_input(f"Frame {i} - Cam 1", key=f"cam1_frame_{i}_{time.time()}")
391
+ if img:
392
+ filename = generate_filename(f"1_{i}")
393
+ with open(filename, "wb") as f:
394
+ f.write(img.getvalue())
395
+ st.session_state['cam1_frames'].append(filename)
396
+ logger.info(f"Saved frame {i} from Camera 1: {filename}")
397
+ time.sleep(1.0 / slice_count)
398
+ st.session_state['captured_images'].extend(st.session_state['cam1_frames'])
399
+ update_gallery()
400
+ for frame in st.session_state['cam1_frames']:
401
+ st.image(Image.open(frame), caption=frame, use_container_width=True)
402
 
403
+ with tab2:
404
+ st.header("Fine-Tune Titan 🔧")
405
+ if 'builder' not in st.session_state or not st.session_state.get('model_loaded', False):
406
+ st.warning("Please build or load a Titan first! ⚠️")
407
  else:
408
+ if isinstance(st.session_state['builder'], ModelBuilder):
409
+ uploaded_csv = st.file_uploader("Upload CSV for SFT", type="csv")
410
+ if uploaded_csv and st.button("Fine-Tune with Uploaded CSV 🔄"):
411
+ csv_path = f"uploaded_sft_data_{int(time.time())}.csv"
412
+ with open(csv_path, "wb") as f:
413
+ f.write(uploaded_csv.read())
414
+ new_model_name = f"{st.session_state['builder'].config.name}-sft-{int(time.time())}"
415
+ new_config = ModelConfig(name=new_model_name, base_model=st.session_state['builder'].config.base_model, size="small")
416
+ st.session_state['builder'].config = new_config
417
+ st.session_state['builder'].fine_tune_sft(csv_path)
418
+ st.session_state['builder'].save_model(new_config.model_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  zip_path = f"{new_config.model_path}.zip"
420
+ zip_directory(new_config.model_path, zip_path)
421
+ st.markdown(get_download_link(zip_path, "application/zip", "Download Fine-Tuned Titan"), unsafe_allow_html=True)
422
+ elif isinstance(st.session_state['builder'], DiffusionBuilder):
423
+ captured_images = get_gallery_files(["png"])
424
+ if len(captured_images) >= 2:
425
+ demo_data = [{"image": img, "text": f"Superhero {os.path.basename(img).split('.')[0]}"} for img in captured_images[:min(len(captured_images), slice_count)]]
426
+ edited_data = st.data_editor(pd.DataFrame(demo_data), num_rows="dynamic")
427
+ if st.button("Fine-Tune with Dataset 🔄"):
428
+ images = [Image.open(row["image"]) for _, row in edited_data.iterrows()]
429
+ texts = [row["text"] for _, row in edited_data.iterrows()]
430
+ new_model_name = f"{st.session_state['builder'].config.name}-sft-{int(time.time())}"
431
+ new_config = DiffusionConfig(name=new_model_name, base_model=st.session_state['builder'].config.base_model, size="small")
432
+ st.session_state['builder'].config = new_config
433
+ st.session_state['builder'].fine_tune_sft(images, texts)
434
+ st.session_state['builder'].save_model(new_config.model_path)
435
+ zip_path = f"{new_config.model_path}.zip"
436
+ zip_directory(new_config.model_path, zip_path)
437
+ st.markdown(get_download_link(zip_path, "application/zip", "Download Fine-Tuned Diffusion Model"), unsafe_allow_html=True)
438
+ csv_path = f"sft_dataset_{int(time.time())}.csv"
439
+ with open(csv_path, "w", newline="") as f:
440
+ writer = csv.writer(f)
441
+ writer.writerow(["image", "text"])
442
+ for _, row in edited_data.iterrows():
443
+ writer.writerow([row["image"], row["text"]])
444
+ st.markdown(get_download_link(csv_path, "text/csv", "Download SFT Dataset CSV"), unsafe_allow_html=True)
445
+
446
+ with tab3:
447
+ st.header("Build Titan 🌱")
448
+ model_type = st.selectbox("Model Type", ["Causal LM", "Diffusion"], key="build_type")
449
+ base_model_options = {
450
+ "Causal LM": ["HuggingFaceTB/SmolLM-135M", "Qwen/Qwen1.5-0.5B-Chat"],
451
+ "Diffusion": [
452
+ "OFA-Sys/small-stable-diffusion-v0 (LDM/Conditional)",
453
+ "google/ddpm-ema-celebahq-256 (DDPM/SDE/Autoregressive Proxy)"
454
+ ]
455
+ }
456
+ base_model = st.selectbox("Select Tiny Model", base_model_options[model_type])
457
+ model_name = st.text_input("Model Name", f"tiny-titan-{int(time.time())}")
458
+ if st.button("Download Model ⬇️"):
459
+ config = (ModelConfig if model_type == "Causal LM" else DiffusionConfig)(name=model_name, base_model=base_model.split(" ")[0], size="small")
460
+ builder = ModelBuilder() if model_type == "Causal LM" else DiffusionBuilder()
461
+ model_type_for_diffusion = "StableDiffusion" if "small-stable-diffusion" in base_model else "DDPM"
462
+ builder.load_model(base_model.split(" ")[0], config, model_type_for_diffusion)
463
+ builder.save_model(config.model_path)
464
+ st.session_state['builder'] = builder
465
+ st.session_state['model_loaded'] = True
466
+ st.rerun()
467
 
468
  with tab4:
469
+ st.header("Test Titan 🧪")
470
+ if 'builder' not in st.session_state or not st.session_state.get('model_loaded', False):
471
+ st.warning("Please build or load a Titan first! ⚠️")
472
  else:
473
+ captured_images = get_gallery_files(["png"])
474
+ if captured_images:
475
+ selected_image = st.selectbox("Select Image", captured_images)
476
+ prompt = st.text_area("Enter Text Prompt", f"Superhero {os.path.basename(selected_image).split('.')[0]}")
477
+ pipeline_options = ["Stable Diffusion (LDM/Conditional)", "DDPM (DDPM/SDE/Autoregressive Proxy)"] if isinstance(st.session_state['builder'], DiffusionBuilder) else ["Causal LM"]
478
+ selected_pipeline = st.selectbox("Select Pipeline", pipeline_options)
479
+ if st.button("Run Test 🚀"):
480
+ if isinstance(st.session_state['builder'], ModelBuilder):
481
+ result = st.session_state['builder'].evaluate(prompt)
482
+ st.write(f"**Generated Response**: {result}")
483
+ elif isinstance(st.session_state['builder'], DiffusionBuilder):
484
+ if selected_pipeline == "Stable Diffusion (LDM/Conditional)":
485
+ image = st.session_state['builder'].generate(prompt)
486
+ else: # DDPM
487
+ image = st.session_state['builder'].generate(prompt)
488
+ st.image(image, caption=f"Generated from {selected_pipeline}")
489
 
490
  with tab5:
491
+ st.header("Agentic RAG Party 🌐")
492
+ if 'builder' not in st.session_state or not st.session_state.get('model_loaded', False):
493
+ st.warning("Please build or load a Titan first! ⚠️")
 
494
  else:
495
+ if isinstance(st.session_state['builder'], ModelBuilder):
496
+ if st.button("Run NLP RAG Demo 🎉"):
497
+ agent = PartyPlannerAgent(st.session_state['builder'].model, st.session_state['builder'].tokenizer)
498
+ task = "Plan a luxury superhero-themed party at Wayne Manor."
499
+ plan_df = agent.plan_party(task)
500
+ st.dataframe(plan_df)
501
+ elif isinstance(st.session_state['builder'], DiffusionBuilder):
502
+ if st.button("Run CV RAG Demo 🎉"):
503
+ agent = CVPartyPlannerAgent(st.session_state['builder'].pipeline)
504
+ task = "Generate images for a luxury superhero-themed party."
505
+ plan_df = agent.plan_party(task)
506
+ st.dataframe(plan_df)
507
+ for _, row in plan_df.iterrows():
508
+ image = agent.generate(row["Image Idea"])
509
+ st.image(image, caption=f"{row['Theme']} - {row['Image Idea']}")
510
+
511
+ # Display Logs
 
 
 
 
 
 
 
 
 
 
 
512
  st.sidebar.subheader("Action Logs 📜")
513
  log_container = st.sidebar.empty()
514
  with log_container:
515
  for record in log_records:
516
  st.write(f"{record.asctime} - {record.levelname} - {record.message}")
517
 
518
+ # Initial Gallery Update
519
  update_gallery()