awacke1 commited on
Commit
481e614
·
verified ·
1 Parent(s): 37158f8

Create backup-03252025.app.py

Browse files
Files changed (1) hide show
  1. backup-03252025.app.py +564 -0
backup-03252025.app.py ADDED
@@ -0,0 +1,564 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import glob
4
+ import base64
5
+ import time
6
+ import shutil
7
+ import streamlit as st
8
+ import pandas as pd
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from transformers import AutoModelForCausalLM, AutoTokenizer, AutoModel
13
+ from diffusers import StableDiffusionPipeline
14
+ from torch.utils.data import Dataset, DataLoader
15
+ import csv
16
+ import fitz
17
+ import requests
18
+ from PIL import Image
19
+ import cv2
20
+ import numpy as np
21
+ import logging
22
+ import asyncio
23
+ import aiofiles
24
+ from io import BytesIO
25
+ from dataclasses import dataclass
26
+ from typing import Optional, Tuple
27
+ import zipfile
28
+ import math
29
+ import random
30
+ import re
31
+
32
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
33
+ logger = logging.getLogger(__name__)
34
+ log_records = []
35
+
36
+ class LogCaptureHandler(logging.Handler):
37
+ def emit(self, record):
38
+ log_records.append(record)
39
+
40
+ logger.addHandler(LogCaptureHandler())
41
+
42
+ st.set_page_config(
43
+ page_title="AI Vision & SFT Titans 🚀",
44
+ page_icon="🤖",
45
+ layout="wide",
46
+ initial_sidebar_state="expanded",
47
+ menu_items={
48
+ 'Get Help': 'https://huggingface.co/awacke1',
49
+ 'Report a Bug': 'https://huggingface.co/spaces/awacke1',
50
+ 'About': "AI Vision & SFT Titans: PDFs, OCR, Image Gen, Line Drawings, Custom Diffusion, and SFT on CPU! 🌌"
51
+ }
52
+ )
53
+
54
+ if 'history' not in st.session_state:
55
+ st.session_state['history'] = []
56
+ if 'builder' not in st.session_state:
57
+ st.session_state['builder'] = None
58
+ if 'model_loaded' not in st.session_state:
59
+ st.session_state['model_loaded'] = False
60
+ if 'processing' not in st.session_state:
61
+ st.session_state['processing'] = {}
62
+ if 'asset_checkboxes' not in st.session_state:
63
+ st.session_state['asset_checkboxes'] = {}
64
+ if 'downloaded_pdfs' not in st.session_state:
65
+ st.session_state['downloaded_pdfs'] = {}
66
+ if 'unique_counter' not in st.session_state:
67
+ st.session_state['unique_counter'] = 0
68
+ if 'selected_model_type' not in st.session_state:
69
+ st.session_state['selected_model_type'] = "Causal LM"
70
+ if 'selected_model' not in st.session_state:
71
+ st.session_state['selected_model'] = "None"
72
+ if 'cam0_file' not in st.session_state:
73
+ st.session_state['cam0_file'] = None
74
+ if 'cam1_file' not in st.session_state:
75
+ st.session_state['cam1_file'] = None
76
+
77
+ @dataclass
78
+ class ModelConfig:
79
+ name: str
80
+ base_model: str
81
+ size: str
82
+ domain: Optional[str] = None
83
+ model_type: str = "causal_lm"
84
+ @property
85
+ def model_path(self):
86
+ return f"models/{self.name}"
87
+
88
+ @dataclass
89
+ class DiffusionConfig:
90
+ name: str
91
+ base_model: str
92
+ size: str
93
+ domain: Optional[str] = None
94
+ @property
95
+ def model_path(self):
96
+ return f"diffusion_models/{self.name}"
97
+
98
+ class ModelBuilder:
99
+ def __init__(self):
100
+ self.config = None
101
+ self.model = None
102
+ self.tokenizer = None
103
+ self.jokes = ["Why did the AI go to therapy? Too many layers to unpack! 😂", "Training complete! Time for a binary coffee break. ☕"]
104
+ def load_model(self, model_path: str, config: Optional[ModelConfig] = None):
105
+ with st.spinner(f"Loading {model_path}... ⏳"):
106
+ self.model = AutoModelForCausalLM.from_pretrained(model_path)
107
+ self.tokenizer = AutoTokenizer.from_pretrained(model_path)
108
+ if self.tokenizer.pad_token is None:
109
+ self.tokenizer.pad_token = self.tokenizer.eos_token
110
+ if config:
111
+ self.config = config
112
+ self.model.to("cuda" if torch.cuda.is_available() else "cpu")
113
+ st.success(f"Model loaded! 🎉 {random.choice(self.jokes)}")
114
+ return self
115
+ def save_model(self, path: str):
116
+ with st.spinner("Saving model... 💾"):
117
+ os.makedirs(os.path.dirname(path), exist_ok=True)
118
+ self.model.save_pretrained(path)
119
+ self.tokenizer.save_pretrained(path)
120
+ st.success(f"Model saved at {path}! ✅")
121
+
122
+ class DiffusionBuilder:
123
+ def __init__(self):
124
+ self.config = None
125
+ self.pipeline = None
126
+ def load_model(self, model_path: str, config: Optional[DiffusionConfig] = None):
127
+ with st.spinner(f"Loading diffusion model {model_path}... ⏳"):
128
+ self.pipeline = StableDiffusionPipeline.from_pretrained(model_path, torch_dtype=torch.float32).to("cpu")
129
+ if config:
130
+ self.config = config
131
+ st.success(f"Diffusion model loaded! 🎨")
132
+ return self
133
+ def save_model(self, path: str):
134
+ with st.spinner("Saving diffusion model... 💾"):
135
+ os.makedirs(os.path.dirname(path), exist_ok=True)
136
+ self.pipeline.save_pretrained(path)
137
+ st.success(f"Diffusion model saved at {path}! ✅")
138
+ def generate(self, prompt: str):
139
+ return self.pipeline(prompt, num_inference_steps=20).images[0]
140
+
141
+ def generate_filename(sequence, ext="png"):
142
+ timestamp = time.strftime("%d%m%Y%H%M%S")
143
+ return f"{sequence}_{timestamp}.{ext}"
144
+
145
+ def pdf_url_to_filename(url):
146
+ safe_name = re.sub(r'[<>:"/\\|?*]', '_', url)
147
+ return f"{safe_name}.pdf"
148
+
149
+ def get_download_link(file_path, mime_type="application/pdf", label="Download"):
150
+ with open(file_path, 'rb') as f:
151
+ data = f.read()
152
+ b64 = base64.b64encode(data).decode()
153
+ return f'<a href="data:{mime_type};base64,{b64}" download="{os.path.basename(file_path)}">{label}</a>'
154
+
155
+ def zip_directory(directory_path, zip_path):
156
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
157
+ for root, _, files in os.walk(directory_path):
158
+ for file in files:
159
+ zipf.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), os.path.dirname(directory_path)))
160
+
161
+ def get_model_files(model_type="causal_lm"):
162
+ path = "models/*" if model_type == "causal_lm" else "diffusion_models/*"
163
+ dirs = [d for d in glob.glob(path) if os.path.isdir(d)]
164
+ return dirs if dirs else ["None"]
165
+
166
+ def get_gallery_files(file_types=["png", "pdf"]):
167
+ return sorted(list(set([f for ext in file_types for f in glob.glob(f"*.{ext}")]))) # Deduplicate files
168
+
169
+ def get_pdf_files():
170
+ return sorted(glob.glob("*.pdf"))
171
+
172
+ def download_pdf(url, output_path):
173
+ try:
174
+ response = requests.get(url, stream=True, timeout=10)
175
+ if response.status_code == 200:
176
+ with open(output_path, "wb") as f:
177
+ for chunk in response.iter_content(chunk_size=8192):
178
+ f.write(chunk)
179
+ return True
180
+ except requests.RequestException as e:
181
+ logger.error(f"Failed to download {url}: {e}")
182
+ return False
183
+
184
+ async def process_pdf_snapshot(pdf_path, mode="single"):
185
+ start_time = time.time()
186
+ status = st.empty()
187
+ status.text(f"Processing PDF Snapshot ({mode})... (0s)")
188
+ try:
189
+ doc = fitz.open(pdf_path)
190
+ output_files = []
191
+ if mode == "single":
192
+ page = doc[0]
193
+ pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
194
+ output_file = generate_filename("single", "png")
195
+ pix.save(output_file)
196
+ output_files.append(output_file)
197
+ elif mode == "twopage":
198
+ for i in range(min(2, len(doc))):
199
+ page = doc[i]
200
+ pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
201
+ output_file = generate_filename(f"twopage_{i}", "png")
202
+ pix.save(output_file)
203
+ output_files.append(output_file)
204
+ elif mode == "allpages":
205
+ for i in range(len(doc)):
206
+ page = doc[i]
207
+ pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
208
+ output_file = generate_filename(f"page_{i}", "png")
209
+ pix.save(output_file)
210
+ output_files.append(output_file)
211
+ doc.close()
212
+ elapsed = int(time.time() - start_time)
213
+ status.text(f"PDF Snapshot ({mode}) completed in {elapsed}s!")
214
+ update_gallery()
215
+ return output_files
216
+ except Exception as e:
217
+ status.error(f"Failed to process PDF: {str(e)}")
218
+ return []
219
+
220
+ async def process_ocr(image, output_file):
221
+ start_time = time.time()
222
+ status = st.empty()
223
+ status.text("Processing GOT-OCR2_0... (0s)")
224
+ tokenizer = AutoTokenizer.from_pretrained("ucaslcl/GOT-OCR2_0", trust_remote_code=True)
225
+ model = AutoModel.from_pretrained("ucaslcl/GOT-OCR2_0", trust_remote_code=True, torch_dtype=torch.float32).to("cpu").eval()
226
+ # Save image to temporary file since GOT-OCR2_0 expects a file path
227
+ temp_file = f"temp_{int(time.time())}.png"
228
+ image.save(temp_file)
229
+ result = model.chat(tokenizer, temp_file, ocr_type='ocr')
230
+ os.remove(temp_file) # Clean up temporary file
231
+ elapsed = int(time.time() - start_time)
232
+ status.text(f"GOT-OCR2_0 completed in {elapsed}s!")
233
+ async with aiofiles.open(output_file, "w") as f:
234
+ await f.write(result)
235
+ update_gallery()
236
+ return result
237
+
238
+ async def process_image_gen(prompt, output_file):
239
+ start_time = time.time()
240
+ status = st.empty()
241
+ status.text("Processing Image Gen... (0s)")
242
+ if st.session_state['builder'] and isinstance(st.session_state['builder'], DiffusionBuilder) and st.session_state['builder'].pipeline:
243
+ pipeline = st.session_state['builder'].pipeline
244
+ else:
245
+ pipeline = StableDiffusionPipeline.from_pretrained("OFA-Sys/small-stable-diffusion-v0", torch_dtype=torch.float32).to("cpu")
246
+ gen_image = pipeline(prompt, num_inference_steps=20).images[0]
247
+ elapsed = int(time.time() - start_time)
248
+ status.text(f"Image Gen completed in {elapsed}s!")
249
+ gen_image.save(output_file)
250
+ update_gallery()
251
+ return gen_image
252
+
253
+ st.title("AI Vision & SFT Titans 🚀")
254
+
255
+ # Sidebar
256
+ model_type = st.sidebar.selectbox("Model Type", ["Causal LM", "Diffusion"], key="sidebar_model_type", index=0 if st.session_state['selected_model_type'] == "Causal LM" else 1)
257
+ model_dirs = get_model_files(model_type)
258
+ if model_dirs and st.session_state['selected_model'] == "None" and "None" not in model_dirs:
259
+ st.session_state['selected_model'] = model_dirs[0]
260
+ selected_model = st.sidebar.selectbox("Select Saved Model", model_dirs, key="sidebar_model_select", index=model_dirs.index(st.session_state['selected_model']) if st.session_state['selected_model'] in model_dirs else 0)
261
+ if selected_model != "None" and st.sidebar.button("Load Model 📂"):
262
+ builder = ModelBuilder() if model_type == "Causal LM" else DiffusionBuilder()
263
+ config = (ModelConfig if model_type == "Causal LM" else DiffusionConfig)(name=os.path.basename(selected_model), base_model="unknown", size="small")
264
+ builder.load_model(selected_model, config)
265
+ st.session_state['builder'] = builder
266
+ st.session_state['model_loaded'] = True
267
+ st.rerun()
268
+
269
+ st.sidebar.header("Captured Files 📜")
270
+ cols = st.sidebar.columns(2)
271
+ with cols[0]:
272
+ if st.button("Zip All 🤐"):
273
+ zip_path = f"all_assets_{int(time.time())}.zip"
274
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
275
+ for file in get_gallery_files():
276
+ zipf.write(file, os.path.basename(file))
277
+ st.sidebar.markdown(get_download_link(zip_path, "application/zip", "Download All Assets"), unsafe_allow_html=True)
278
+ with cols[1]:
279
+ if st.button("Zap All! 🗑️"):
280
+ for file in get_gallery_files():
281
+ os.remove(file)
282
+ st.session_state['asset_checkboxes'].clear()
283
+ st.session_state['downloaded_pdfs'].clear()
284
+ st.session_state['cam0_file'] = None
285
+ st.session_state['cam1_file'] = None
286
+ st.sidebar.success("All assets vaporized! 💨")
287
+ st.rerun()
288
+
289
+ gallery_size = st.sidebar.slider("Gallery Size", 1, 10, 2)
290
+ def update_gallery():
291
+ all_files = get_gallery_files()
292
+ if all_files:
293
+ st.sidebar.subheader("Asset Gallery 📸📖")
294
+ cols = st.sidebar.columns(2)
295
+ for idx, file in enumerate(all_files[:gallery_size * 2]):
296
+ with cols[idx % 2]:
297
+ st.session_state['unique_counter'] += 1
298
+ unique_id = st.session_state['unique_counter']
299
+ if file.endswith('.png'):
300
+ st.image(Image.open(file), caption=os.path.basename(file), use_container_width=True)
301
+ else:
302
+ doc = fitz.open(file)
303
+ pix = doc[0].get_pixmap(matrix=fitz.Matrix(0.5, 0.5))
304
+ img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
305
+ st.image(img, caption=os.path.basename(file), use_container_width=True)
306
+ doc.close()
307
+ checkbox_key = f"asset_{file}_{unique_id}"
308
+ st.session_state['asset_checkboxes'][file] = st.checkbox(
309
+ "Use for SFT/Input",
310
+ value=st.session_state['asset_checkboxes'].get(file, False),
311
+ key=checkbox_key
312
+ )
313
+ mime_type = "image/png" if file.endswith('.png') else "application/pdf"
314
+ st.markdown(get_download_link(file, mime_type, "Snag It! 📥"), unsafe_allow_html=True)
315
+ if st.button("Zap It! 🗑️", key=f"delete_{file}_{unique_id}"):
316
+ os.remove(file)
317
+ if file in st.session_state['asset_checkboxes']:
318
+ del st.session_state['asset_checkboxes'][file]
319
+ if file.endswith('.pdf'):
320
+ url_key = next((k for k, v in st.session_state['downloaded_pdfs'].items() if v == file), None)
321
+ if url_key:
322
+ del st.session_state['downloaded_pdfs'][url_key]
323
+ if file == st.session_state['cam0_file']:
324
+ st.session_state['cam0_file'] = None
325
+ if file == st.session_state['cam1_file']:
326
+ st.session_state['cam1_file'] = None
327
+ st.sidebar.success(f"Asset {os.path.basename(file)} vaporized! 💨")
328
+ st.rerun()
329
+ update_gallery()
330
+
331
+ st.sidebar.subheader("Action Logs 📜")
332
+ log_container = st.sidebar.empty()
333
+ with log_container:
334
+ for record in log_records:
335
+ st.write(f"{record.asctime} - {record.levelname} - {record.message}")
336
+
337
+ st.sidebar.subheader("History 📜")
338
+ history_container = st.sidebar.empty()
339
+ with history_container:
340
+ for entry in st.session_state['history'][-gallery_size * 2:]:
341
+ st.write(entry)
342
+
343
+ tab1, tab2, tab3, tab4 = st.tabs([
344
+ "Camera Snap 📷", "Download PDFs 📥", "Test OCR 🔍", "Build Titan 🌱"
345
+ ])
346
+
347
+ with tab1:
348
+ st.header("Camera Snap 📷")
349
+ st.subheader("Single Capture")
350
+ cols = st.columns(2)
351
+ with cols[0]:
352
+ cam0_img = st.camera_input("Take a picture - Cam 0", key="cam0")
353
+ if cam0_img:
354
+ filename = generate_filename("cam0")
355
+ if st.session_state['cam0_file'] and os.path.exists(st.session_state['cam0_file']):
356
+ os.remove(st.session_state['cam0_file'])
357
+ with open(filename, "wb") as f:
358
+ f.write(cam0_img.getvalue())
359
+ st.session_state['cam0_file'] = filename
360
+ entry = f"Snapshot from Cam 0: {filename}"
361
+ if entry not in st.session_state['history']:
362
+ st.session_state['history'] = [e for e in st.session_state['history'] if not e.startswith("Snapshot from Cam 0:")] + [entry]
363
+ st.image(Image.open(filename), caption="Camera 0", use_container_width=True)
364
+ logger.info(f"Saved snapshot from Camera 0: {filename}")
365
+ update_gallery()
366
+ elif st.session_state['cam0_file'] and os.path.exists(st.session_state['cam0_file']):
367
+ st.image(Image.open(st.session_state['cam0_file']), caption="Camera 0", use_container_width=True)
368
+ with cols[1]:
369
+ cam1_img = st.camera_input("Take a picture - Cam 1", key="cam1")
370
+ if cam1_img:
371
+ filename = generate_filename("cam1")
372
+ if st.session_state['cam1_file'] and os.path.exists(st.session_state['cam1_file']):
373
+ os.remove(st.session_state['cam1_file'])
374
+ with open(filename, "wb") as f:
375
+ f.write(cam1_img.getvalue())
376
+ st.session_state['cam1_file'] = filename
377
+ entry = f"Snapshot from Cam 1: {filename}"
378
+ if entry not in st.session_state['history']:
379
+ st.session_state['history'] = [e for e in st.session_state['history'] if not e.startswith("Snapshot from Cam 1:")] + [entry]
380
+ st.image(Image.open(filename), caption="Camera 1", use_container_width=True)
381
+ logger.info(f"Saved snapshot from Camera 1: {filename}")
382
+ update_gallery()
383
+ elif st.session_state['cam1_file'] and os.path.exists(st.session_state['cam1_file']):
384
+ st.image(Image.open(st.session_state['cam1_file']), caption="Camera 1", use_container_width=True)
385
+
386
+ with tab2:
387
+ st.header("Download PDFs 📥")
388
+ if st.button("Examples 📚"):
389
+ example_urls = [
390
+ "https://arxiv.org/pdf/2308.03892",
391
+ "https://arxiv.org/pdf/1912.01703",
392
+ "https://arxiv.org/pdf/2408.11039",
393
+ "https://arxiv.org/pdf/2109.10282",
394
+ "https://arxiv.org/pdf/2112.10752",
395
+ "https://arxiv.org/pdf/2308.11236",
396
+ "https://arxiv.org/pdf/1706.03762",
397
+ "https://arxiv.org/pdf/2006.11239",
398
+ "https://arxiv.org/pdf/2305.11207",
399
+ "https://arxiv.org/pdf/2106.09685",
400
+ "https://arxiv.org/pdf/2005.11401",
401
+ "https://arxiv.org/pdf/2106.10504"
402
+ ]
403
+ st.session_state['pdf_urls'] = "\n".join(example_urls)
404
+
405
+ url_input = st.text_area("Enter PDF URLs (one per line)", value=st.session_state.get('pdf_urls', ""), height=200)
406
+ if st.button("Robo-Download 🤖"):
407
+ urls = url_input.strip().split("\n")
408
+ progress_bar = st.progress(0)
409
+ status_text = st.empty()
410
+ total_urls = len(urls)
411
+ existing_pdfs = get_pdf_files()
412
+ for idx, url in enumerate(urls):
413
+ if url:
414
+ output_path = pdf_url_to_filename(url)
415
+ status_text.text(f"Fetching {idx + 1}/{total_urls}: {os.path.basename(output_path)}...")
416
+ if output_path not in existing_pdfs:
417
+ if download_pdf(url, output_path):
418
+ st.session_state['downloaded_pdfs'][url] = output_path
419
+ logger.info(f"Downloaded PDF from {url} to {output_path}")
420
+ entry = f"Downloaded PDF: {output_path}"
421
+ if entry not in st.session_state['history']:
422
+ st.session_state['history'].append(entry)
423
+ st.session_state['asset_checkboxes'][output_path] = True # Auto-check the box
424
+ else:
425
+ st.error(f"Failed to nab {url} 😿")
426
+ else:
427
+ st.info(f"Already got {os.path.basename(output_path)}! Skipping... 🐾")
428
+ st.session_state['downloaded_pdfs'][url] = output_path
429
+ progress_bar.progress((idx + 1) / total_urls)
430
+ status_text.text("Robo-Download complete! 🚀")
431
+ update_gallery()
432
+
433
+ mode = st.selectbox("Snapshot Mode", ["Single Page (High-Res)", "Two Pages (High-Res)", "All Pages (High-Res)"], key="download_mode")
434
+ if st.button("Snapshot Selected 📸"):
435
+ selected_pdfs = [path for path in get_gallery_files() if path.endswith('.pdf') and st.session_state['asset_checkboxes'].get(path, False)]
436
+ if selected_pdfs:
437
+ for pdf_path in selected_pdfs:
438
+ mode_key = {"Single Page (High-Res)": "single", "Two Pages (High-Res)": "twopage", "All Pages (High-Res)": "allpages"}[mode]
439
+ snapshots = asyncio.run(process_pdf_snapshot(pdf_path, mode_key))
440
+ for snapshot in snapshots:
441
+ st.image(Image.open(snapshot), caption=snapshot, use_container_width=True)
442
+ st.session_state['asset_checkboxes'][snapshot] = True # Auto-check new snapshots
443
+ update_gallery()
444
+ else:
445
+ st.warning("No PDFs selected for snapshotting! Check some boxes in the sidebar gallery.")
446
+
447
+ with tab3:
448
+ st.header("Test OCR 🔍")
449
+ all_files = get_gallery_files()
450
+ if all_files:
451
+ if st.button("OCR All Assets 🚀"):
452
+ full_text = "# OCR Results\n\n"
453
+ for file in all_files:
454
+ if file.endswith('.png'):
455
+ image = Image.open(file)
456
+ else:
457
+ doc = fitz.open(file)
458
+ pix = doc[0].get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
459
+ image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
460
+ doc.close()
461
+ output_file = generate_filename(f"ocr_{os.path.basename(file)}", "txt")
462
+ result = asyncio.run(process_ocr(image, output_file))
463
+ full_text += f"## {os.path.basename(file)}\n\n{result}\n\n"
464
+ entry = f"OCR Test: {file} -> {output_file}"
465
+ if entry not in st.session_state['history']:
466
+ st.session_state['history'].append(entry)
467
+ md_output_file = f"full_ocr_{int(time.time())}.md"
468
+ with open(md_output_file, "w") as f:
469
+ f.write(full_text)
470
+ st.success(f"Full OCR saved to {md_output_file}")
471
+ st.markdown(get_download_link(md_output_file, "text/markdown", "Download Full OCR Markdown"), unsafe_allow_html=True)
472
+ selected_file = st.selectbox("Select Image or PDF", all_files, key="ocr_select")
473
+ if selected_file:
474
+ if selected_file.endswith('.png'):
475
+ image = Image.open(selected_file)
476
+ else:
477
+ doc = fitz.open(selected_file)
478
+ pix = doc[0].get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
479
+ image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
480
+ doc.close()
481
+ st.image(image, caption="Input Image", use_container_width=True)
482
+ if st.button("Run OCR 🚀", key="ocr_run"):
483
+ output_file = generate_filename("ocr_output", "txt")
484
+ st.session_state['processing']['ocr'] = True
485
+ result = asyncio.run(process_ocr(image, output_file))
486
+ entry = f"OCR Test: {selected_file} -> {output_file}"
487
+ if entry not in st.session_state['history']:
488
+ st.session_state['history'].append(entry)
489
+ st.text_area("OCR Result", result, height=200, key="ocr_result")
490
+ st.success(f"OCR output saved to {output_file}")
491
+ st.session_state['processing']['ocr'] = False
492
+ if selected_file.endswith('.pdf') and st.button("OCR All Pages 🚀", key="ocr_all_pages"):
493
+ doc = fitz.open(selected_file)
494
+ full_text = f"# OCR Results for {os.path.basename(selected_file)}\n\n"
495
+ for i in range(len(doc)):
496
+ pix = doc[i].get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
497
+ image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
498
+ output_file = generate_filename(f"ocr_page_{i}", "txt")
499
+ result = asyncio.run(process_ocr(image, output_file))
500
+ full_text += f"## Page {i + 1}\n\n{result}\n\n"
501
+ entry = f"OCR Test: {selected_file} Page {i + 1} -> {output_file}"
502
+ if entry not in st.session_state['history']:
503
+ st.session_state['history'].append(entry)
504
+ md_output_file = f"full_ocr_{os.path.basename(selected_file)}_{int(time.time())}.md"
505
+ with open(md_output_file, "w") as f:
506
+ f.write(full_text)
507
+ st.success(f"Full OCR saved to {md_output_file}")
508
+ st.markdown(get_download_link(md_output_file, "text/markdown", "Download Full OCR Markdown"), unsafe_allow_html=True)
509
+ else:
510
+ st.warning("No assets in gallery yet. Use Camera Snap or Download PDFs!")
511
+
512
+ with tab4:
513
+ st.header("Build Titan 🌱")
514
+ model_type = st.selectbox("Model Type", ["Causal LM", "Diffusion"], key="build_type")
515
+ base_model = st.selectbox("Select Tiny Model",
516
+ ["HuggingFaceTB/SmolLM-135M", "Qwen/Qwen1.5-0.5B-Chat"] if model_type == "Causal LM" else
517
+ ["OFA-Sys/small-stable-diffusion-v0", "stabilityai/stable-diffusion-2-base"])
518
+ model_name = st.text_input("Model Name", f"tiny-titan-{int(time.time())}")
519
+ domain = st.text_input("Target Domain", "general")
520
+ if st.button("Download Model ⬇️"):
521
+ config = (ModelConfig if model_type == "Causal LM" else DiffusionConfig)(name=model_name, base_model=base_model, size="small", domain=domain)
522
+ builder = ModelBuilder() if model_type == "Causal LM" else DiffusionBuilder()
523
+ builder.load_model(base_model, config)
524
+ builder.save_model(config.model_path)
525
+ st.session_state['builder'] = builder
526
+ st.session_state['model_loaded'] = True
527
+ st.session_state['selected_model_type'] = model_type
528
+ st.session_state['selected_model'] = config.model_path
529
+ entry = f"Built {model_type} model: {model_name}"
530
+ if entry not in st.session_state['history']:
531
+ st.session_state['history'].append(entry)
532
+ st.success(f"Model downloaded and saved to {config.model_path}! 🎉")
533
+ st.rerun()
534
+
535
+ tab5 = st.tabs(["Test Image Gen 🎨"])[0]
536
+ with tab5:
537
+ st.header("Test Image Gen 🎨")
538
+ all_files = get_gallery_files()
539
+ if all_files:
540
+ selected_file = st.selectbox("Select Image or PDF", all_files, key="gen_select")
541
+ if selected_file:
542
+ if selected_file.endswith('.png'):
543
+ image = Image.open(selected_file)
544
+ else:
545
+ doc = fitz.open(selected_file)
546
+ pix = doc[0].get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
547
+ image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
548
+ doc.close()
549
+ st.image(image, caption="Reference Image", use_container_width=True)
550
+ prompt = st.text_area("Prompt", "Generate a neon superhero version of this image", key="gen_prompt")
551
+ if st.button("Run Image Gen 🚀", key="gen_run"):
552
+ output_file = generate_filename("gen_output", "png")
553
+ st.session_state['processing']['gen'] = True
554
+ result = asyncio.run(process_image_gen(prompt, output_file))
555
+ entry = f"Image Gen Test: {prompt} -> {output_file}"
556
+ if entry not in st.session_state['history']:
557
+ st.session_state['history'].append(entry)
558
+ st.image(result, caption="Generated Image", use_container_width=True)
559
+ st.success(f"Image saved to {output_file}")
560
+ st.session_state['processing']['gen'] = False
561
+ else:
562
+ st.warning("No images or PDFs in gallery yet. Use Camera Snap or Download PDFs!")
563
+
564
+ update_gallery()