ssdataanalysis commited on
Commit
7b7de26
·
verified ·
1 Parent(s): 70c7c32

Patch: fix dependency/runtime fallbacks for OCR models

Browse files
Files changed (5) hide show
  1. __pycache__/app.cpython-314.pyc +0 -0
  2. app.py +203 -34
  3. apt.txt +1 -0
  4. model_registry.json +42 -0
  5. requirements.txt +3 -1
__pycache__/app.cpython-314.pyc CHANGED
Binary files a/__pycache__/app.cpython-314.pyc and b/__pycache__/app.cpython-314.pyc differ
 
app.py CHANGED
@@ -17,8 +17,18 @@ from PIL import Image
17
 
18
  try:
19
  from transformers import pipeline as hf_pipeline
 
 
 
 
 
 
20
  except Exception: # pragma: no cover
21
  hf_pipeline = None # pragma: no cover
 
 
 
 
22
 
23
  try:
24
  import torch
@@ -128,6 +138,7 @@ ZERO_GPU_MIN_GUARDED_FREE_GB = 1.0
128
  ZERO_GPU_MODEL_FALLBACK_GB_SAFE = 2.6
129
  ZERO_GPU_MODEL_FALLBACK_GB_MAX = 2.0
130
  DEFAULT_MAX_TOKENS = 4096
 
131
  LOCAL_PIPELINE_CACHE_MAX = 1
132
  TESSERACT_EXECUTABLE_PATHS = ("/usr/bin/tesseract", "/usr/local/bin/tesseract", "/opt/conda/bin/tesseract", "/bin/tesseract")
133
 
@@ -155,6 +166,16 @@ TRUST_REMOTE_CODE_MODELS = {
155
  TRUST_REMOTE_CODE_MODELS = {m.lower() for m in TRUST_REMOTE_CODE_MODELS}
156
 
157
  PIPELINE_TASK_HINTS = {
 
 
 
 
 
 
 
 
 
 
158
  "ronylicha/gigapdf-ocr-hebrew": "image-to-text",
159
  "liskcell/qunie-v7-mini": "image-to-text",
160
  "0cve0/openmlkitocr": "image-to-text",
@@ -180,7 +201,9 @@ GATED_MODELS_REQUIRING_TOKEN = {
180
  GATED_MODELS_REQUIRING_TOKEN = {m.lower() for m in GATED_MODELS_REQUIRING_TOKEN}
181
 
182
  _LOCAL_PIPELINE_CACHE: OrderedDict = OrderedDict()
 
183
  _LOCAL_PIPELINE_LOCK = threading.Lock()
 
184
 
185
 
186
  def load_registry() -> List[dict]:
@@ -377,6 +400,110 @@ def _build_local_pipeline(model_id: str, hf_token: str, task: str, trust_remote_
377
  raise RuntimeError(f"Failed to load local pipeline for {model_id}: {last_error}")
378
 
379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  def _get_local_pipeline(model_id: str, hf_token: str, task: str, trust_remote_code: bool = False):
381
  device = "gpu" if (torch is not None and torch.cuda.is_available()) else "cpu"
382
  key = _pipeline_cache_key(model_id, task or "auto", device, trust_remote_code)
@@ -402,6 +529,7 @@ def _get_local_pipeline(model_id: str, hf_token: str, task: str, trust_remote_co
402
  if torch is not None and torch.cuda.is_available():
403
  torch.cuda.empty_cache()
404
  gc.collect()
 
405
  return pipeline
406
 
407
 
@@ -425,6 +553,21 @@ def _clear_local_pipeline_cache() -> None:
425
  torch.cuda.empty_cache()
426
 
427
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  def run_local_model(entry: dict, image_bytes: bytes, prompt: str, hf_token: str) -> str:
429
  if hf_pipeline is None:
430
  raise RuntimeError("transformers is not installed in this space.")
@@ -434,7 +577,13 @@ def run_local_model(entry: dict, image_bytes: bytes, prompt: str, hf_token: str)
434
  model_id = entry["model_id"]
435
  task = _model_pipeline_task(entry)
436
  trust_remote_code = _model_requires_trust_remote_code(entry)
437
- pipeline = _get_local_pipeline(model_id, hf_token, task, trust_remote_code=trust_remote_code)
 
 
 
 
 
 
438
  params = sanitize_generation_params(entry.get("parameters", {}))
439
  image = _decode_image(image_bytes)
440
 
@@ -460,34 +609,53 @@ def run_local_model(entry: dict, image_bytes: bytes, prompt: str, hf_token: str)
460
  if isinstance(key, str) and key in supported_body_keys:
461
  params.setdefault(key, value)
462
 
463
- calls = []
464
- if prompt:
465
- calls.extend(
466
- [
467
- lambda: pipeline({"image": image, "text": prompt}, **params),
468
- lambda: pipeline({"image": image, "question": prompt}, **params),
469
- lambda: pipeline({"text": prompt, "image": image}, **params),
470
- lambda: pipeline({"images": image, "text": prompt}, **params),
471
- lambda: pipeline(image, text=prompt, **params),
472
- lambda: pipeline(image, question=prompt, **params),
473
- lambda: pipeline(image, **params),
474
- lambda: pipeline({"image": image}, **params),
475
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  )
477
- else:
478
- calls.extend([lambda: pipeline(image, **params), lambda: pipeline({"image": image}, **params)])
 
 
 
 
 
 
479
 
480
- last_error = None
481
- for call in calls:
482
- try:
483
- output = call()
484
- parsed = parse_output(output)
485
- if parsed:
486
- return parsed
487
- except Exception as exc: # pragma: no cover
488
- last_error = exc
489
- continue
490
- raise RuntimeError(f"Local model inference failed for {model_id}: {last_error}")
491
 
492
 
493
  def run_tesseract(image_bytes: bytes, params: dict) -> str:
@@ -838,15 +1006,16 @@ def run_comparison(
838
  continue
839
  futures_map[executor.submit(run_single_model, entry, image_bytes, "", hf_token)] = entry
840
 
841
- for future in as_completed(futures_map):
842
- result = future.result()
843
- if ground_truth_text:
844
- cer, wer = compute_cer_wer(ground_truth_text, result.output)
845
- result.cer = cer
846
- result.wer = wer
847
- results.append(result)
848
 
849
  _clear_local_pipeline_cache()
 
850
 
851
  order_map = {model["id"]: idx for idx, model in enumerate(models)}
852
  results.sort(key=lambda r: order_map.get(r.model_id, 1_000_000))
 
17
 
18
  try:
19
  from transformers import pipeline as hf_pipeline
20
+ from transformers import (
21
+ AutoModelForCausalLM,
22
+ AutoModelForImageTextToText,
23
+ AutoModelForVision2Seq,
24
+ AutoProcessor,
25
+ )
26
  except Exception: # pragma: no cover
27
  hf_pipeline = None # pragma: no cover
28
+ AutoModelForCausalLM = None # pragma: no cover
29
+ AutoModelForImageTextToText = None # pragma: no cover
30
+ AutoModelForVision2Seq = None # pragma: no cover
31
+ AutoProcessor = None # pragma: no cover
32
 
33
  try:
34
  import torch
 
138
  ZERO_GPU_MODEL_FALLBACK_GB_SAFE = 2.6
139
  ZERO_GPU_MODEL_FALLBACK_GB_MAX = 2.0
140
  DEFAULT_MAX_TOKENS = 4096
141
+ DIRECT_MODEL_CACHE_MAX = 1
142
  LOCAL_PIPELINE_CACHE_MAX = 1
143
  TESSERACT_EXECUTABLE_PATHS = ("/usr/bin/tesseract", "/usr/local/bin/tesseract", "/opt/conda/bin/tesseract", "/bin/tesseract")
144
 
 
166
  TRUST_REMOTE_CODE_MODELS = {m.lower() for m in TRUST_REMOTE_CODE_MODELS}
167
 
168
  PIPELINE_TASK_HINTS = {
169
+ "qwen/qwen3-vl-8b-instruct": "image-to-text",
170
+ "qwen/qwen3-vl-4b-instruct": "image-to-text",
171
+ "qwen/qwen3-vl-8b-thinking": "image-to-text",
172
+ "qwen/qwen3-vl-4b-thinking": "image-to-text",
173
+ "qwen/qwen3-vl-30b-a3b-instruct": "image-to-text",
174
+ "qwen/qwen3-vl-30b-a3b-thinking": "image-to-text",
175
+ "google/gemma-4-e4b-it": "image-to-text",
176
+ "google/gemma-4-12b-it": "image-to-text",
177
+ "google/gemma-4-26b-a4b-it": "image-to-text",
178
+ "google/gemma-4-31b-it": "image-to-text",
179
  "ronylicha/gigapdf-ocr-hebrew": "image-to-text",
180
  "liskcell/qunie-v7-mini": "image-to-text",
181
  "0cve0/openmlkitocr": "image-to-text",
 
201
  GATED_MODELS_REQUIRING_TOKEN = {m.lower() for m in GATED_MODELS_REQUIRING_TOKEN}
202
 
203
  _LOCAL_PIPELINE_CACHE: OrderedDict = OrderedDict()
204
+ _LOCAL_DIRECT_CACHE: OrderedDict = OrderedDict()
205
  _LOCAL_PIPELINE_LOCK = threading.Lock()
206
+ _LOCAL_DIRECT_LOCK = threading.Lock()
207
 
208
 
209
  def load_registry() -> List[dict]:
 
400
  raise RuntimeError(f"Failed to load local pipeline for {model_id}: {last_error}")
401
 
402
 
403
+ def _load_direct_components(model_id: str, hf_token: str, trust_remote_code: bool = False):
404
+ if AutoProcessor is None:
405
+ return None, None
406
+ token = _sanitize_hf_token(hf_token) or None
407
+ component = (model_id, trust_remote_code)
408
+ with _LOCAL_DIRECT_LOCK:
409
+ cached = _LOCAL_DIRECT_CACHE.get(component)
410
+ if cached is not None:
411
+ _LOCAL_DIRECT_CACHE.move_to_end(component)
412
+ return cached
413
+
414
+ processor = AutoProcessor.from_pretrained(model_id, token=token, trust_remote_code=trust_remote_code)
415
+
416
+ if torch is None:
417
+ raise RuntimeError("PyTorch is required for direct model loading.")
418
+
419
+ model_errors = []
420
+ model = None
421
+ for model_class in [AutoModelForImageTextToText, AutoModelForVision2Seq, AutoModelForCausalLM]:
422
+ if model_class is None:
423
+ continue
424
+ try:
425
+ model = model_class.from_pretrained(
426
+ model_id,
427
+ token=token,
428
+ trust_remote_code=trust_remote_code,
429
+ torch_dtype=getattr(torch, "float16", None),
430
+ )
431
+ break
432
+ except Exception as exc: # pragma: no cover
433
+ model_errors.append(f"{getattr(model_class, '__name__', str(model_class))}: {exc}")
434
+
435
+ if model is None:
436
+ raise RuntimeError(f"Direct loading failed for {model_id}. " + " | ".join(model_errors))
437
+
438
+ if torch.cuda.is_available():
439
+ model = model.to("cuda")
440
+
441
+ with _LOCAL_DIRECT_LOCK:
442
+ _LOCAL_DIRECT_CACHE[component] = (model, processor)
443
+ if len(_LOCAL_DIRECT_CACHE) > DIRECT_MODEL_CACHE_MAX:
444
+ _LOCAL_DIRECT_CACHE.popitem(last=False)
445
+ return model, processor
446
+
447
+
448
+ def _direct_infer(model_id: str, image_bytes: bytes, prompt: str, hf_token: str, params: dict, trust_remote_code: bool = False) -> str:
449
+ if torch is None:
450
+ raise RuntimeError("PyTorch is required for direct model inference.")
451
+ model, processor = _load_direct_components(model_id, hf_token, trust_remote_code=trust_remote_code)
452
+ image = _decode_image(image_bytes)
453
+
454
+ input_candidates = []
455
+ if prompt:
456
+ input_candidates.extend(
457
+ [
458
+ lambda: processor(text=prompt, images=image, return_tensors="pt"),
459
+ lambda: processor(images=image, text=prompt, return_tensors="pt"),
460
+ lambda: processor(prompt, image, return_tensors="pt"),
461
+ lambda: processor(prompt, return_tensors="pt"),
462
+ ]
463
+ )
464
+ input_candidates.append(lambda: processor(images=image, return_tensors="pt"))
465
+
466
+ prepared_inputs = None
467
+ prep_error = None
468
+ for builder in input_candidates:
469
+ try:
470
+ candidate = builder()
471
+ if isinstance(candidate, Mapping) and candidate:
472
+ prepared_inputs = {
473
+ k: (v.to(model.device) if hasattr(v, "to") else v) for k, v in candidate.items()
474
+ }
475
+ break
476
+ except Exception as exc: # pragma: no cover
477
+ prep_error = exc
478
+
479
+ if prepared_inputs is None:
480
+ raise RuntimeError(f"Could not prepare inputs for {model_id}: {prep_error}")
481
+
482
+ gen_kwargs = {}
483
+ if "max_new_tokens" in params:
484
+ gen_kwargs["max_new_tokens"] = params["max_new_tokens"]
485
+ if "temperature" in params:
486
+ gen_kwargs["temperature"] = params["temperature"]
487
+ if "top_p" in params:
488
+ gen_kwargs["top_p"] = params["top_p"]
489
+ if "top_k" in params:
490
+ gen_kwargs["top_k"] = params["top_k"]
491
+ if "do_sample" in params:
492
+ gen_kwargs["do_sample"] = params["do_sample"]
493
+ if "num_beams" in params:
494
+ gen_kwargs["num_beams"] = params["num_beams"]
495
+
496
+ with torch.no_grad():
497
+ generated = model.generate(**prepared_inputs, **gen_kwargs)
498
+
499
+ if isinstance(generated, torch.Tensor):
500
+ decoded = processor.batch_decode(generated, skip_special_tokens=True)
501
+ return parse_output(decoded)
502
+ if isinstance(generated, (list, tuple)):
503
+ return parse_output(generated)
504
+ return parse_output(str(generated))
505
+
506
+
507
  def _get_local_pipeline(model_id: str, hf_token: str, task: str, trust_remote_code: bool = False):
508
  device = "gpu" if (torch is not None and torch.cuda.is_available()) else "cpu"
509
  key = _pipeline_cache_key(model_id, task or "auto", device, trust_remote_code)
 
529
  if torch is not None and torch.cuda.is_available():
530
  torch.cuda.empty_cache()
531
  gc.collect()
532
+ gc.collect()
533
  return pipeline
534
 
535
 
 
553
  torch.cuda.empty_cache()
554
 
555
 
556
+ def _clear_direct_cache() -> None:
557
+ global _LOCAL_DIRECT_CACHE
558
+ with _LOCAL_DIRECT_LOCK:
559
+ directs = list(_LOCAL_DIRECT_CACHE.values())
560
+ _LOCAL_DIRECT_CACHE.clear()
561
+ for model, _ in directs:
562
+ try:
563
+ del model
564
+ except Exception:
565
+ pass
566
+ gc.collect()
567
+ if torch is not None and torch.cuda.is_available():
568
+ torch.cuda.empty_cache()
569
+
570
+
571
  def run_local_model(entry: dict, image_bytes: bytes, prompt: str, hf_token: str) -> str:
572
  if hf_pipeline is None:
573
  raise RuntimeError("transformers is not installed in this space.")
 
577
  model_id = entry["model_id"]
578
  task = _model_pipeline_task(entry)
579
  trust_remote_code = _model_requires_trust_remote_code(entry)
580
+ try:
581
+ pipeline = _get_local_pipeline(model_id, hf_token, task, trust_remote_code=trust_remote_code)
582
+ except Exception as exc:
583
+ pipeline = None
584
+ pipeline_error = exc
585
+ else:
586
+ pipeline_error = None
587
  params = sanitize_generation_params(entry.get("parameters", {}))
588
  image = _decode_image(image_bytes)
589
 
 
609
  if isinstance(key, str) and key in supported_body_keys:
610
  params.setdefault(key, value)
611
 
612
+ if pipeline is not None:
613
+ calls = []
614
+ if prompt:
615
+ calls.extend(
616
+ [
617
+ lambda: pipeline({"image": image, "text": prompt}, **params),
618
+ lambda: pipeline({"image": image, "question": prompt}, **params),
619
+ lambda: pipeline({"text": prompt, "image": image}, **params),
620
+ lambda: pipeline({"images": image, "text": prompt}, **params),
621
+ lambda: pipeline(image, text=prompt, **params),
622
+ lambda: pipeline(image, question=prompt, **params),
623
+ lambda: pipeline(image, **params),
624
+ lambda: pipeline({"image": image}, **params),
625
+ ]
626
+ )
627
+ else:
628
+ calls.extend([lambda: pipeline(image, **params), lambda: pipeline({"image": image}, **params)])
629
+
630
+ for call in calls:
631
+ try:
632
+ output = call()
633
+ parsed = parse_output(output)
634
+ if parsed:
635
+ return parsed
636
+ except Exception as exc: # pragma: no cover
637
+ pipeline_error = exc
638
+ continue
639
+
640
+ try:
641
+ direct_output = _direct_infer(
642
+ model_id,
643
+ image_bytes,
644
+ prompt,
645
+ hf_token,
646
+ params,
647
+ trust_remote_code=trust_remote_code,
648
  )
649
+ parsed = parse_output(direct_output)
650
+ if parsed:
651
+ return parsed
652
+ except Exception as exc: # pragma: no cover
653
+ if pipeline_error is None:
654
+ pipeline_error = exc
655
+ else:
656
+ pipeline_error = RuntimeError(f"{pipeline_error}; direct fallback failed: {exc}")
657
 
658
+ raise RuntimeError(f"Local model inference failed for {model_id}: {pipeline_error}")
 
 
 
 
 
 
 
 
 
 
659
 
660
 
661
  def run_tesseract(image_bytes: bytes, params: dict) -> str:
 
1006
  continue
1007
  futures_map[executor.submit(run_single_model, entry, image_bytes, "", hf_token)] = entry
1008
 
1009
+ for future in as_completed(futures_map):
1010
+ result = future.result()
1011
+ if ground_truth_text:
1012
+ cer, wer = compute_cer_wer(ground_truth_text, result.output)
1013
+ result.cer = cer
1014
+ result.wer = wer
1015
+ results.append(result)
1016
 
1017
  _clear_local_pipeline_cache()
1018
+ _clear_direct_cache()
1019
 
1020
  order_map = {model["id"]: idx for idx, model in enumerate(models)}
1021
  results.sort(key=lambda r: order_map.get(r.model_id, 1_000_000))
apt.txt CHANGED
@@ -1,3 +1,4 @@
1
  tesseract-ocr
2
  tesseract-ocr-eng
3
  tesseract-ocr-heb
 
 
1
  tesseract-ocr
2
  tesseract-ocr-eng
3
  tesseract-ocr-heb
4
+ libgl1
model_registry.json CHANGED
@@ -5,6 +5,7 @@
5
  "name": "Qwen3-VL-8B-Instruct",
6
  "model_id": "Qwen/Qwen3-VL-8B-Instruct",
7
  "provider": "local_transformer",
 
8
  "enabled": true,
9
  "prompt": "Transcribe every piece of printed and handwritten text from this document image. Keep paragraph/line layout when obvious. Return plain text only.",
10
  "parameters": {
@@ -20,6 +21,7 @@
20
  "name": "Qwen3-VL-4B-Instruct",
21
  "model_id": "Qwen/Qwen3-VL-4B-Instruct",
22
  "provider": "local_transformer",
 
23
  "enabled": true,
24
  "prompt": "Transcribe every piece of printed and handwritten text from this Hebrew document image. Keep line breaks and text order.",
25
  "parameters": {
@@ -35,6 +37,7 @@
35
  "name": "Qwen3-VL-8B-Thinking",
36
  "model_id": "Qwen/Qwen3-VL-8B-Thinking",
37
  "provider": "local_transformer",
 
38
  "enabled": true,
39
  "prompt": "Use deliberate reasoning to first recover layout and then output final OCR text exactly as visible. Hide reasoning and return only plain text.",
40
  "parameters": {
@@ -52,6 +55,7 @@
52
  "name": "Qwen3-VL-4B-Thinking",
53
  "model_id": "Qwen/Qwen3-VL-4B-Thinking",
54
  "provider": "local_transformer",
 
55
  "enabled": true,
56
  "prompt": "Use a deliberate OCR pass for printed and handwritten Hebrew text, then output only the final transcription.",
57
  "parameters": {
@@ -69,6 +73,7 @@
69
  "name": "Qwen3-VL-30B-A3B-Instruct",
70
  "model_id": "Qwen/Qwen3-VL-30B-A3B-Instruct",
71
  "provider": "local_transformer",
 
72
  "enabled": true,
73
  "prompt": "OCR this Hebrew document image and return plain text only, preserving order, lines and spacing.",
74
  "parameters": {
@@ -83,6 +88,7 @@
83
  "name": "Qwen3-VL-30B-A3B-Thinking",
84
  "model_id": "Qwen/Qwen3-VL-30B-A3B-Thinking",
85
  "provider": "local_transformer",
 
86
  "enabled": true,
87
  "prompt": "Think through layout and character ambiguity first, then output final OCR text only.",
88
  "parameters": {
@@ -238,6 +244,8 @@
238
  "name": "Chandra2 (datalab-to/chandra-ocr-2)",
239
  "model_id": "datalab-to/chandra-ocr-2",
240
  "provider": "local_transformer",
 
 
241
  "enabled": true,
242
  "prompt": "Output only the exact text from the uploaded document, including handwritten and printed parts.",
243
  "parameters": {
@@ -251,6 +259,8 @@
251
  "name": "ronylicha/gigapdf-ocr-hebrew",
252
  "model_id": "ronylicha/gigapdf-ocr-hebrew",
253
  "provider": "local_transformer",
 
 
254
  "enabled": true,
255
  "prompt": "Read the Hebrew document image and return only transcribed text.",
256
  "parameters": {
@@ -264,6 +274,8 @@
264
  "name": "Qunie-V7-mini",
265
  "model_id": "liskcell/Qunie-V7-mini",
266
  "provider": "local_transformer",
 
 
267
  "enabled": true,
268
  "prompt": "Strict OCR extraction. Output line-by-line text from the whole image.",
269
  "parameters": {
@@ -278,6 +290,8 @@
278
  "name": "OpenMLKitOCR",
279
  "model_id": "0cve0/OpenMLKitOCR",
280
  "provider": "local_transformer",
 
 
281
  "enabled": true,
282
  "prompt": "OCR the document and return readable text as-is.",
283
  "parameters": {
@@ -291,6 +305,8 @@
291
  "name": "Tzefa-Word-OCR-TrOCR",
292
  "model_id": "WARAJA/Tzefa-Word-OCR-TrOCR",
293
  "provider": "local_transformer",
 
 
294
  "enabled": true,
295
  "prompt": "Do OCR on this image. Return only transcribed words in reading order.",
296
  "parameters": {
@@ -304,6 +320,8 @@
304
  "name": "Qunie-V7-Pico",
305
  "model_id": "liskcell/Qunie-V7-Pico",
306
  "provider": "local_transformer",
 
 
307
  "enabled": true,
308
  "prompt": "Extract all readable text. Prioritize transcription correctness over grammar cleanup.",
309
  "parameters": {
@@ -318,6 +336,7 @@
318
  "name": "Aya Vision 32B",
319
  "model_id": "CohereLabs/aya-vision-32b",
320
  "provider": "local_transformer",
 
321
  "enabled": true,
322
  "prompt": "Return plain OCR output only from the image, including punctuation and line breaks.",
323
  "parameters": {
@@ -332,6 +351,7 @@
332
  "name": "Aya Vision 8B",
333
  "model_id": "CohereLabs/aya-vision-8b",
334
  "provider": "local_transformer",
 
335
  "enabled": true,
336
  "prompt": "Return only text from the document image; preserve paragraph breaks where clear.",
337
  "parameters": {
@@ -346,6 +366,8 @@
346
  "name": "Phi-4-Multimodal-Instruct",
347
  "model_id": "microsoft/Phi-4-multimodal-instruct",
348
  "provider": "local_transformer",
 
 
349
  "enabled": true,
350
  "prompt": "OCR-only transcription. Output exactly what is printed/handwritten in the image.",
351
  "parameters": {
@@ -360,6 +382,8 @@
360
  "name": "surya2 (datalab-to/surya-ocr-2)",
361
  "model_id": "datalab-to/surya-ocr-2",
362
  "provider": "local_transformer",
 
 
363
  "enabled": true,
364
  "prompt": "Return only OCR output from this document image, preserving line breaks and order.",
365
  "parameters": {
@@ -374,6 +398,8 @@
374
  "name": "PaddleOCR-VL",
375
  "model_id": "PaddlePaddle/PaddleOCR-VL",
376
  "provider": "local_transformer",
 
 
377
  "enabled": true,
378
  "prompt": "Transcribe all readable printed and handwritten text from this document image in order.",
379
  "parameters": {
@@ -387,6 +413,8 @@
387
  "name": "PaddleOCR-VL 1.5",
388
  "model_id": "PaddlePaddle/PaddleOCR-VL-1.5",
389
  "provider": "local_transformer",
 
 
390
  "enabled": true,
391
  "prompt": "Transcribe all readable printed and handwritten text from this document image in order.",
392
  "parameters": {
@@ -400,6 +428,8 @@
400
  "name": "PaddleOCR-VL 1.6",
401
  "model_id": "PaddlePaddle/PaddleOCR-VL-1.6",
402
  "provider": "local_transformer",
 
 
403
  "enabled": true,
404
  "prompt": "Transcribe all readable printed and handwritten text from this document image in order.",
405
  "parameters": {
@@ -413,6 +443,8 @@
413
  "name": "DeepSeek OCR-1",
414
  "model_id": "deepseek-ai/DeepSeek-OCR",
415
  "provider": "local_transformer",
 
 
416
  "enabled": true,
417
  "prompt": "Transcribe all printed and handwritten text from this Hebrew document image. Output only plain text.",
418
  "parameters": {
@@ -426,6 +458,8 @@
426
  "name": "DeepSeek OCR-2",
427
  "model_id": "deepseek-ai/DeepSeek-OCR-2",
428
  "provider": "local_transformer",
 
 
429
  "enabled": true,
430
  "prompt": "Transcribe all printed and handwritten text from this Hebrew document image. Preserve line breaks as visible.",
431
  "parameters": {
@@ -454,6 +488,8 @@
454
  "name": "cyttic/exp10-trocr-hebrew-matan-full",
455
  "model_id": "cyttic/exp10-trocr-hebrew-matan-full",
456
  "provider": "local_transformer",
 
 
457
  "enabled": true,
458
  "prompt": "Transcribe all readable printed and handwritten Hebrew text from this document image. Return plain text only, preserve line breaks and spacing.",
459
  "parameters": {
@@ -468,6 +504,8 @@
468
  "name": "cyttic/exp23-directfit-unfrozen",
469
  "model_id": "cyttic/exp23-directfit-unfrozen",
470
  "provider": "local_transformer",
 
 
471
  "enabled": true,
472
  "prompt": "OCR this document image and extract all printed and handwritten Hebrew text faithfully. Return only the plain transcription.",
473
  "parameters": {
@@ -482,6 +520,8 @@
482
  "name": "cyttic/heb-verifier17-connected",
483
  "model_id": "cyttic/heb-verifier17-connected",
484
  "provider": "local_transformer",
 
 
485
  "enabled": true,
486
  "prompt": "Return only the transcribed text from the uploaded document image (Hebrew document with printed and handwritten text). Preserve line order.",
487
  "parameters": {
@@ -496,6 +536,8 @@
496
  "name": "cyttic/exp26-composed1m",
497
  "model_id": "cyttic/exp26-composed1m",
498
  "provider": "local_transformer",
 
 
499
  "enabled": true,
500
  "prompt": "Transcribe exactly what is visible in the image, including Hebrew text lines and mixed-direction fragments.",
501
  "parameters": {
 
5
  "name": "Qwen3-VL-8B-Instruct",
6
  "model_id": "Qwen/Qwen3-VL-8B-Instruct",
7
  "provider": "local_transformer",
8
+ "pipeline_task": "image-to-text",
9
  "enabled": true,
10
  "prompt": "Transcribe every piece of printed and handwritten text from this document image. Keep paragraph/line layout when obvious. Return plain text only.",
11
  "parameters": {
 
21
  "name": "Qwen3-VL-4B-Instruct",
22
  "model_id": "Qwen/Qwen3-VL-4B-Instruct",
23
  "provider": "local_transformer",
24
+ "pipeline_task": "image-to-text",
25
  "enabled": true,
26
  "prompt": "Transcribe every piece of printed and handwritten text from this Hebrew document image. Keep line breaks and text order.",
27
  "parameters": {
 
37
  "name": "Qwen3-VL-8B-Thinking",
38
  "model_id": "Qwen/Qwen3-VL-8B-Thinking",
39
  "provider": "local_transformer",
40
+ "pipeline_task": "image-to-text",
41
  "enabled": true,
42
  "prompt": "Use deliberate reasoning to first recover layout and then output final OCR text exactly as visible. Hide reasoning and return only plain text.",
43
  "parameters": {
 
55
  "name": "Qwen3-VL-4B-Thinking",
56
  "model_id": "Qwen/Qwen3-VL-4B-Thinking",
57
  "provider": "local_transformer",
58
+ "pipeline_task": "image-to-text",
59
  "enabled": true,
60
  "prompt": "Use a deliberate OCR pass for printed and handwritten Hebrew text, then output only the final transcription.",
61
  "parameters": {
 
73
  "name": "Qwen3-VL-30B-A3B-Instruct",
74
  "model_id": "Qwen/Qwen3-VL-30B-A3B-Instruct",
75
  "provider": "local_transformer",
76
+ "pipeline_task": "image-to-text",
77
  "enabled": true,
78
  "prompt": "OCR this Hebrew document image and return plain text only, preserving order, lines and spacing.",
79
  "parameters": {
 
88
  "name": "Qwen3-VL-30B-A3B-Thinking",
89
  "model_id": "Qwen/Qwen3-VL-30B-A3B-Thinking",
90
  "provider": "local_transformer",
91
+ "pipeline_task": "image-to-text",
92
  "enabled": true,
93
  "prompt": "Think through layout and character ambiguity first, then output final OCR text only.",
94
  "parameters": {
 
244
  "name": "Chandra2 (datalab-to/chandra-ocr-2)",
245
  "model_id": "datalab-to/chandra-ocr-2",
246
  "provider": "local_transformer",
247
+ "pipeline_task": "image-to-text",
248
+ "trust_remote_code": true,
249
  "enabled": true,
250
  "prompt": "Output only the exact text from the uploaded document, including handwritten and printed parts.",
251
  "parameters": {
 
259
  "name": "ronylicha/gigapdf-ocr-hebrew",
260
  "model_id": "ronylicha/gigapdf-ocr-hebrew",
261
  "provider": "local_transformer",
262
+ "pipeline_task": "image-to-text",
263
+ "trust_remote_code": true,
264
  "enabled": true,
265
  "prompt": "Read the Hebrew document image and return only transcribed text.",
266
  "parameters": {
 
274
  "name": "Qunie-V7-mini",
275
  "model_id": "liskcell/Qunie-V7-mini",
276
  "provider": "local_transformer",
277
+ "pipeline_task": "image-to-text",
278
+ "trust_remote_code": true,
279
  "enabled": true,
280
  "prompt": "Strict OCR extraction. Output line-by-line text from the whole image.",
281
  "parameters": {
 
290
  "name": "OpenMLKitOCR",
291
  "model_id": "0cve0/OpenMLKitOCR",
292
  "provider": "local_transformer",
293
+ "pipeline_task": "image-to-text",
294
+ "trust_remote_code": true,
295
  "enabled": true,
296
  "prompt": "OCR the document and return readable text as-is.",
297
  "parameters": {
 
305
  "name": "Tzefa-Word-OCR-TrOCR",
306
  "model_id": "WARAJA/Tzefa-Word-OCR-TrOCR",
307
  "provider": "local_transformer",
308
+ "pipeline_task": "image-to-text",
309
+ "trust_remote_code": true,
310
  "enabled": true,
311
  "prompt": "Do OCR on this image. Return only transcribed words in reading order.",
312
  "parameters": {
 
320
  "name": "Qunie-V7-Pico",
321
  "model_id": "liskcell/Qunie-V7-Pico",
322
  "provider": "local_transformer",
323
+ "pipeline_task": "image-to-text",
324
+ "trust_remote_code": true,
325
  "enabled": true,
326
  "prompt": "Extract all readable text. Prioritize transcription correctness over grammar cleanup.",
327
  "parameters": {
 
336
  "name": "Aya Vision 32B",
337
  "model_id": "CohereLabs/aya-vision-32b",
338
  "provider": "local_transformer",
339
+ "pipeline_task": "image-to-text",
340
  "enabled": true,
341
  "prompt": "Return plain OCR output only from the image, including punctuation and line breaks.",
342
  "parameters": {
 
351
  "name": "Aya Vision 8B",
352
  "model_id": "CohereLabs/aya-vision-8b",
353
  "provider": "local_transformer",
354
+ "pipeline_task": "image-to-text",
355
  "enabled": true,
356
  "prompt": "Return only text from the document image; preserve paragraph breaks where clear.",
357
  "parameters": {
 
366
  "name": "Phi-4-Multimodal-Instruct",
367
  "model_id": "microsoft/Phi-4-multimodal-instruct",
368
  "provider": "local_transformer",
369
+ "pipeline_task": "image-text-to-text",
370
+ "trust_remote_code": true,
371
  "enabled": true,
372
  "prompt": "OCR-only transcription. Output exactly what is printed/handwritten in the image.",
373
  "parameters": {
 
382
  "name": "surya2 (datalab-to/surya-ocr-2)",
383
  "model_id": "datalab-to/surya-ocr-2",
384
  "provider": "local_transformer",
385
+ "pipeline_task": "image-to-text",
386
+ "trust_remote_code": true,
387
  "enabled": true,
388
  "prompt": "Return only OCR output from this document image, preserving line breaks and order.",
389
  "parameters": {
 
398
  "name": "PaddleOCR-VL",
399
  "model_id": "PaddlePaddle/PaddleOCR-VL",
400
  "provider": "local_transformer",
401
+ "pipeline_task": "image-to-text",
402
+ "trust_remote_code": true,
403
  "enabled": true,
404
  "prompt": "Transcribe all readable printed and handwritten text from this document image in order.",
405
  "parameters": {
 
413
  "name": "PaddleOCR-VL 1.5",
414
  "model_id": "PaddlePaddle/PaddleOCR-VL-1.5",
415
  "provider": "local_transformer",
416
+ "pipeline_task": "image-to-text",
417
+ "trust_remote_code": true,
418
  "enabled": true,
419
  "prompt": "Transcribe all readable printed and handwritten text from this document image in order.",
420
  "parameters": {
 
428
  "name": "PaddleOCR-VL 1.6",
429
  "model_id": "PaddlePaddle/PaddleOCR-VL-1.6",
430
  "provider": "local_transformer",
431
+ "pipeline_task": "image-to-text",
432
+ "trust_remote_code": true,
433
  "enabled": true,
434
  "prompt": "Transcribe all readable printed and handwritten text from this document image in order.",
435
  "parameters": {
 
443
  "name": "DeepSeek OCR-1",
444
  "model_id": "deepseek-ai/DeepSeek-OCR",
445
  "provider": "local_transformer",
446
+ "pipeline_task": "image-to-text",
447
+ "trust_remote_code": true,
448
  "enabled": true,
449
  "prompt": "Transcribe all printed and handwritten text from this Hebrew document image. Output only plain text.",
450
  "parameters": {
 
458
  "name": "DeepSeek OCR-2",
459
  "model_id": "deepseek-ai/DeepSeek-OCR-2",
460
  "provider": "local_transformer",
461
+ "pipeline_task": "image-to-text",
462
+ "trust_remote_code": true,
463
  "enabled": true,
464
  "prompt": "Transcribe all printed and handwritten text from this Hebrew document image. Preserve line breaks as visible.",
465
  "parameters": {
 
488
  "name": "cyttic/exp10-trocr-hebrew-matan-full",
489
  "model_id": "cyttic/exp10-trocr-hebrew-matan-full",
490
  "provider": "local_transformer",
491
+ "pipeline_task": "image-to-text",
492
+ "trust_remote_code": true,
493
  "enabled": true,
494
  "prompt": "Transcribe all readable printed and handwritten Hebrew text from this document image. Return plain text only, preserve line breaks and spacing.",
495
  "parameters": {
 
504
  "name": "cyttic/exp23-directfit-unfrozen",
505
  "model_id": "cyttic/exp23-directfit-unfrozen",
506
  "provider": "local_transformer",
507
+ "pipeline_task": "image-to-text",
508
+ "trust_remote_code": true,
509
  "enabled": true,
510
  "prompt": "OCR this document image and extract all printed and handwritten Hebrew text faithfully. Return only the plain transcription.",
511
  "parameters": {
 
520
  "name": "cyttic/heb-verifier17-connected",
521
  "model_id": "cyttic/heb-verifier17-connected",
522
  "provider": "local_transformer",
523
+ "pipeline_task": "image-to-text",
524
+ "trust_remote_code": true,
525
  "enabled": true,
526
  "prompt": "Return only the transcribed text from the uploaded document image (Hebrew document with printed and handwritten text). Preserve line order.",
527
  "parameters": {
 
536
  "name": "cyttic/exp26-composed1m",
537
  "model_id": "cyttic/exp26-composed1m",
538
  "provider": "local_transformer",
539
+ "pipeline_task": "image-to-text",
540
+ "trust_remote_code": true,
541
  "enabled": true,
542
  "prompt": "Transcribe exactly what is visible in the image, including Hebrew text lines and mixed-direction fragments.",
543
  "parameters": {
requirements.txt CHANGED
@@ -3,7 +3,9 @@ huggingface-hub>=0.25.0,<1.0.0
3
  requests>=2.32.3
4
  Pillow>=10.4.0
5
  pytesseract>=0.3.13
6
- transformers>=4.57.0
 
 
7
  qwen-vl-utils>=0.0.8
8
  tokenizers>=0.20.0
9
  sentencepiece>=0.1.99
 
3
  requests>=2.32.3
4
  Pillow>=10.4.0
5
  pytesseract>=0.3.13
6
+ transformers>=5.14.1,<6.0.0
7
+ addict
8
+ torchvision
9
  qwen-vl-utils>=0.0.8
10
  tokenizers>=0.20.0
11
  sentencepiece>=0.1.99