Update app.py
Browse files
app.py
CHANGED
|
@@ -29,14 +29,13 @@ else:
|
|
| 29 |
|
| 30 |
os.environ['PLAYWRIGHT_BROWSERS_PATH'] = "/home/user/huggingface/ms-playwright"
|
| 31 |
# os.system("playwright install chromium")
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
subprocess.run(["playwright", "install"])
|
| 40 |
|
| 41 |
import gradio as gr
|
| 42 |
import fitz # PyMuPDF
|
|
@@ -56,6 +55,20 @@ from transformers import AutoProcessor, AutoModelForImageTextToText
|
|
| 56 |
# tts = None
|
| 57 |
# voice_style = None
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
def extract_pdf_content(pdf_path, max_pages=2):
|
| 60 |
"""Extract text and images from up to max_pages of a PDF."""
|
| 61 |
doc = fitz.open(pdf_path)
|
|
@@ -203,9 +216,20 @@ Text:
|
|
| 203 |
padding=True
|
| 204 |
).to("cuda")
|
| 205 |
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
from threading import Thread
|
| 208 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
streamer = TextIteratorStreamer(processor.tokenizer, skip_prompt=True, skip_special_tokens=True)
|
| 210 |
generation_kwargs = dict(
|
| 211 |
**inputs,
|
|
@@ -213,6 +237,7 @@ Text:
|
|
| 213 |
max_new_tokens=2048*16,
|
| 214 |
do_sample=True,
|
| 215 |
repetition_penalty=repetition_penalty_val,
|
|
|
|
| 216 |
)
|
| 217 |
|
| 218 |
if len(images) > 0:
|
|
@@ -220,7 +245,22 @@ Text:
|
|
| 220 |
else:
|
| 221 |
generation_kwargs.update(dict(temperature=1.0, top_p=0.95, top_k=20, min_p=0.0))
|
| 222 |
|
| 223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
thread.start()
|
| 225 |
|
| 226 |
output_text = ""
|
|
@@ -228,6 +268,85 @@ Text:
|
|
| 228 |
output_text += new_text
|
| 229 |
yield output_text, None
|
| 230 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
# DEBUG: Log raw output text
|
| 232 |
with open("log/debug_vlm_output.txt", "w", encoding="utf-8") as f:
|
| 233 |
f.write(output_text)
|
|
@@ -890,7 +1009,7 @@ def create_demo():
|
|
| 890 |
with gr.Column(scale=1):
|
| 891 |
# url_input = gr.Textbox(label="Enter a Website URL π", placeholder=r"e.g. https://storykorean.com/stories?level=beginner&story=tiger", value=r"https://storykorean.com/stories?level=beginner&story=tiger")
|
| 892 |
# https://www.bbc.com/korean/articles/c5yz89k5dw0o
|
| 893 |
-
url_input = gr.Textbox(label="Enter a Website URL π", placeholder=r"e.g. https://www.
|
| 894 |
|
| 895 |
pdf_input = gr.File(label="Or Upload Book PDF π", file_types=[".pdf"], value=example_pdf)
|
| 896 |
|
|
@@ -911,6 +1030,7 @@ def create_demo():
|
|
| 911 |
|
| 912 |
with gr.Row():
|
| 913 |
submit_btn = gr.Button("β¨ Generate Flashcards β¨", variant="primary")
|
|
|
|
| 914 |
stop_btn = gr.Button("π Stop Generation", variant="stop")
|
| 915 |
|
| 916 |
with gr.Column(scale=2):
|
|
@@ -924,13 +1044,19 @@ def create_demo():
|
|
| 924 |
last_source_state = gr.State(None)
|
| 925 |
last_korean_words_state = gr.State(None)
|
| 926 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 927 |
generate_event = submit_btn.click(
|
| 928 |
fn=process_pdf,
|
| 929 |
inputs=[pdf_input, url_input, translit_lang, translit_format, target_lang, max_text_char_input, repetition_penalty_input, last_source_state, last_korean_words_state],
|
| 930 |
outputs=[output_html, last_source_state, last_korean_words_state, stream_box, extracted_text_box, extracted_images_gallery]
|
| 931 |
)
|
| 932 |
|
| 933 |
-
|
|
|
|
| 934 |
|
| 935 |
# Force autoscroll using Custom JS
|
| 936 |
stream_box.change(
|
|
|
|
| 29 |
|
| 30 |
os.environ['PLAYWRIGHT_BROWSERS_PATH'] = "/home/user/huggingface/ms-playwright"
|
| 31 |
# os.system("playwright install chromium")
|
| 32 |
+
result = subprocess.run(
|
| 33 |
+
[sys.executable, "-m", "playwright", "install", "chromium"],
|
| 34 |
+
env={**os.environ},
|
| 35 |
+
check=True,
|
| 36 |
+
stdout=subprocess.PIPE,
|
| 37 |
+
stderr=subprocess.PIPE
|
| 38 |
+
)
|
|
|
|
| 39 |
|
| 40 |
import gradio as gr
|
| 41 |
import fitz # PyMuPDF
|
|
|
|
| 55 |
# tts = None
|
| 56 |
# voice_style = None
|
| 57 |
|
| 58 |
+
global_stop_thinking = [False]
|
| 59 |
+
global_kill_threads = [False]
|
| 60 |
+
|
| 61 |
+
def set_stop_thinking():
|
| 62 |
+
global_stop_thinking[0] = True
|
| 63 |
+
print(f"[STOP-THINK] set_stop_thinking CALLED! Flag is now: {global_stop_thinking[0]}")
|
| 64 |
+
return gr.update(value="β‘ Forcing generation...")
|
| 65 |
+
|
| 66 |
+
def set_kill_threads():
|
| 67 |
+
global_kill_threads[0] = True
|
| 68 |
+
print(f"[STOP-THINK] set_kill_threads CALLED! Flag is now: {global_kill_threads[0]}")
|
| 69 |
+
return gr.update(value="π Stopping...")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
def extract_pdf_content(pdf_path, max_pages=2):
|
| 73 |
"""Extract text and images from up to max_pages of a PDF."""
|
| 74 |
doc = fitz.open(pdf_path)
|
|
|
|
| 216 |
padding=True
|
| 217 |
).to("cuda")
|
| 218 |
|
| 219 |
+
global_stop_thinking[0] = False
|
| 220 |
+
global_kill_threads[0] = False
|
| 221 |
+
print(f"[STOP-THINK] Flags RESET. stop_thinking={global_stop_thinking[0]}, kill={global_kill_threads[0]}")
|
| 222 |
+
|
| 223 |
+
from transformers import TextIteratorStreamer, StoppingCriteria, StoppingCriteriaList
|
| 224 |
from threading import Thread
|
| 225 |
|
| 226 |
+
class StopThinkingCriteria(StoppingCriteria):
|
| 227 |
+
def __call__(self, input_ids, scores, **kwargs):
|
| 228 |
+
val = global_stop_thinking[0] or global_kill_threads[0]
|
| 229 |
+
if val:
|
| 230 |
+
print(f"[STOP-THINK] Criteria returning True! stop={global_stop_thinking[0]} kill={global_kill_threads[0]}")
|
| 231 |
+
return val
|
| 232 |
+
|
| 233 |
streamer = TextIteratorStreamer(processor.tokenizer, skip_prompt=True, skip_special_tokens=True)
|
| 234 |
generation_kwargs = dict(
|
| 235 |
**inputs,
|
|
|
|
| 237 |
max_new_tokens=2048*16,
|
| 238 |
do_sample=True,
|
| 239 |
repetition_penalty=repetition_penalty_val,
|
| 240 |
+
stopping_criteria=StoppingCriteriaList([StopThinkingCriteria()])
|
| 241 |
)
|
| 242 |
|
| 243 |
if len(images) > 0:
|
|
|
|
| 245 |
else:
|
| 246 |
generation_kwargs.update(dict(temperature=1.0, top_p=0.95, top_k=20, min_p=0.0))
|
| 247 |
|
| 248 |
+
generation_result = []
|
| 249 |
+
def generate_and_capture(**kwargs):
|
| 250 |
+
try:
|
| 251 |
+
out = model.generate(**kwargs)
|
| 252 |
+
generation_result.append(out)
|
| 253 |
+
except Exception as e:
|
| 254 |
+
import traceback
|
| 255 |
+
print(f"\n[THREAD1 ERROR] model.generate crashed: {e}")
|
| 256 |
+
traceback.print_exc()
|
| 257 |
+
finally:
|
| 258 |
+
try:
|
| 259 |
+
streamer.end()
|
| 260 |
+
except Exception:
|
| 261 |
+
pass
|
| 262 |
+
|
| 263 |
+
thread = Thread(target=generate_and_capture, kwargs=generation_kwargs)
|
| 264 |
thread.start()
|
| 265 |
|
| 266 |
output_text = ""
|
|
|
|
| 268 |
output_text += new_text
|
| 269 |
yield output_text, None
|
| 270 |
|
| 271 |
+
thread.join()
|
| 272 |
+
|
| 273 |
+
if global_kill_threads[0]:
|
| 274 |
+
yield output_text + "\n\n[Generation completely stopped by user.]", None
|
| 275 |
+
return
|
| 276 |
+
|
| 277 |
+
if global_stop_thinking[0]:
|
| 278 |
+
global_stop_thinking[0] = False
|
| 279 |
+
print(f"[STOP-THINK] INJECTION PATH entered. Reset flag to: {global_stop_thinking[0]}")
|
| 280 |
+
|
| 281 |
+
# Inject the closure of thinking and start of JSON
|
| 282 |
+
injection_text = "\n</think>\n\n```json\n[\n"
|
| 283 |
+
output_text += injection_text
|
| 284 |
+
yield output_text, None
|
| 285 |
+
|
| 286 |
+
# Restart generation with updated context
|
| 287 |
+
generated_ids = generation_result[0]
|
| 288 |
+
injection_ids = processor.tokenizer(injection_text, return_tensors="pt", add_special_tokens=False).input_ids.to("cuda")
|
| 289 |
+
new_input_ids = torch.cat([generated_ids, injection_ids], dim=-1)
|
| 290 |
+
|
| 291 |
+
# Update attention mask
|
| 292 |
+
new_mask = torch.cat([
|
| 293 |
+
inputs["attention_mask"],
|
| 294 |
+
torch.ones((1, new_input_ids.shape[1] - inputs["attention_mask"].shape[1]), dtype=inputs["attention_mask"].dtype, device="cuda")
|
| 295 |
+
], dim=-1)
|
| 296 |
+
|
| 297 |
+
new_inputs = {
|
| 298 |
+
"input_ids": new_input_ids,
|
| 299 |
+
"attention_mask": new_mask
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
# Carry over only the visual features; discard stale keys like input_token_type or rope_deltas
|
| 303 |
+
keys_to_keep = ["pixel_values", "image_grid_thw", "pixel_values_videos", "video_grid_thw"]
|
| 304 |
+
for k in keys_to_keep:
|
| 305 |
+
if k in inputs:
|
| 306 |
+
new_inputs[k] = inputs[k]
|
| 307 |
+
|
| 308 |
+
class KillCriteria(StoppingCriteria):
|
| 309 |
+
def __call__(self, input_ids, scores, **kwargs):
|
| 310 |
+
return global_kill_threads[0]
|
| 311 |
+
|
| 312 |
+
new_streamer = TextIteratorStreamer(processor.tokenizer, skip_prompt=True, skip_special_tokens=True)
|
| 313 |
+
new_generation_kwargs = dict(
|
| 314 |
+
**new_inputs,
|
| 315 |
+
streamer=new_streamer,
|
| 316 |
+
max_new_tokens=2048*16,
|
| 317 |
+
do_sample=True,
|
| 318 |
+
repetition_penalty=repetition_penalty_val,
|
| 319 |
+
stopping_criteria=StoppingCriteriaList([KillCriteria()])
|
| 320 |
+
)
|
| 321 |
+
|
| 322 |
+
if len(images) > 0:
|
| 323 |
+
new_generation_kwargs.update(dict(temperature=0.6, top_p=0.95, top_k=20, min_p=0.0))
|
| 324 |
+
else:
|
| 325 |
+
new_generation_kwargs.update(dict(temperature=1.0, top_p=0.95, top_k=20, min_p=0.0))
|
| 326 |
+
|
| 327 |
+
def thread2_target(**kwargs):
|
| 328 |
+
try:
|
| 329 |
+
model.generate(**kwargs)
|
| 330 |
+
except Exception as e:
|
| 331 |
+
import traceback
|
| 332 |
+
print(f"\n[THREAD2 ERROR] model.generate crashed: {e}")
|
| 333 |
+
traceback.print_exc()
|
| 334 |
+
finally:
|
| 335 |
+
# Always unblock the streamer to prevent Gradio UI from hanging permanently
|
| 336 |
+
try:
|
| 337 |
+
new_streamer.end()
|
| 338 |
+
except Exception:
|
| 339 |
+
pass
|
| 340 |
+
|
| 341 |
+
thread2 = Thread(target=thread2_target, kwargs=new_generation_kwargs)
|
| 342 |
+
thread2.start()
|
| 343 |
+
|
| 344 |
+
for new_text in new_streamer:
|
| 345 |
+
output_text += new_text
|
| 346 |
+
yield output_text, None
|
| 347 |
+
|
| 348 |
+
thread2.join()
|
| 349 |
+
|
| 350 |
# DEBUG: Log raw output text
|
| 351 |
with open("log/debug_vlm_output.txt", "w", encoding="utf-8") as f:
|
| 352 |
f.write(output_text)
|
|
|
|
| 1009 |
with gr.Column(scale=1):
|
| 1010 |
# url_input = gr.Textbox(label="Enter a Website URL π", placeholder=r"e.g. https://storykorean.com/stories?level=beginner&story=tiger", value=r"https://storykorean.com/stories?level=beginner&story=tiger")
|
| 1011 |
# https://www.bbc.com/korean/articles/c5yz89k5dw0o
|
| 1012 |
+
url_input = gr.Textbox(label="Enter a Website URL π", placeholder=r"e.g. https://www.koreanstudyjunkie.com/post/korean-reading-exercise-for-all-levels-beginner-intermediate-advanced", value=r"https://www.koreanstudyjunkie.com/post/korean-reading-exercise-for-all-levels-beginner-intermediate-advanced")
|
| 1013 |
|
| 1014 |
pdf_input = gr.File(label="Or Upload Book PDF π", file_types=[".pdf"], value=example_pdf)
|
| 1015 |
|
|
|
|
| 1030 |
|
| 1031 |
with gr.Row():
|
| 1032 |
submit_btn = gr.Button("β¨ Generate Flashcards β¨", variant="primary")
|
| 1033 |
+
stop_thinking_btn = gr.Button("β‘ Stop thinking, Generate now", variant="secondary")
|
| 1034 |
stop_btn = gr.Button("π Stop Generation", variant="stop")
|
| 1035 |
|
| 1036 |
with gr.Column(scale=2):
|
|
|
|
| 1044 |
last_source_state = gr.State(None)
|
| 1045 |
last_korean_words_state = gr.State(None)
|
| 1046 |
|
| 1047 |
+
def reset_btn_text():
|
| 1048 |
+
return gr.update(value="β‘ Stop thinking, Generate now"), gr.update(value="π Stop Generation")
|
| 1049 |
+
|
| 1050 |
+
submit_btn.click(fn=reset_btn_text, inputs=None, outputs=[stop_thinking_btn, stop_btn], queue=False)
|
| 1051 |
+
|
| 1052 |
generate_event = submit_btn.click(
|
| 1053 |
fn=process_pdf,
|
| 1054 |
inputs=[pdf_input, url_input, translit_lang, translit_format, target_lang, max_text_char_input, repetition_penalty_input, last_source_state, last_korean_words_state],
|
| 1055 |
outputs=[output_html, last_source_state, last_korean_words_state, stream_box, extracted_text_box, extracted_images_gallery]
|
| 1056 |
)
|
| 1057 |
|
| 1058 |
+
stop_thinking_btn.click(fn=set_stop_thinking, inputs=None, outputs=stop_thinking_btn, queue=False)
|
| 1059 |
+
stop_btn.click(fn=set_kill_threads, inputs=None, outputs=stop_btn, queue=False).then(fn=None, inputs=None, outputs=None, cancels=[generate_event])
|
| 1060 |
|
| 1061 |
# Force autoscroll using Custom JS
|
| 1062 |
stream_box.change(
|