Structura-VL / app.py
AliHamza852's picture
Update app.py
131244d verified
Raw
History Blame Contribute Delete
4.53 kB
import os
import torch
import gradio as gr
from PIL import Image
from peft import PeftModel
from qwen_vl_utils import process_vision_info
from transformers import Qwen2VLForConditionalGeneration, AutoProcessor
# --- Professional "Cyber-Dark" CSS ---
custom_css = """
.gradio-container {background-color: #050505; color: #e5e7eb;}
.feedback-card {border: 1px solid #6366f1; padding: 20px; border-radius: 12px; background: #0f172a; margin: 10px 0;}
#header-text {text-align: center; background: linear-gradient(to right, #818cf8, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-size: 2.5rem; font-weight: 800;}
button.primary {background: linear-gradient(90deg, #6366f1, #a855f7) !important; color: white !important; font-weight: bold !important; border: none !important; transition: 0.3s;}
button.primary:hover {transform: scale(1.02); opacity: 0.9;}
.tabs {border: none !important;}
footer {visibility: hidden}
"""
# --- Path Configuration ---
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
MODEL_ID = "Qwen/Qwen2-VL-2B-Instruct"
# Files are in the root directory alongside app.py
LORA_PATH = CURRENT_DIR
# --- Model Loading ---
print(f"Initializing Base Model: {MODEL_ID}")
base_model = Qwen2VLForConditionalGeneration.from_pretrained(
MODEL_ID,
torch_dtype=torch.float16,
device_map="auto"
)
print(f"Applying LoRA Adapters from: {LORA_PATH}")
model = PeftModel.from_pretrained(base_model, LORA_PATH)
processor = AutoProcessor.from_pretrained(LORA_PATH)
model.eval()
# --- Inference Logic ---
def analyze_document(input_img, custom_prompt):
if input_img is None:
return "Error: No image provided. Please upload a document."
if not custom_prompt:
custom_prompt = "Convert this document image into structured Markdown."
try:
# Prepare content for Qwen2-VL
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": input_img},
{"type": "text", "text": custom_prompt},
],
}
]
# 1. Apply Template
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
# 2. Extract Vision Info
image_inputs, video_inputs = process_vision_info(messages)
# 3. Process Inputs (Force float16 to match model)
inputs = processor(
text=[text],
images=image_inputs,
videos=video_inputs,
padding=True,
return_tensors="pt"
).to(model.device).to(torch.float16)
# 4. Generate
with torch.no_grad():
generated_ids = model.generate(
**inputs,
max_new_tokens=1024,
do_sample=False # Keep it deterministic for document tasks
)
# Trim the prompt tokens
generated_ids_trimmed = [
out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_text = processor.batch_decode(
generated_ids_trimmed,
skip_special_tokens=True,
clean_up_tokenization_spaces=False
)
return output_text[0]
except Exception as e:
return f"System Error: {str(e)}"
# --- UI Layout ---
with gr.Blocks(css=custom_css, theme=gr.themes.Monochrome()) as demo:
gr.Markdown("# 🔮 STRUCTURA-VL: DOCUMENT INTELLIGENCE", elem_id="header-text")
with gr.Row():
with gr.Column(scale=1, variant="panel"):
img_input = gr.Image(type="pil", label="Input Document")
prompt_input = gr.Textbox(
label="System Prompt",
value="Convert this document image into structured Markdown.",
lines=2
)
submit_btn = gr.Button("✨ EXTRACT MARKDOWN", variant="primary")
with gr.Column(scale=2):
md_output = gr.Markdown(label="Rendered Result")
with gr.Accordion("Raw Markdown Source", open=False):
raw_output = gr.Code(label="Code", language="markdown")
# Map Events
submit_btn.click(
fn=analyze_document,
inputs=[img_input, prompt_input],
outputs=[md_output]
).then(
fn=lambda x: x,
inputs=[md_output],
outputs=[raw_output]
)
if __name__ == "__main__":
demo.launch()