import aiofiles import asyncio import base64 import fitz import glob import logging import os import pandas as pd import pytz import random import re import requests import shutil import streamlit as st import time import torch import zipfile import json from dataclasses import dataclass from datetime import datetime from diffusers import StableDiffusionPipeline from io import BytesIO from openai import OpenAI from PIL import Image from transformers import AutoModelForCausalLM, AutoTokenizer, AutoModel from typing import Optional client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'), organization=os.getenv('OPENAI_ORG_ID')) logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) log_records = [] class LogCaptureHandler(logging.Handler): def emit(self, record): log_records.append(record) logger.addHandler(LogCaptureHandler()) st.set_page_config( page_title="AI Vision & SFT Titans π", page_icon="π€", layout="wide", initial_sidebar_state="expanded", menu_items={ 'Get Help': 'https://huggingface.co/awacke1', 'Report a Bug': 'https://huggingface.co/spaces/awacke1', 'About': "AI Vision & SFT Titans: PDFs, OCR, Image Gen, Line Drawings, Custom Diffusion, and SFT on CPU! π" } ) st.session_state.setdefault('history', []) st.session_state.setdefault('builder', None) st.session_state.setdefault('model_loaded', False) st.session_state.setdefault('processing', {}) st.session_state.setdefault('asset_checkboxes', {}) st.session_state.setdefault('downloaded_pdfs', {}) st.session_state.setdefault('unique_counter', 0) st.session_state.setdefault('selected_model_type', "Causal LM") st.session_state.setdefault('selected_model', "None") st.session_state.setdefault('cam0_file', None) st.session_state.setdefault('cam1_file', None) st.session_state.setdefault('characters', []) st.session_state.setdefault('char_form_reset', False) if 'asset_gallery_container' not in st.session_state: st.session_state['asset_gallery_container'] = st.sidebar.empty() @dataclass class ModelConfig: name: str base_model: str size: str domain: Optional[str] = None model_type: str = "causal_lm" @property def model_path(self): return f"models/{self.name}" @dataclass class DiffusionConfig: name: str base_model: str size: str domain: Optional[str] = None @property def model_path(self): return f"diffusion_models/{self.name}" class ModelBuilder: def __init__(self): self.config = None self.model = None self.tokenizer = None self.jokes = [ "Why did the AI go to therapy? Too many layers to unpack! π", "Training complete! Time for a binary coffee break. β", "I told my neural network a joke; it couldn't stop dropping bits! π€", "I asked the AI for a pun, and it said, 'I'm punning on parallel processing!' π", "Debugging my code is like a stand-up routineβalways a series of exceptions! π" ] def load_model(self, model_path: str, config: Optional[ModelConfig] = None): with st.spinner(f"Loading {model_path}... β³"): self.model = AutoModelForCausalLM.from_pretrained(model_path) self.tokenizer = AutoTokenizer.from_pretrained(model_path) if self.tokenizer.pad_token is None: self.tokenizer.pad_token = self.tokenizer.eos_token if config: self.config = config self.model.to("cuda" if torch.cuda.is_available() else "cpu") st.success(f"Model loaded! π {random.choice(self.jokes)}") return self def save_model(self, path: str): with st.spinner("Saving model... πΎ"): os.makedirs(os.path.dirname(path), exist_ok=True) self.model.save_pretrained(path) self.tokenizer.save_pretrained(path) st.success(f"Model saved at {path}! β ") class DiffusionBuilder: def __init__(self): self.config = None self.pipeline = None def load_model(self, model_path: str, config: Optional[DiffusionConfig] = None): with st.spinner(f"Loading diffusion model {model_path}... β³"): self.pipeline = StableDiffusionPipeline.from_pretrained(model_path, torch_dtype=torch.float32).to("cpu") if config: self.config = config st.success("Diffusion model loaded! π¨") return self def save_model(self, path: str): with st.spinner("Saving diffusion model... πΎ"): os.makedirs(os.path.dirname(path), exist_ok=True) self.pipeline.save_pretrained(path) st.success(f"Diffusion model saved at {path}! β ") def generate(self, prompt: str): return self.pipeline(prompt, num_inference_steps=20).images[0] def generate_filename(sequence, ext="png"): return f"{sequence}_{time.strftime('%d%m%Y%H%M%S')}.{ext}" def pdf_url_to_filename(url): return re.sub(r'[<>:"/\\|?*]', '_', url) + ".pdf" def get_download_link(file_path, mime_type="application/pdf", label="Download"): return f'<a href="data:{mime_type};base64,{base64.b64encode(open(file_path, "rb").read()).decode()}" download="{os.path.basename(file_path)}">{label}</a>' def zip_directory(directory_path, zip_path): with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: [zipf.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), os.path.dirname(directory_path))) for root, _, files in os.walk(directory_path) for file in files] def get_model_files(model_type="causal_lm"): return [d for d in glob.glob("models/*" if model_type == "causal_lm" else "diffusion_models/*") if os.path.isdir(d)] or ["None"] def get_gallery_files(file_types=["png", "pdf"]): return sorted(list({f for ext in file_types for f in glob.glob(f"*.{ext}")})) def get_pdf_files(): return sorted(glob.glob("*.pdf")) def download_pdf(url, output_path): try: response = requests.get(url, stream=True, timeout=10) if response.status_code == 200: with open(output_path, "wb") as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) ret = True else: ret = False except requests.RequestException as e: logger.error(f"Failed to download {url}: {e}") ret = False return ret async def process_pdf_snapshot(pdf_path, mode="single"): start_time = time.time() status = st.empty() status.text(f"Processing PDF Snapshot ({mode})... (0s)") try: doc = fitz.open(pdf_path) output_files = [] if mode == "single": page = doc[0] pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) output_file = generate_filename("single", "png") pix.save(output_file) output_files.append(output_file) elif mode == "twopage": for i in range(min(2, len(doc))): page = doc[i] pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) output_file = generate_filename(f"twopage_{i}", "png") pix.save(output_file) output_files.append(output_file) elif mode == "allpages": for i in range(len(doc)): page = doc[i] pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) output_file = generate_filename(f"page_{i}", "png") pix.save(output_file) output_files.append(output_file) doc.close() elapsed = int(time.time() - start_time) status.text(f"PDF Snapshot ({mode}) completed in {elapsed}s!") return output_files except Exception as e: status.error(f"Failed to process PDF: {str(e)}") return [] async def process_gpt4o_ocr(image, output_file): start_time = time.time() status = st.empty() status.text("Processing GPT-4o OCR... (0s)") buffered = BytesIO() image.save(buffered, format="PNG") img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") messages = [{ "role": "user", "content": [ {"type": "text", "text": "Extract electronic text, and explain"}, {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_str}", "detail": "auto"}} ] }] try: response = client.chat.completions.create(model="gpt-4o", messages=messages, max_tokens=300) result = response.choices[0].message.content elapsed = int(time.time() - start_time) status.text(f"GPT-4o OCR completed in {elapsed}s!") async with aiofiles.open(output_file, "w") as f: await f.write(result) return result except Exception as e: status.error(f"Failed to process image with GPT-4o: {str(e)}") return "" async def process_image_gen(prompt, output_file): start_time = time.time() status = st.empty() status.text("Processing Image Gen... (0s)") pipeline = (st.session_state['builder'].pipeline if st.session_state.get('builder') and isinstance(st.session_state['builder'], DiffusionBuilder) and st.session_state['builder'].pipeline else StableDiffusionPipeline.from_pretrained("OFA-Sys/small-stable-diffusion-v0", torch_dtype=torch.float32).to("cpu")) gen_image = pipeline(prompt, num_inference_steps=20).images[0] elapsed = int(time.time() - start_time) status.text(f"Image Gen completed in {elapsed}s!") gen_image.save(output_file) return gen_image def process_image_with_prompt(image, prompt, model="gpt-4o-mini", detail="auto"): buffered = BytesIO() image.save(buffered, format="PNG") img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") messages = [{ "role": "user", "content": [ {"type": "text", "text": prompt}, {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_str}", "detail": detail}} ] }] try: response = client.chat.completions.create(model=model, messages=messages, max_tokens=300) return response.choices[0].message.content except Exception as e: return f"Error processing image with GPT: {str(e)}" def process_text_with_prompt(text, prompt, model="gpt-4o-mini"): messages = [{"role": "user", "content": f"{prompt}\n\n{text}"}] try: response = client.chat.completions.create(model=model, messages=messages, max_tokens=300) return response.choices[0].message.content except Exception as e: return f"Error processing text with GPT: {str(e)}" def randomize_character_content(): intro_templates = [ "{char} is a valiant knight who is silent and reserved, he looks handsome but aloof.", "{char} is a mischievous thief with a heart of gold, always sneaking around but helping those in need.", "{char} is a wise scholar who loves books more than people, often lost in thought.", "{char} is a fiery warrior with a short temper, but fiercely loyal to friends.", "{char} is a gentle healer who speaks softly, always carrying herbs and a warm smile." ] greeting_templates = [ "You were startled by the sudden intrusion of a man into your home. 'I am from the knight's guild, and I have been ordered to arrest you.'", "A shadowy figure steps into the light. 'I heard you needed helpβnameβs {char}, best thief in town.'", "A voice calls from behind a stack of books. 'Oh, hello! Iβm {char}, didnβt see you thereβtoo many scrolls!'", "A booming voice echoes, 'Iβm {char}, and Iβm here to fight for justiceβor at least a good brawl!'", "A soft hand touches your shoulder. 'Iβm {char}, here to heal your woundsβdonβt worry, Iβve got you.'" ] name = f"Character_{random.randint(1000, 9999)}" gender = random.choice(["Male", "Female"]) intro = random.choice(intro_templates).format(char=name) greeting = random.choice(greeting_templates).format(char=name) return name, gender, intro, greeting def save_character(character_data): characters = st.session_state.get('characters', []) characters.append(character_data) st.session_state['characters'] = characters with open("characters.json", "w") as f: json.dump(characters, f) def load_characters(): try: with open("characters.json", "r") as f: characters = json.load(f) st.session_state['characters'] = characters except FileNotFoundError: st.session_state['characters'] = [] st.sidebar.subheader("Gallery Settings") st.session_state.setdefault('gallery_size', 2) st.session_state['gallery_size'] = st.sidebar.slider("Gallery Size", 1, 10, st.session_state['gallery_size'], key="gallery_size_slider") tabs = st.tabs([ "Camera Snap π·", "Test OCR π", "MD Gallery π", "Download PDFs π₯", "Build Titan π±", "Test Image Gen π¨", "PDF Process π", "Image Process πΌοΈ", "Character Editor π§βπ¨", "Character Gallery πΌοΈ" ]) (tab_camera, tab_ocr, tab_md_gallery, tab_download, tab_build, tab_imggen, tab_pdf_process, tab_image_process, tab_character_editor, tab_character_gallery) = tabs with tab_camera: st.header("Camera Snap π·") st.subheader("Single Capture") cols = st.columns(2) with cols[0]: cam0_img = st.camera_input("Take a picture - Cam 0", key="cam0") if cam0_img: filename = generate_filename("cam0") if st.session_state['cam0_file'] and os.path.exists(st.session_state['cam0_file']): os.remove(st.session_state['cam0_file']) with open(filename, "wb") as f: f.write(cam0_img.getvalue()) st.session_state['cam0_file'] = filename entry = f"Snapshot from Cam 0: {filename}" st.session_state['history'].append(entry) st.image(Image.open(filename), caption="Camera 0", use_container_width=True) logger.info(f"Saved snapshot from Camera 0: {filename}") with cols[1]: cam1_img = st.camera_input("Take a picture - Cam 1", key="cam1") if cam1_img: filename = generate_filename("cam1") if st.session_state['cam1_file'] and os.path.exists(st.session_state['cam1_file']): os.remove(st.session_state['cam1_file']) with open(filename, "wb") as f: f.write(cam1_img.getvalue()) st.session_state['cam1_file'] = filename entry = f"Snapshot from Cam 1: {filename}" st.session_state['history'].append(entry) st.image(Image.open(filename), caption="Camera 1", use_container_width=True) logger.info(f"Saved snapshot from Camera 1: {filename}") with tab_download: st.header("Download PDFs π₯") if st.button("Examples π"): example_urls = [ "https://arxiv.org/pdf/2308.03892", "https://arxiv.org/pdf/1912.01703", "https://arxiv.org/pdf/2408.11039", "https://arxiv.org/pdf/2109.10282", "https://arxiv.org/pdf/2112.10752", "https://arxiv.org/pdf/2308.11236", "https://arxiv.org/pdf/1706.03762", "https://arxiv.org/pdf/2006.11239", "https://arxiv.org/pdf/2305.11207", "https://arxiv.org/pdf/2106.09685", "https://arxiv.org/pdf/2005.11401", "https://arxiv.org/pdf/2106.10504" ] st.session_state['pdf_urls'] = "\n".join(example_urls) url_input = st.text_area("Enter PDF URLs (one per line)", value=st.session_state.get('pdf_urls', ""), height=200) if st.button("Robo-Download π€"): urls = url_input.strip().split("\n") progress_bar = st.progress(0) status_text = st.empty() total_urls = len(urls) existing_pdfs = get_pdf_files() for idx, url in enumerate(urls): if url: output_path = pdf_url_to_filename(url) status_text.text(f"Fetching {idx + 1}/{total_urls}: {os.path.basename(output_path)}...") if output_path not in existing_pdfs: if download_pdf(url, output_path): st.session_state['downloaded_pdfs'][url] = output_path logger.info(f"Downloaded PDF from {url} to {output_path}") entry = f"Downloaded PDF: {output_path}" st.session_state['history'].append(entry) st.session_state['asset_checkboxes'][output_path] = True else: st.error(f"Failed to nab {url} πΏ") else: st.info(f"Already got {os.path.basename(output_path)}! Skipping... πΎ") st.session_state['downloaded_pdfs'][url] = output_path progress_bar.progress((idx + 1) / total_urls) status_text.text("Robo-Download complete! π") mode = st.selectbox("Snapshot Mode", ["Single Page (High-Res)", "Two Pages (High-Res)", "All Pages (High-Res)"], key="download_mode") if st.button("Snapshot Selected πΈ"): selected_pdfs = [path for path in get_gallery_files() if path.endswith('.pdf') and st.session_state['asset_checkboxes'].get(path, False)] if selected_pdfs: for pdf_path in selected_pdfs: if not os.path.exists(pdf_path): st.warning(f"File not found: {pdf_path}. Skipping.") continue mode_key = {"Single Page (High-Res)": "single", "Two Pages (High-Res)": "twopage", "All Pages (High-Res)": "allpages"}[mode] snapshots = asyncio.run(process_pdf_snapshot(pdf_path, mode_key)) for snapshot in snapshots: st.image(Image.open(snapshot), caption=snapshot, use_container_width=True) st.session_state['asset_checkboxes'][snapshot] = True else: st.warning("No PDFs selected for snapshotting! Check some boxes in the sidebar.") with tab_ocr: st.header("Test OCR π") all_files = get_gallery_files() if all_files: if st.button("OCR All Assets π"): full_text = "# OCR Results (GPT-4o)\n\n" for file in all_files: if file.endswith('.png'): image = Image.open(file) else: doc = fitz.open(file) pix = doc[0].get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) doc.close() output_file = generate_filename(f"ocr_{os.path.basename(file)}", "txt") result = asyncio.run(process_gpt4o_ocr(image, output_file)) full_text += f"## {os.path.basename(file)}\n\n{result}\n\n" entry = f"OCR Test: {file} -> {output_file}" st.session_state['history'].append(entry) md_output_file = f"full_ocr_{int(time.time())}.md" with open(md_output_file, "w") as f: f.write(full_text) st.success(f"Full OCR saved to {md_output_file}") st.markdown(get_download_link(md_output_file, "text/markdown", "Download Full OCR Markdown"), unsafe_allow_html=True) selected_file = st.selectbox("Select Image or PDF", all_files, key="ocr_select") if selected_file: if selected_file.endswith('.png'): image = Image.open(selected_file) else: doc = fitz.open(selected_file) pix = doc[0].get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) doc.close() st.image(image, caption="Input Image", use_container_width=True) if st.button("Run OCR π", key="ocr_run"): output_file = generate_filename("ocr_output", "md") st.session_state['processing']['ocr'] = True result = asyncio.run(process_gpt4o_ocr(image, output_file)) entry = f"OCR Test: {selected_file} -> {output_file}" st.session_state['history'].append(entry) #st.text_area("OCR Result", result, height=200, key="ocr_result") st.code(result, language="python", line_numbers=True, wrap_lines=True, height=200) # 03312025 5:55AM if len(result) > 50: open(output_file, "w").write(result); st.success(f"OCR output saved to {output_file}") else: st.warning("OCR output too short; file not saved.") st.session_state['processing']['ocr'] = False if selected_file.endswith('.pdf') and st.button("OCR All Pages π", key="ocr_all_pages"): doc = fitz.open(selected_file) full_text = f"# OCR Results for {os.path.basename(selected_file)}\n\n" for i in range(len(doc)): pix = doc[i].get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) output_file = generate_filename(f"ocr_page_{i}", "txt") result = asyncio.run(process_gpt4o_ocr(image, output_file)) full_text += f"## Page {i + 1}\n\n{result}\n\n" entry = f"OCR Test: {selected_file} Page {i + 1} -> {output_file}" st.session_state['history'].append(entry) md_output_file = f"full_ocr_{os.path.basename(selected_file)}_{int(time.time())}.md" with open(md_output_file, "w") as f: f.write(full_text) st.success(f"Full OCR saved to {md_output_file}") st.markdown(get_download_link(md_output_file, "text/markdown", "Download Full OCR Markdown"), unsafe_allow_html=True) else: st.warning("No assets in gallery yet. Use Camera Snap or Download PDFs!") with tab_build: st.header("Build Titan π±") model_type = st.selectbox("Model Type", ["Causal LM", "Diffusion"], key="build_type") base_model = st.selectbox( "Select Tiny Model", ["HuggingFaceTB/SmolLM-135M", "Qwen/Qwen1.5-0.5B-Chat"] if model_type == "Causal LM" else ["OFA-Sys/small-stable-diffusion-v0", "stabilityai/stable-diffusion-2-base"] ) model_name = st.text_input("Model Name", f"tiny-titan-{int(time.time())}") domain = st.text_input("Target Domain", "general") if st.button("Download Model β¬οΈ"): config = (ModelConfig if model_type == "Causal LM" else DiffusionConfig)( name=model_name, base_model=base_model, size="small", domain=domain ) builder = ModelBuilder() if model_type == "Causal LM" else DiffusionBuilder() builder.load_model(base_model, config) builder.save_model(config.model_path) st.session_state['builder'] = builder st.session_state['model_loaded'] = True st.session_state['selected_model_type'] = model_type st.session_state['selected_model'] = config.model_path entry = f"Built {model_type} model: {model_name}" st.session_state['history'].append(entry) st.success(f"Model downloaded and saved to {config.model_path}! π") st.rerun() with tab_imggen: st.header("Test Image Gen π¨") all_files = get_gallery_files() if all_files: selected_file = st.selectbox("Select Image or PDF", all_files, key="gen_select") if selected_file: if selected_file.endswith('.png'): image = Image.open(selected_file) else: doc = fitz.open(selected_file) pix = doc[0].get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) doc.close() st.image(image, caption="Reference Image", use_container_width=True) prompt = st.text_area("Prompt", "Generate a neon superhero version of this image", key="gen_prompt") if st.button("Run Image Gen π", key="gen_run"): output_file = generate_filename("gen_output", "png") st.session_state['processing']['gen'] = True result = asyncio.run(process_image_gen(prompt, output_file)) entry = f"Image Gen Test: {prompt} -> {output_file}" st.session_state['history'].append(entry) st.image(result, caption="Generated Image", use_container_width=True) st.success(f"Image saved to {output_file}") st.session_state['processing']['gen'] = False else: st.warning("No images or PDFs in gallery yet. Use Camera Snap or Download PDFs!") with tab_pdf_process: st.header("PDF Process") st.subheader("Upload PDFs for GPT-based text extraction") gpt_models = ["gpt-4o", "gpt-4o-mini"] selected_gpt_model = st.selectbox("Select GPT Model", gpt_models, key="pdf_gpt_model") detail_level = st.selectbox("Detail Level", ["auto", "low", "high"], key="pdf_detail_level") uploaded_pdfs = st.file_uploader("Upload PDF files", type=["pdf"], accept_multiple_files=True, key="pdf_process_uploader") view_mode = st.selectbox("View Mode", ["Single Page", "Double Page"], key="pdf_view_mode") if st.button("Process Uploaded PDFs", key="process_pdfs"): combined_text = "" for pdf_file in uploaded_pdfs: pdf_bytes = pdf_file.read() temp_pdf_path = f"temp_{pdf_file.name}" with open(temp_pdf_path, "wb") as f: f.write(pdf_bytes) try: doc = fitz.open(temp_pdf_path) st.write(f"Processing {pdf_file.name} with {len(doc)} pages") if view_mode == "Single Page": for i, page in enumerate(doc): pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) st.image(img, caption=f"{pdf_file.name} Page {i+1}") gpt_text = process_image_with_prompt(img, "Extract the electronic text from image", model=selected_gpt_model, detail=detail_level) combined_text += f"\n## {pdf_file.name} - Page {i+1}\n\n{gpt_text}\n" else: pages = list(doc) for i in range(0, len(pages), 2): if i+1 < len(pages): pix1 = pages[i].get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) img1 = Image.frombytes("RGB", [pix1.width, pix1.height], pix1.samples) pix2 = pages[i+1].get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) img2 = Image.frombytes("RGB", [pix2.width, pix2.height], pix2.samples) total_width = img1.width + img2.width max_height = max(img1.height, img2.height) combined_img = Image.new("RGB", (total_width, max_height)) combined_img.paste(img1, (0, 0)) combined_img.paste(img2, (img1.width, 0)) st.image(combined_img, caption=f"{pdf_file.name} Pages {i+1}-{i+2}") gpt_text = process_image_with_prompt(combined_img, "Extract the electronic text from image", model=selected_gpt_model, detail=detail_level) combined_text += f"\n## {pdf_file.name} - Pages {i+1}-{i+2}\n\n{gpt_text}\n" else: pix = pages[i].get_pixmap(matrix=fitz.Matrix(2.0, 2.0)) img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) st.image(img, caption=f"{pdf_file.name} Page {i+1}") gpt_text = process_image_with_prompt(img, "Extract the electronic text from image", model=selected_gpt_model, detail=detail_level) combined_text += f"\n## {pdf_file.name} - Page {i+1}\n\n{gpt_text}\n" doc.close() except Exception as e: st.error(f"Error processing {pdf_file.name}: {str(e)}") finally: os.remove(temp_pdf_path) output_filename = generate_filename("processed_pdf", "md") with open(output_filename, "w", encoding="utf-8") as f: f.write(combined_text) st.success(f"PDF processing complete. MD file saved as {output_filename}") st.markdown(get_download_link(output_filename, "text/markdown", "Download Processed PDF MD"), unsafe_allow_html=True) with tab_image_process: st.header("Image Process") st.subheader("Upload Images for GPT-based OCR") gpt_models = ["gpt-4o", "gpt-4o-mini"] selected_gpt_model = st.selectbox("Select GPT Model", gpt_models, key="img_gpt_model") detail_level = st.selectbox("Detail Level", ["auto", "low", "high"], key="img_detail_level") prompt_img = st.text_input("Enter prompt for image processing", "Extract the electronic text from image", key="img_process_prompt") uploaded_images = st.file_uploader("Upload image files", type=["png", "jpg", "jpeg"], accept_multiple_files=True, key="image_process_uploader") if st.button("Process Uploaded Images", key="process_images"): combined_text = "" for img_file in uploaded_images: try: img = Image.open(img_file) st.image(img, caption=img_file.name) gpt_text = process_image_with_prompt(img, prompt_img, model=selected_gpt_model, detail=detail_level) combined_text += f"\n## {img_file.name}\n\n{gpt_text}\n" except Exception as e: st.error(f"Error processing image {img_file.name}: {str(e)}") output_filename = generate_filename("processed_image", "md") with open(output_filename, "w", encoding="utf-8") as f: f.write(combined_text) st.success(f"Image processing complete. MD file saved as {output_filename}") st.markdown(get_download_link(output_filename, "text/markdown", "Download Processed Image MD"), unsafe_allow_html=True) with tab_md_gallery: st.header("MD Gallery and GPT Processing") gpt_models = ["gpt-4o", "gpt-4o-mini"] selected_gpt_model = st.selectbox("Select GPT Model", gpt_models, key="md_gpt_model") md_files = sorted(glob.glob("*.md")) if md_files: st.subheader("Individual File Processing") cols = st.columns(2) for idx, md_file in enumerate(md_files): with cols[idx % 2]: st.write(md_file) if st.button(f"Process {md_file}", key=f"process_md_{md_file}"): try: with open(md_file, "r", encoding="utf-8") as f: content = f.read() prompt_md = "Summarize this into markdown outline with emojis and number the topics 1..12" result_text = process_text_with_prompt(content, prompt_md, model=selected_gpt_model) st.markdown(result_text) output_filename = generate_filename(f"processed_{os.path.splitext(md_file)[0]}", "md") with open(output_filename, "w", encoding="utf-8") as f: f.write(result_text) st.markdown(get_download_link(output_filename, "text/markdown", f"Download {output_filename}"), unsafe_allow_html=True) except Exception as e: st.error(f"Error processing {md_file}: {str(e)}") st.subheader("Knowledge Processing") st.write("Select MD files to combine and process:") selected_md = {} for md_file in md_files: selected_md[md_file] = st.checkbox(md_file, key=f"checkbox_md_{md_file}") Knowledge_prompt = st.text_input("1. Change into a numbered markdown outline with emojis. 2. Number the topics and make sure to include all data.", key="Knowledge_prompt") if st.button("Process Selected MD Files", key="process_Knowledge_md"): combined_content = "" for md_file, selected in selected_md.items(): if selected: try: with open(md_file, "r", encoding="utf-8") as f: combined_content += f"\n## {md_file}\n" + f.read() + "\n" except Exception as e: st.error(f"Error reading {md_file}: {str(e)}") if combined_content: result_text = process_text_with_prompt(combined_content, Knowledge_prompt, model=selected_gpt_model) st.markdown(result_text) output_filename = generate_filename("Knowledge_processed_md", "md") with open(output_filename, "w", encoding="utf-8") as f: f.write(result_text) st.success(f"Knowledge processing complete. MD file saved as {output_filename}") st.markdown(get_download_link(output_filename, "text/markdown", "Download Knowledge Processed MD"), unsafe_allow_html=True) else: st.warning("No MD files selected.") else: st.warning("No MD files found.") with tab_character_editor: st.header("Character Editor π§βπ¨") st.subheader("Create Your Character") with st.form(key="character_form"): if st.session_state.get('char_form_reset', False): default_name, default_gender, default_intro, default_greeting = randomize_character_content() st.session_state['char_form_reset'] = False else: default_name, default_gender, default_intro, default_greeting = randomize_character_content() if st.form_submit_button("Randomize Content π²"): default_name, default_gender, default_intro, default_greeting = randomize_character_content() name = st.text_input("Name (3-25 characters, letters, numbers, underscore, hyphen, space only)", value=default_name, max_chars=25, key="char_name") gender = st.radio("Gender", ["Male", "Female"], index=["Male", "Female"].index(default_gender), key="char_gender") intro = st.text_area("Intro (Publicly seen)", value=default_intro, max_chars=300, key="char_intro") greeting = st.text_area("Greeting", value=default_greeting, max_chars=300, key="char_greeting") if st.form_submit_button("Create Character"): if not name or len(name) < 3: st.error("Name must be 3-25 characters long.") elif not re.match(r'^[a-zA-Z0-9 _-]+$', name): st.error("Name can only contain letters, numbers, underscores, hyphens, and spaces.") elif not intro or not greeting: st.error("Intro and Greeting cannot be empty.") else: character_data = { "name": name, "gender": gender, "intro": intro, "greeting": greeting, "created_at": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "tags": ["OC"] } save_character(character_data) st.success(f"Character '{name}' created successfully!") st.session_state['char_form_reset'] = True st.rerun() with tab_character_gallery: st.header("Character Gallery πΌοΈ") load_characters() characters = st.session_state.get('characters', []) if characters: st.subheader("Your Characters") cols = st.columns(3) for idx, char in enumerate(characters): with cols[idx % 3]: st.markdown(f"**{char['name']}**") st.write(f"**Gender**: {char['gender']}") st.write(f"**Intro**: {char['intro']}") st.write(f"**Greeting**: {char['greeting']}") st.write(f"**Created**: {char['created_at']}") st.write(f"**Tags**: {', '.join(char['tags'])}") if st.button(f"Delete {char['name']}", key=f"delete_char_{idx}"): characters.pop(idx) st.session_state['characters'] = characters with open("characters.json", "w") as f: json.dump(characters, f) st.rerun() st.markdown("---") else: st.warning("No characters created yet. Use the Character Editor to create one!") def update_gallery(): container = st.session_state['asset_gallery_container'] container.empty() all_files = get_gallery_files() if all_files: container.markdown("### Asset Gallery πΈπ") cols = container.columns(2) for idx, file in enumerate(all_files[:st.session_state['gallery_size']]): with cols[idx % 2]: st.session_state['unique_counter'] += 1 unique_id = st.session_state['unique_counter'] if file.endswith('.png'): st.image(Image.open(file), caption=os.path.basename(file), use_container_width=True) else: doc = fitz.open(file) pix = doc[0].get_pixmap(matrix=fitz.Matrix(0.5, 0.5)) img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) st.image(img, caption=os.path.basename(file), use_container_width=True) doc.close() checkbox_key = f"asset_{file}_{unique_id}" st.session_state['asset_checkboxes'][file] = st.checkbox("Use for SFT/Input", value=st.session_state['asset_checkboxes'].get(file, False), key=checkbox_key) mime_type = "image/png" if file.endswith('.png') else "application/pdf" st.markdown(get_download_link(file, mime_type, "Snag It! π₯"), unsafe_allow_html=True) if st.button("Zap It! ποΈ", key=f"delete_{file}_{unique_id}"): os.remove(file) st.session_state['asset_checkboxes'].pop(file, None) st.success(f"Asset {os.path.basename(file)} vaporized! π¨") st.rerun() update_gallery() st.sidebar.subheader("Action Logs π") for record in log_records: st.sidebar.write(f"{record.asctime} - {record.levelname} - {record.message}") st.sidebar.subheader("History π") for entry in st.session_state.get("history", []): if entry is not None: st.sidebar.write(entry)