baohuynhbk14 commited on
Commit
13cd016
Β·
verified Β·
1 Parent(s): 8bff5f1

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +44 -3
  2. index.html +42 -8
app.py CHANGED
@@ -12,6 +12,7 @@ import sys
12
  import tempfile
13
  import threading
14
  import time
 
15
  import uuid
16
  import zipfile
17
  from pathlib import Path
@@ -641,7 +642,10 @@ MODEL = AutoModel.from_pretrained(
641
  torch.set_grad_enabled(False)
642
  print("Unlimited-OCR model ready.", flush=True)
643
 
644
- app = Server(title="Unlimited-OCR Hugging Face Space")
 
 
 
645
  _MODEL_LOCK = threading.Lock()
646
  _THREAD_CONTEXT = threading.local()
647
  _ACTIVE_REQUESTS: dict[str, threading.Event] = {}
@@ -726,7 +730,19 @@ def _run_model_worker(
726
  except GenerationCancelled:
727
  emit("cancelled", None)
728
  except BaseException as exc:
729
- emit("error", exc)
 
 
 
 
 
 
 
 
 
 
 
 
730
  finally:
731
  ring_window = getattr(MODEL.config, "_ring_window", None)
732
  if ring_window is not None:
@@ -759,6 +775,12 @@ def run_ocr_batch(
759
  ) -> Iterator[dict[str, Any]]:
760
  """Run one ZeroGPU batch and stream UI events through Gradio's SSE queue."""
761
 
 
 
 
 
 
 
762
  session_dir = _session_dir(session_id)
763
  pages = _read_metadata(session_dir).get("pages") or []
764
  if not _SAFE_REQUEST_ID.fullmatch(request_id):
@@ -837,9 +859,22 @@ def run_ocr_batch(
837
  elif kind == "final":
838
  final_output = str(value or "")
839
  elif kind == "error":
 
 
 
 
 
 
840
  yield {
841
  "event": "error",
842
- "data": {"message": f"Hugging Face inference failed: {value}"},
 
 
 
 
 
 
 
843
  }
844
  return
845
  elif kind == "cancelled":
@@ -887,6 +922,12 @@ def run_ocr_batch(
887
  "mapped_pages": len(pairs),
888
  },
889
  }
 
 
 
 
 
 
890
  finally:
891
  cancel.set()
892
  if worker.is_alive():
 
12
  import tempfile
13
  import threading
14
  import time
15
+ import traceback
16
  import uuid
17
  import zipfile
18
  from pathlib import Path
 
642
  torch.set_grad_enabled(False)
643
  print("Unlimited-OCR model ready.", flush=True)
644
 
645
+ app = Server(debug=True, title="Unlimited-OCR Hugging Face Space")
646
+ # Hugging Face's Gradio hot-reload launcher looks for the conventional `demo`
647
+ # symbol. Keep both names pointing at the same Server instance.
648
+ demo = app
649
  _MODEL_LOCK = threading.Lock()
650
  _THREAD_CONTEXT = threading.local()
651
  _ACTIVE_REQUESTS: dict[str, threading.Event] = {}
 
730
  except GenerationCancelled:
731
  emit("cancelled", None)
732
  except BaseException as exc:
733
+ error_traceback = traceback.format_exc()
734
+ print(
735
+ f"[OCR ERROR] request={settings.request_id} "
736
+ f"pages={settings.page_indices}: {exc}\n{error_traceback}",
737
+ flush=True,
738
+ )
739
+ emit(
740
+ "error",
741
+ {
742
+ "message": str(exc) or exc.__class__.__name__,
743
+ "exception_type": exc.__class__.__name__,
744
+ },
745
+ )
746
  finally:
747
  ring_window = getattr(MODEL.config, "_ring_window", None)
748
  if ring_window is not None:
 
775
  ) -> Iterator[dict[str, Any]]:
776
  """Run one ZeroGPU batch and stream UI events through Gradio's SSE queue."""
777
 
778
+ print(
779
+ f"[OCR START] request={request_id} batch={batch_number}/{total_batches} "
780
+ f"session={session_id} pages={page_indices} mode={mode} "
781
+ f"max_tokens={max_tokens}",
782
+ flush=True,
783
+ )
784
  session_dir = _session_dir(session_id)
785
  pages = _read_metadata(session_dir).get("pages") or []
786
  if not _SAFE_REQUEST_ID.fullmatch(request_id):
 
859
  elif kind == "final":
860
  final_output = str(value or "")
861
  elif kind == "error":
862
+ if isinstance(value, dict):
863
+ error_message = value.get("message") or "Unknown inference error"
864
+ exception_type = value.get("exception_type") or "Error"
865
+ else:
866
+ error_message = str(value) or "Unknown inference error"
867
+ exception_type = type(value).__name__
868
  yield {
869
  "event": "error",
870
+ "data": {
871
+ "message": (
872
+ f"Hugging Face inference failed "
873
+ f"({exception_type}): {error_message}"
874
+ ),
875
+ "request_id": request_id,
876
+ "batch": batch_number,
877
+ },
878
  }
879
  return
880
  elif kind == "cancelled":
 
922
  "mapped_pages": len(pairs),
923
  },
924
  }
925
+ print(
926
+ f"[OCR DONE] request={request_id} batch={batch_number}/{total_batches} "
927
+ f"characters={len(raw_output)} mapped_pages={len(pairs)} "
928
+ f"latency={latency:.3f}s",
929
+ flush=True,
930
+ )
931
  finally:
932
  cancel.set()
933
  if worker.is_alive():
index.html CHANGED
@@ -327,6 +327,14 @@
327
  requestAnimationFrame(() => { state.rawScrollScheduled = false; viewer.scrollTop = viewer.scrollHeight; });
328
  }
329
  }
 
 
 
 
 
 
 
 
330
  function semanticColor(label) {
331
  const key = String(label || 'box').trim().toLowerCase();
332
  if (colors[key]) return colors[key];
@@ -766,30 +774,53 @@
766
  $('#latencyText').textContent = `${data.elapsed_seconds}s`; $('#downloadButton').disabled = false; renderAllPages(); selectPage(state.currentPage);
767
  }
768
  if (name === 'cancelled') { setRunning(false); setStatus('STOPPED'); }
769
- if (name === 'error') { setRunning(false); setStatus('ERROR', 'error'); showToast(data.message || 'Streaming failed'); }
 
 
 
770
  }
771
  async function consumeGradioJob(eventId, signal) {
772
  const response = await fetch(`/gradio_api/call/run_ocr_batch/${encodeURIComponent(eventId)}`, {signal});
773
- if (!response.ok) { const data = await response.json().catch(() => ({})); throw new Error(data.detail || `Gradio HTTP ${response.status}`); }
774
- const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; let lastPayload = '';
 
 
 
 
 
775
  while (true) {
776
  const {value, done} = await reader.read(); buffer += decoder.decode(value || new Uint8Array(), {stream: !done}).replace(/\r\n/g, '\n');
777
  let boundary;
778
  while ((boundary = buffer.indexOf('\n\n')) >= 0) {
779
  const block = buffer.slice(0, boundary); buffer = buffer.slice(boundary + 2); let name = 'message'; const dataLines = [];
780
  for (const line of block.split('\n')) { if (line.startsWith('event:')) name = line.slice(6).trim(); if (line.startsWith('data:')) dataLines.push(line.slice(5).trimStart()); }
781
- if (name === 'error') throw new Error(dataLines.join('\n') || 'ZeroGPU job failed');
 
 
 
 
782
  if (dataLines.length && (name === 'generating' || name === 'complete')) {
783
- const parsed = JSON.parse(dataLines.join('\n')); const update = Array.isArray(parsed) ? parsed[0] : parsed; const signature = JSON.stringify(update);
784
- if (update && update.event && signature !== lastPayload) { lastPayload = signature; processEvent(update.event, update.data || {}); if (update.event === 'error') throw new Error((update.data && update.data.message) || 'OCR failed'); }
 
 
 
 
 
 
 
 
785
  }
786
  }
787
  if (done) break;
788
  }
 
789
  }
790
  async function callZeroGpuBatch(args, signal) {
791
  const response = await fetch('/gradio_api/call/run_ocr_batch', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({data:args}), signal});
792
- const data = await response.json().catch(() => ({})); if (!response.ok || !data.event_id) throw new Error(data.detail || `Unable to queue ZeroGPU job (${response.status})`);
 
 
793
  await consumeGradioJob(data.event_id, signal);
794
  }
795
  async function startGeneration() {
@@ -815,7 +846,10 @@
815
  }
816
  if (state.running) processEvent('done', {completed_pages:Object.keys(state.results).length, selected_pages:pageIndices.length, elapsed_seconds:Math.round((performance.now() - startedAt) / 100) / 10});
817
  } catch (error) {
818
- if (error.name !== 'AbortError') { setStatus('STREAM ERROR', 'error'); showToast(error.message); }
 
 
 
819
  setRunning(false);
820
  }
821
  }
 
327
  requestAnimationFrame(() => { state.rawScrollScheduled = false; viewer.scrollTop = viewer.scrollHeight; });
328
  }
329
  }
330
+ function appendStreamDiagnostic(message) {
331
+ const output = $('#rawOutput');
332
+ const text = String(message || 'Unknown streaming error');
333
+ if (output.textContent && !output.textContent.endsWith('\n')) output.appendChild(document.createTextNode('\n'));
334
+ output.appendChild(document.createTextNode(`\n===== STREAM ERROR =====\n${text}\n`));
335
+ output.parentElement.scrollTop = output.parentElement.scrollHeight;
336
+ console.error('[Unlimited-OCR stream]', text);
337
+ }
338
  function semanticColor(label) {
339
  const key = String(label || 'box').trim().toLowerCase();
340
  if (colors[key]) return colors[key];
 
774
  $('#latencyText').textContent = `${data.elapsed_seconds}s`; $('#downloadButton').disabled = false; renderAllPages(); selectPage(state.currentPage);
775
  }
776
  if (name === 'cancelled') { setRunning(false); setStatus('STOPPED'); }
777
+ if (name === 'error') {
778
+ const detail = data.message || 'Streaming failed';
779
+ setRunning(false); setStatus('ERROR', 'error'); appendStreamDiagnostic(detail); showToast(detail);
780
+ }
781
  }
782
  async function consumeGradioJob(eventId, signal) {
783
  const response = await fetch(`/gradio_api/call/run_ocr_batch/${encodeURIComponent(eventId)}`, {signal});
784
+ if (!response.ok) {
785
+ const body = await response.text().catch(() => '');
786
+ let detail = body; try { const parsed = JSON.parse(body); detail = parsed.detail || parsed.error || body; } catch (_) {}
787
+ throw new Error(`Gradio result HTTP ${response.status}${detail ? `: ${detail}` : ''} [job ${eventId}]`);
788
+ }
789
+ if (!response.body) throw new Error(`Gradio returned an empty stream [job ${eventId}]`);
790
+ const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; let lastPayload = ''; let completed = false;
791
  while (true) {
792
  const {value, done} = await reader.read(); buffer += decoder.decode(value || new Uint8Array(), {stream: !done}).replace(/\r\n/g, '\n');
793
  let boundary;
794
  while ((boundary = buffer.indexOf('\n\n')) >= 0) {
795
  const block = buffer.slice(0, boundary); buffer = buffer.slice(boundary + 2); let name = 'message'; const dataLines = [];
796
  for (const line of block.split('\n')) { if (line.startsWith('event:')) name = line.slice(6).trim(); if (line.startsWith('data:')) dataLines.push(line.slice(5).trimStart()); }
797
+ if (name === 'error') {
798
+ const rawError = dataLines.join('\n'); let detail = rawError;
799
+ try { const parsedError = JSON.parse(rawError); detail = parsedError.message || parsedError.error || parsedError.detail || rawError; } catch (_) {}
800
+ throw new Error(`${detail || 'ZeroGPU job failed without an error payload'} [job ${eventId}]`);
801
+ }
802
  if (dataLines.length && (name === 'generating' || name === 'complete')) {
803
+ let parsed;
804
+ try { parsed = JSON.parse(dataLines.join('\n')); }
805
+ catch (error) { throw new Error(`Invalid Gradio SSE JSON: ${error.message} [job ${eventId}]`); }
806
+ if (name === 'complete') completed = true;
807
+ const update = Array.isArray(parsed) ? parsed[0] : parsed; const signature = JSON.stringify(update);
808
+ if (update && update.event && signature !== lastPayload) {
809
+ lastPayload = signature;
810
+ if (update.event === 'error') throw new Error(`${(update.data && update.data.message) || 'OCR failed'} [job ${eventId}]`);
811
+ processEvent(update.event, update.data || {});
812
+ }
813
  }
814
  }
815
  if (done) break;
816
  }
817
+ if (!completed) throw new Error(`Gradio stream closed before the job completed [job ${eventId}]`);
818
  }
819
  async function callZeroGpuBatch(args, signal) {
820
  const response = await fetch('/gradio_api/call/run_ocr_batch', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({data:args}), signal});
821
+ const body = await response.text().catch(() => ''); let data = {};
822
+ try { data = JSON.parse(body); } catch (_) {}
823
+ if (!response.ok || !data.event_id) throw new Error(`${data.detail || data.error || body || `Unable to queue ZeroGPU job (${response.status})`} [queue HTTP ${response.status}]`);
824
  await consumeGradioJob(data.event_id, signal);
825
  }
826
  async function startGeneration() {
 
846
  }
847
  if (state.running) processEvent('done', {completed_pages:Object.keys(state.results).length, selected_pages:pageIndices.length, elapsed_seconds:Math.round((performance.now() - startedAt) / 100) / 10});
848
  } catch (error) {
849
+ if (error.name !== 'AbortError') {
850
+ const detail = error && error.message ? error.message : String(error);
851
+ setStatus('STREAM ERROR', 'error'); appendStreamDiagnostic(detail); showToast(detail);
852
+ }
853
  setRunning(false);
854
  }
855
  }