Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,17 @@
|
|
| 1 |
# Copyright: Shayekh Bin Islam. KAIST, South Korea. 2026.
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import gradio as gr
|
| 4 |
import fitz # PyMuPDF
|
| 5 |
from PIL import Image
|
|
@@ -8,13 +20,12 @@ import json
|
|
| 8 |
import base64
|
| 9 |
import soundfile as sf
|
| 10 |
import torch
|
| 11 |
-
import spaces
|
| 12 |
|
| 13 |
from supertonic import TTS
|
| 14 |
-
from
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
tts = None
|
| 19 |
voice_style = None
|
| 20 |
|
|
@@ -41,18 +52,26 @@ def get_base64_image(image):
|
|
| 41 |
|
| 42 |
@spaces.GPU
|
| 43 |
def extract_vocabulary(pdf_text, images, translit_lang, translit_format, target_lang):
|
| 44 |
-
"""Use
|
| 45 |
-
global
|
| 46 |
|
| 47 |
os.makedirs("log", exist_ok=True)
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
prompt_text = f"""Extract 3 to 5 key Korean words or phrases from the following text and images.
|
| 50 |
Return ONLY a valid JSON list of dictionaries, where each dictionary has four keys:
|
| 51 |
- 'korean' (the Korean text)
|
| 52 |
-
- 'transliteration' (the pronunciation transliterated into {translit_lang.upper()} script/characters, formatted as {translit_format}.
|
| 53 |
- 'translation' (the translation into {target_lang.upper()})
|
| 54 |
- 'explanation' (a brief grammar or context note in {target_lang.upper()}).
|
| 55 |
-
No markdown formatting, just raw JSON.
|
| 56 |
|
| 57 |
Text:
|
| 58 |
{pdf_text[:1500]}
|
|
@@ -62,16 +81,19 @@ Text:
|
|
| 62 |
with open("log/debug_vlm_prompt.txt", "w", encoding="utf-8") as f:
|
| 63 |
f.write(prompt_text)
|
| 64 |
|
| 65 |
-
content = [
|
|
|
|
| 66 |
|
| 67 |
for i, img in enumerate(images):
|
| 68 |
# DEBUG: Log images
|
| 69 |
img.save(f"log/debug_image_{i}.png", format="PNG")
|
|
|
|
| 70 |
|
| 71 |
content.append({
|
| 72 |
-
"type": "
|
| 73 |
-
"image_url": {"url": get_base64_image(img)}
|
| 74 |
})
|
|
|
|
|
|
|
| 75 |
|
| 76 |
messages = [
|
| 77 |
{
|
|
@@ -81,26 +103,46 @@ Text:
|
|
| 81 |
]
|
| 82 |
|
| 83 |
try:
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
# DEBUG: Log raw output text
|
| 88 |
with open("log/debug_vlm_output.txt", "w", encoding="utf-8") as f:
|
| 89 |
f.write(output_text)
|
| 90 |
|
| 91 |
except Exception as e:
|
| 92 |
-
print(f"Error during
|
| 93 |
return []
|
| 94 |
|
| 95 |
try:
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
if
|
| 100 |
-
clean_text =
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
|
|
|
| 104 |
|
| 105 |
data = json.loads(clean_text)
|
| 106 |
if not isinstance(data, list):
|
|
@@ -458,26 +500,27 @@ def create_demo():
|
|
| 458 |
|
| 459 |
@spaces.GPU
|
| 460 |
def main():
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
|
|
|
| 481 |
)
|
| 482 |
|
| 483 |
print("Loading Supertonic TTS...")
|
|
@@ -488,8 +531,8 @@ def main():
|
|
| 488 |
voice_style = tts.get_voice_style(tts.voice_style_names[0])
|
| 489 |
|
| 490 |
demo = create_demo()
|
| 491 |
-
demo.launch(server_name="0.0.0.0", server_port=
|
| 492 |
-
|
| 493 |
|
| 494 |
if __name__ == "__main__":
|
| 495 |
main()
|
|
|
|
| 1 |
# Copyright: Shayekh Bin Islam. KAIST, South Korea. 2026.
|
| 2 |
|
| 3 |
+
try:
|
| 4 |
+
import spaces
|
| 5 |
+
except ImportError:
|
| 6 |
+
class spaces:
|
| 7 |
+
@staticmethod
|
| 8 |
+
def GPU(*args, **kwargs):
|
| 9 |
+
def decorator(func):
|
| 10 |
+
return func
|
| 11 |
+
if len(args) == 1 and callable(args[0]) and not kwargs:
|
| 12 |
+
return args[0]
|
| 13 |
+
return decorator
|
| 14 |
+
|
| 15 |
import gradio as gr
|
| 16 |
import fitz # PyMuPDF
|
| 17 |
from PIL import Image
|
|
|
|
| 20 |
import base64
|
| 21 |
import soundfile as sf
|
| 22 |
import torch
|
|
|
|
| 23 |
|
| 24 |
from supertonic import TTS
|
| 25 |
+
from transformers import AutoProcessor, AutoModelForImageTextToText
|
| 26 |
|
| 27 |
+
model = None
|
| 28 |
+
processor = None
|
| 29 |
tts = None
|
| 30 |
voice_style = None
|
| 31 |
|
|
|
|
| 52 |
|
| 53 |
@spaces.GPU
|
| 54 |
def extract_vocabulary(pdf_text, images, translit_lang, translit_format, target_lang):
|
| 55 |
+
"""Use Transformers to extract vocabulary from text and images."""
|
| 56 |
+
global model, processor
|
| 57 |
|
| 58 |
os.makedirs("log", exist_ok=True)
|
| 59 |
|
| 60 |
+
if pdf_text.strip() == "":
|
| 61 |
+
pdf_text = '''"No Text available, see provided images only."'''
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
non_english = ""
|
| 65 |
+
if translit_lang.upper() != "ENGLISH":
|
| 66 |
+
non_english = f" CRITICAL: You MUST use the native alphabet/script of {translit_lang.upper()}, do NOT use English letters unless requested."
|
| 67 |
+
|
| 68 |
prompt_text = f"""Extract 3 to 5 key Korean words or phrases from the following text and images.
|
| 69 |
Return ONLY a valid JSON list of dictionaries, where each dictionary has four keys:
|
| 70 |
- 'korean' (the Korean text)
|
| 71 |
+
- 'transliteration' (the pronunciation transliterated into {translit_lang.upper()} script/characters, formatted as {translit_format}.{non_english})
|
| 72 |
- 'translation' (the translation into {target_lang.upper()})
|
| 73 |
- 'explanation' (a brief grammar or context note in {target_lang.upper()}).
|
| 74 |
+
No markdown formatting, just raw JSON with ```json and ``` markers.
|
| 75 |
|
| 76 |
Text:
|
| 77 |
{pdf_text[:1500]}
|
|
|
|
| 81 |
with open("log/debug_vlm_prompt.txt", "w", encoding="utf-8") as f:
|
| 82 |
f.write(prompt_text)
|
| 83 |
|
| 84 |
+
content = []
|
| 85 |
+
pil_images = []
|
| 86 |
|
| 87 |
for i, img in enumerate(images):
|
| 88 |
# DEBUG: Log images
|
| 89 |
img.save(f"log/debug_image_{i}.png", format="PNG")
|
| 90 |
+
pil_images.append(img)
|
| 91 |
|
| 92 |
content.append({
|
| 93 |
+
"type": "image",
|
|
|
|
| 94 |
})
|
| 95 |
+
|
| 96 |
+
content += [{"type": "text", "text": prompt_text}]
|
| 97 |
|
| 98 |
messages = [
|
| 99 |
{
|
|
|
|
| 103 |
]
|
| 104 |
|
| 105 |
try:
|
| 106 |
+
model.to("cuda")
|
| 107 |
+
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
| 108 |
+
inputs = processor(
|
| 109 |
+
text=[text],
|
| 110 |
+
images=pil_images if pil_images else None,
|
| 111 |
+
return_tensors="pt",
|
| 112 |
+
padding=True
|
| 113 |
+
).to("cuda")
|
| 114 |
+
|
| 115 |
+
generated_ids = model.generate(
|
| 116 |
+
**inputs,
|
| 117 |
+
max_new_tokens=2048*4,
|
| 118 |
+
temperature=1.0,
|
| 119 |
+
top_p=0.95,
|
| 120 |
+
do_sample=True
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
generated_ids = [
|
| 124 |
+
output_ids[len(input_ids):] for input_ids, output_ids in zip(inputs.input_ids, generated_ids)
|
| 125 |
+
]
|
| 126 |
+
output_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
|
| 127 |
|
| 128 |
# DEBUG: Log raw output text
|
| 129 |
with open("log/debug_vlm_output.txt", "w", encoding="utf-8") as f:
|
| 130 |
f.write(output_text)
|
| 131 |
|
| 132 |
except Exception as e:
|
| 133 |
+
print(f"Error during Transformers inference: {e}")
|
| 134 |
return []
|
| 135 |
|
| 136 |
try:
|
| 137 |
+
import re
|
| 138 |
+
# Extract JSON from markdown code fences or raw output
|
| 139 |
+
json_match = re.search(r'```(?:json)?\s*([\s\S]*?)```', output_text)
|
| 140 |
+
if json_match:
|
| 141 |
+
clean_text = json_match.group(1).strip()
|
| 142 |
+
else:
|
| 143 |
+
# Fallback: find first [ ... ] or { ... } block
|
| 144 |
+
json_match = re.search(r'(\[[\s\S]*\]|\{[\s\S]*\})', output_text)
|
| 145 |
+
clean_text = json_match.group(1).strip() if json_match else output_text.strip()
|
| 146 |
|
| 147 |
data = json.loads(clean_text)
|
| 148 |
if not isinstance(data, list):
|
|
|
|
| 500 |
|
| 501 |
@spaces.GPU
|
| 502 |
def main():
|
| 503 |
+
global model, processor, tts, voice_style
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
# model_id = "Qwen/Qwen3.5-9B"
|
| 507 |
+
model_id = "Qwen/Qwen3.5-2B"
|
| 508 |
+
|
| 509 |
+
print(f"Loading {model_id} model via Transformers...")
|
| 510 |
+
|
| 511 |
+
processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
|
| 512 |
+
|
| 513 |
+
try:
|
| 514 |
+
with open("chat_template.jinja", "r", encoding="utf-8") as f:
|
| 515 |
+
processor.chat_template = f.read()
|
| 516 |
+
except Exception as e:
|
| 517 |
+
print("Could not load custom chat template:", e)
|
| 518 |
+
|
| 519 |
+
model = AutoModelForImageTextToText.from_pretrained(
|
| 520 |
+
model_id,
|
| 521 |
+
torch_dtype=torch.bfloat16,
|
| 522 |
+
device_map="cpu",
|
| 523 |
+
trust_remote_code=True
|
| 524 |
)
|
| 525 |
|
| 526 |
print("Loading Supertonic TTS...")
|
|
|
|
| 531 |
voice_style = tts.get_voice_style(tts.voice_style_names[0])
|
| 532 |
|
| 533 |
demo = create_demo()
|
| 534 |
+
demo.launch(server_name="0.0.0.0", server_port=7865)
|
| 535 |
+
|
| 536 |
|
| 537 |
if __name__ == "__main__":
|
| 538 |
main()
|