prithivMLmods commited on
Commit
8df2bee
·
verified ·
1 Parent(s): 73d998e

update app

Browse files
Files changed (1) hide show
  1. app.py +283 -129
app.py CHANGED
@@ -91,6 +91,12 @@ image_examples = [
91
  ]
92
 
93
 
 
 
 
 
 
 
94
  def pil_to_data_url(img: Image.Image, fmt="PNG"):
95
  buf = BytesIO()
96
  img.save(buf, format=fmt)
@@ -148,25 +154,45 @@ EXAMPLE_CARDS_HTML = build_example_cards_html()
148
 
149
  def load_example_data(idx_str):
150
  try:
151
- idx = int(float(idx_str))
152
  except Exception:
153
- return json.dumps({"status": "error", "message": "Invalid example index"})
 
154
  if idx < 0 or idx >= len(image_examples):
155
- return json.dumps({"status": "error", "message": "Example index out of range"})
 
156
  ex = image_examples[idx]
157
  img_b64 = file_to_data_url(ex["image"])
158
  if not img_b64:
159
- return json.dumps({"status": "error", "message": "Could not load example image"})
160
- return json.dumps({
 
161
  "status": "ok",
162
  "query": ex["query"],
163
  "image": img_b64,
164
  "model": ex["model"],
165
  "name": os.path.basename(ex["image"]),
166
- })
167
 
168
 
169
- def calc_timeout_image(model_name, text, image, max_new_tokens, temperature, top_p, top_k, repetition_penalty, gpu_timeout):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  try:
171
  return int(gpu_timeout)
172
  except Exception:
@@ -175,58 +201,129 @@ def calc_timeout_image(model_name, text, image, max_new_tokens, temperature, top
175
 
176
  @spaces.GPU(duration=calc_timeout_image)
177
  def generate_image(model_name, text, image, max_new_tokens, temperature, top_p, top_k, repetition_penalty, gpu_timeout=60):
178
- if model_name not in MODEL_MAP:
179
- raise gr.Error("Please select a valid model.")
180
- if image is None:
181
- raise gr.Error("Please upload an image.")
182
- if not text or not str(text).strip():
183
- raise gr.Error("Please enter your OCR/query instruction.")
184
- if len(str(text)) > MAX_INPUT_TOKEN_LENGTH * 8:
185
- raise gr.Error("Query is too long. Please shorten your input.")
186
-
187
- processor, model = MODEL_MAP[model_name]
188
-
189
- messages = [{
190
- "role": "user",
191
- "content": [
192
- {"type": "image"},
193
- {"type": "text", "text": text},
194
- ]
195
- }]
196
- prompt_full = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
197
-
198
- inputs = processor(
199
- text=[prompt_full],
200
- images=[image],
201
- return_tensors="pt",
202
- padding=True
203
- ).to(device)
204
-
205
- streamer = TextIteratorStreamer(processor, skip_prompt=True, skip_special_tokens=True)
206
- generation_kwargs = {
207
- **inputs,
208
- "streamer": streamer,
209
- "max_new_tokens": int(max_new_tokens),
210
- "do_sample": True,
211
- "temperature": float(temperature),
212
- "top_p": float(top_p),
213
- "top_k": int(top_k),
214
- "repetition_penalty": float(repetition_penalty),
215
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
- thread = Thread(target=model.generate, kwargs=generation_kwargs)
218
- thread.start()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
- buffer = ""
221
- for new_text in streamer:
222
- buffer += new_text
223
- buffer = buffer.replace("<|im_end|>", "")
224
- time.sleep(0.01)
225
- yield buffer
226
 
227
- gc.collect()
228
- if torch.cuda.is_available():
229
- torch.cuda.empty_cache()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
 
232
  def noop():
@@ -568,7 +665,6 @@ function init() {
568
  const runBtnEl = document.getElementById('custom-run-btn');
569
  const outputArea = document.getElementById('custom-output-textarea');
570
  const imgStatus = document.getElementById('sb-image-status');
571
- const exampleResultContainer = document.getElementById('example-result-data');
572
 
573
  if (!dropZone || !fileInput || !promptInput || !previewWrap || !previewImg) {
574
  setTimeout(init, 250);
@@ -578,6 +674,8 @@ function init() {
578
  window.__ocr3InitDone = true;
579
  let imageState = null;
580
  let toastTimer = null;
 
 
581
 
582
  function showToast(message, type) {
583
  let toast = document.getElementById('app-toast');
@@ -600,7 +698,6 @@ function init() {
600
  toast.classList.add('visible');
601
  toastTimer = setTimeout(() => toast.classList.remove('visible'), 3500);
602
  }
603
- window.__showToast = showToast;
604
 
605
  function showLoader() {
606
  const l = document.getElementById('output-loader');
@@ -614,8 +711,17 @@ function init() {
614
  const sb = document.getElementById('sb-run-state');
615
  if (sb) sb.textContent = 'Done';
616
  }
 
 
 
 
 
 
 
 
617
  window.__showLoader = showLoader;
618
  window.__hideLoader = hideLoader;
 
619
 
620
  function flashPromptError() {
621
  promptInput.classList.add('error-flash');
@@ -629,19 +735,27 @@ function init() {
629
  setTimeout(() => outputArea.classList.remove('error-flash'), 800);
630
  }
631
 
 
 
 
 
 
 
 
632
  function setGradioValue(containerId, value) {
633
  const container = document.getElementById(containerId);
634
- if (!container) return;
635
- container.querySelectorAll('input, textarea').forEach(el => {
636
- if (el.type === 'file' || el.type === 'range' || el.type === 'checkbox') return;
637
- const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
638
- const ns = Object.getOwnPropertyDescriptor(proto, 'value');
639
- if (ns && ns.set) {
640
- ns.set.call(el, value);
641
- el.dispatchEvent(new Event('input', {bubbles:true, composed:true}));
642
- el.dispatchEvent(new Event('change', {bubbles:true, composed:true}));
643
- }
644
- });
 
645
  }
646
 
647
  function syncImageToGradio() {
@@ -790,7 +904,12 @@ function init() {
790
  showLoader();
791
  setTimeout(() => {
792
  const gradioBtn = document.getElementById('gradio-run-btn');
793
- if (!gradioBtn) return;
 
 
 
 
 
794
  const btn = gradioBtn.querySelector('button');
795
  if (btn) btn.click(); else gradioBtn.click();
796
  }, 180);
@@ -839,34 +958,10 @@ function init() {
839
  });
840
  }
841
 
842
- document.querySelectorAll('.example-card[data-idx]').forEach(card => {
843
- card.addEventListener('click', () => {
844
- const idx = card.getAttribute('data-idx');
845
- document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
846
- card.classList.add('loading');
847
- showToast('Loading example...', 'info');
848
- setGradioValue('example-result-data', '');
849
- setGradioValue('example-idx-input', idx);
850
- setTimeout(() => {
851
- const btn = document.getElementById('example-load-btn');
852
- if (btn) {
853
- const b = btn.querySelector('button');
854
- if (b) b.click(); else btn.click();
855
- }
856
- }, 150);
857
- setTimeout(() => card.classList.remove('loading'), 12000);
858
- });
859
- });
860
-
861
- function checkExampleResult() {
862
- if (!exampleResultContainer) return;
863
- const el = exampleResultContainer.querySelector('textarea') || exampleResultContainer.querySelector('input');
864
- if (!el || !el.value) return;
865
- if (window.__lastExampleVal3 === el.value) return;
866
  try {
867
- const data = JSON.parse(el.value);
868
  if (data.status === 'ok') {
869
- window.__lastExampleVal3 = el.value;
870
  if (data.image) setPreview(data.image, data.name || 'example.jpg');
871
  if (data.query) {
872
  promptInput.value = data.query;
@@ -879,14 +974,88 @@ function init() {
879
  document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
880
  showToast(data.message || 'Failed to load example', 'error');
881
  }
882
- } catch(e) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
  }
884
 
885
- const obsExample = new MutationObserver(checkExampleResult);
886
- if (exampleResultContainer) {
887
- obsExample.observe(exampleResultContainer, {childList:true, subtree:true, characterData:true, attributes:true});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
888
  }
889
- setInterval(checkExampleResult, 500);
890
 
891
  if (outputArea) outputArea.value = '';
892
  const sb = document.getElementById('sb-run-state');
@@ -906,6 +1075,10 @@ function watchOutputs() {
906
 
907
  let lastText = '';
908
 
 
 
 
 
909
  function syncOutput() {
910
  const el = resultContainer.querySelector('textarea') || resultContainer.querySelector('input');
911
  if (!el) return;
@@ -914,7 +1087,15 @@ function watchOutputs() {
914
  lastText = val;
915
  outArea.value = val;
916
  outArea.scrollTop = outArea.scrollHeight;
917
- if (window.__hideLoader && val.trim()) window.__hideLoader();
 
 
 
 
 
 
 
 
918
  }
919
  }
920
 
@@ -1109,33 +1290,6 @@ with gr.Blocks() as demo:
1109
 
1110
  run_btn = gr.Button("Run", elem_id="gradio-run-btn")
1111
 
1112
- def b64_to_pil(b64_str):
1113
- if not b64_str:
1114
- return None
1115
- try:
1116
- if b64_str.startswith("data:image"):
1117
- _, data = b64_str.split(",", 1)
1118
- else:
1119
- data = b64_str
1120
- image_data = base64.b64decode(data)
1121
- return Image.open(BytesIO(image_data)).convert("RGB")
1122
- except Exception:
1123
- return None
1124
-
1125
- def run_ocr(model_name, text, image_b64, max_new_tokens_v, temperature_v, top_p_v, top_k_v, repetition_penalty_v, gpu_timeout_v):
1126
- image = b64_to_pil(image_b64)
1127
- yield from generate_image(
1128
- model_name=model_name,
1129
- text=text,
1130
- image=image,
1131
- max_new_tokens=max_new_tokens_v,
1132
- temperature=temperature_v,
1133
- top_p=top_p_v,
1134
- top_k=top_k_v,
1135
- repetition_penalty=repetition_penalty_v,
1136
- gpu_timeout=gpu_timeout_v,
1137
- )
1138
-
1139
  demo.load(fn=noop, inputs=None, outputs=None, js=gallery_js)
1140
  demo.load(fn=noop, inputs=None, outputs=None, js=wire_outputs_js)
1141
 
 
91
  ]
92
 
93
 
94
+ def select_model(model_name: str):
95
+ if model_name not in MODEL_MAP:
96
+ raise ValueError("Invalid model selected.")
97
+ return MODEL_MAP[model_name]
98
+
99
+
100
  def pil_to_data_url(img: Image.Image, fmt="PNG"):
101
  buf = BytesIO()
102
  img.save(buf, format=fmt)
 
154
 
155
  def load_example_data(idx_str):
156
  try:
157
+ idx = int(str(idx_str).strip())
158
  except Exception:
159
+ return gr.update(value=json.dumps({"status": "error", "message": "Invalid example index"}))
160
+
161
  if idx < 0 or idx >= len(image_examples):
162
+ return gr.update(value=json.dumps({"status": "error", "message": "Example index out of range"}))
163
+
164
  ex = image_examples[idx]
165
  img_b64 = file_to_data_url(ex["image"])
166
  if not img_b64:
167
+ return gr.update(value=json.dumps({"status": "error", "message": "Could not load example image"}))
168
+
169
+ return gr.update(value=json.dumps({
170
  "status": "ok",
171
  "query": ex["query"],
172
  "image": img_b64,
173
  "model": ex["model"],
174
  "name": os.path.basename(ex["image"]),
175
+ }))
176
 
177
 
178
+ def b64_to_pil(b64_str):
179
+ if not b64_str:
180
+ return None
181
+ try:
182
+ if b64_str.startswith("data:"):
183
+ _, data = b64_str.split(",", 1)
184
+ else:
185
+ data = b64_str
186
+ image_data = base64.b64decode(data)
187
+ return Image.open(BytesIO(image_data)).convert("RGB")
188
+ except Exception:
189
+ return None
190
+
191
+
192
+ def calc_timeout_image(*args, **kwargs):
193
+ gpu_timeout = kwargs.get("gpu_timeout", None)
194
+ if gpu_timeout is None and args:
195
+ gpu_timeout = args[-1]
196
  try:
197
  return int(gpu_timeout)
198
  except Exception:
 
201
 
202
  @spaces.GPU(duration=calc_timeout_image)
203
  def generate_image(model_name, text, image, max_new_tokens, temperature, top_p, top_k, repetition_penalty, gpu_timeout=60):
204
+ try:
205
+ if not model_name or model_name not in MODEL_MAP:
206
+ yield "[ERROR] Please select a valid model."
207
+ return
208
+ if image is None:
209
+ yield "[ERROR] Please upload an image."
210
+ return
211
+ if not text or not str(text).strip():
212
+ yield "[ERROR] Please enter your OCR/query instruction."
213
+ return
214
+ if len(str(text)) > MAX_INPUT_TOKEN_LENGTH * 8:
215
+ yield "[ERROR] Query is too long. Please shorten your input."
216
+ return
217
+
218
+ processor, model = select_model(model_name)
219
+
220
+ messages = [{
221
+ "role": "user",
222
+ "content": [
223
+ {"type": "image"},
224
+ {"type": "text", "text": text},
225
+ ]
226
+ }]
227
+
228
+ prompt_full = processor.apply_chat_template(
229
+ messages,
230
+ tokenize=False,
231
+ add_generation_prompt=True
232
+ )
233
+
234
+ inputs = processor(
235
+ text=[prompt_full],
236
+ images=[image],
237
+ return_tensors="pt",
238
+ padding=True,
239
+ truncation=True,
240
+ max_length=MAX_INPUT_TOKEN_LENGTH
241
+ )
242
+
243
+ model_device = getattr(model, "device", None)
244
+ if model_device is None:
245
+ try:
246
+ model_device = next(model.parameters()).device
247
+ except Exception:
248
+ model_device = device
249
+
250
+ inputs = inputs.to(model_device)
251
+
252
+ streamer = TextIteratorStreamer(
253
+ processor.tokenizer if hasattr(processor, "tokenizer") else processor,
254
+ skip_prompt=True,
255
+ skip_special_tokens=True
256
+ )
257
+
258
+ generation_error = {"error": None}
259
+
260
+ generation_kwargs = {
261
+ **inputs,
262
+ "streamer": streamer,
263
+ "max_new_tokens": int(max_new_tokens),
264
+ "do_sample": True,
265
+ "temperature": float(temperature),
266
+ "top_p": float(top_p),
267
+ "top_k": int(top_k),
268
+ "repetition_penalty": float(repetition_penalty),
269
+ }
270
 
271
+ def _run_generation():
272
+ try:
273
+ model.generate(**generation_kwargs)
274
+ except Exception as e:
275
+ generation_error["error"] = e
276
+ try:
277
+ streamer.end()
278
+ except Exception:
279
+ pass
280
+
281
+ thread = Thread(target=_run_generation, daemon=True)
282
+ thread.start()
283
+
284
+ buffer = ""
285
+ for new_text in streamer:
286
+ buffer += new_text.replace("<|im_end|>", "")
287
+ time.sleep(0.01)
288
+ yield buffer
289
+
290
+ thread.join(timeout=1.0)
291
+
292
+ if generation_error["error"] is not None:
293
+ err_msg = f"[ERROR] Inference failed: {str(generation_error['error'])}"
294
+ if buffer.strip():
295
+ yield buffer + "\n\n" + err_msg
296
+ else:
297
+ yield err_msg
298
+ return
299
 
300
+ if not buffer.strip():
301
+ yield "[ERROR] No output was generated."
 
 
 
 
302
 
303
+ except Exception as e:
304
+ yield f"[ERROR] {str(e)}"
305
+ finally:
306
+ gc.collect()
307
+ if torch.cuda.is_available():
308
+ torch.cuda.empty_cache()
309
+
310
+
311
+ def run_ocr(model_name, text, image_b64, max_new_tokens_v, temperature_v, top_p_v, top_k_v, repetition_penalty_v, gpu_timeout_v):
312
+ try:
313
+ image = b64_to_pil(image_b64)
314
+ yield from generate_image(
315
+ model_name=model_name,
316
+ text=text,
317
+ image=image,
318
+ max_new_tokens=max_new_tokens_v,
319
+ temperature=temperature_v,
320
+ top_p=top_p_v,
321
+ top_k=top_k_v,
322
+ repetition_penalty=repetition_penalty_v,
323
+ gpu_timeout=gpu_timeout_v,
324
+ )
325
+ except Exception as e:
326
+ yield f"[ERROR] {str(e)}"
327
 
328
 
329
  def noop():
 
665
  const runBtnEl = document.getElementById('custom-run-btn');
666
  const outputArea = document.getElementById('custom-output-textarea');
667
  const imgStatus = document.getElementById('sb-image-status');
 
668
 
669
  if (!dropZone || !fileInput || !promptInput || !previewWrap || !previewImg) {
670
  setTimeout(init, 250);
 
674
  window.__ocr3InitDone = true;
675
  let imageState = null;
676
  let toastTimer = null;
677
+ let examplePoller = null;
678
+ let lastSeenExamplePayload = null;
679
 
680
  function showToast(message, type) {
681
  let toast = document.getElementById('app-toast');
 
698
  toast.classList.add('visible');
699
  toastTimer = setTimeout(() => toast.classList.remove('visible'), 3500);
700
  }
 
701
 
702
  function showLoader() {
703
  const l = document.getElementById('output-loader');
 
711
  const sb = document.getElementById('sb-run-state');
712
  if (sb) sb.textContent = 'Done';
713
  }
714
+ function setRunErrorState() {
715
+ const l = document.getElementById('output-loader');
716
+ if (l) l.classList.remove('active');
717
+ const sb = document.getElementById('sb-run-state');
718
+ if (sb) sb.textContent = 'Error';
719
+ }
720
+
721
+ window.__showToast = showToast;
722
  window.__showLoader = showLoader;
723
  window.__hideLoader = hideLoader;
724
+ window.__setRunErrorState = setRunErrorState;
725
 
726
  function flashPromptError() {
727
  promptInput.classList.add('error-flash');
 
735
  setTimeout(() => outputArea.classList.remove('error-flash'), 800);
736
  }
737
 
738
+ function getValueFromContainer(containerId) {
739
+ const container = document.getElementById(containerId);
740
+ if (!container) return '';
741
+ const el = container.querySelector('textarea, input');
742
+ return el ? (el.value || '') : '';
743
+ }
744
+
745
  function setGradioValue(containerId, value) {
746
  const container = document.getElementById(containerId);
747
+ if (!container) return false;
748
+ const el = container.querySelector('textarea, input');
749
+ if (!el) return false;
750
+ const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
751
+ const ns = Object.getOwnPropertyDescriptor(proto, 'value');
752
+ if (ns && ns.set) {
753
+ ns.set.call(el, value);
754
+ el.dispatchEvent(new Event('input', {bubbles:true, composed:true}));
755
+ el.dispatchEvent(new Event('change', {bubbles:true, composed:true}));
756
+ return true;
757
+ }
758
+ return false;
759
  }
760
 
761
  function syncImageToGradio() {
 
904
  showLoader();
905
  setTimeout(() => {
906
  const gradioBtn = document.getElementById('gradio-run-btn');
907
+ if (!gradioBtn) {
908
+ setRunErrorState();
909
+ if (outputArea) outputArea.value = '[ERROR] Run button not found.';
910
+ showToast('Run button not found', 'error');
911
+ return;
912
+ }
913
  const btn = gradioBtn.querySelector('button');
914
  if (btn) btn.click(); else gradioBtn.click();
915
  }, 180);
 
958
  });
959
  }
960
 
961
+ function applyExamplePayload(raw) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
962
  try {
963
+ const data = JSON.parse(raw);
964
  if (data.status === 'ok') {
 
965
  if (data.image) setPreview(data.image, data.name || 'example.jpg');
966
  if (data.query) {
967
  promptInput.value = data.query;
 
974
  document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
975
  showToast(data.message || 'Failed to load example', 'error');
976
  }
977
+ } catch (e) {
978
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
979
+ }
980
+ }
981
+
982
+ function startExamplePolling() {
983
+ if (examplePoller) clearInterval(examplePoller);
984
+ let attempts = 0;
985
+ examplePoller = setInterval(() => {
986
+ attempts += 1;
987
+ const current = getValueFromContainer('example-result-data');
988
+ if (current && current !== lastSeenExamplePayload) {
989
+ lastSeenExamplePayload = current;
990
+ clearInterval(examplePoller);
991
+ examplePoller = null;
992
+ applyExamplePayload(current);
993
+ return;
994
+ }
995
+ if (attempts >= 100) {
996
+ clearInterval(examplePoller);
997
+ examplePoller = null;
998
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
999
+ showToast('Example load timed out', 'error');
1000
+ }
1001
+ }, 120);
1002
  }
1003
 
1004
+ function triggerExampleLoad(idx) {
1005
+ const btnWrap = document.getElementById('example-load-btn');
1006
+ const btn = btnWrap ? (btnWrap.querySelector('button') || btnWrap) : null;
1007
+ if (!btn) return;
1008
+
1009
+ let attempts = 0;
1010
+
1011
+ function writeIdxAndClick() {
1012
+ attempts += 1;
1013
+ const ok1 = setGradioValue('example-idx-input', String(idx));
1014
+ setGradioValue('example-result-data', '');
1015
+ const currentVal = getValueFromContainer('example-idx-input');
1016
+
1017
+ if (ok1 && currentVal === String(idx)) {
1018
+ btn.click();
1019
+ startExamplePolling();
1020
+ return;
1021
+ }
1022
+
1023
+ if (attempts < 30) {
1024
+ setTimeout(writeIdxAndClick, 100);
1025
+ } else {
1026
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
1027
+ showToast('Failed to initialize example loader', 'error');
1028
+ }
1029
+ }
1030
+
1031
+ writeIdxAndClick();
1032
+ }
1033
+
1034
+ document.querySelectorAll('.example-card[data-idx]').forEach(card => {
1035
+ card.addEventListener('click', () => {
1036
+ const idx = card.getAttribute('data-idx');
1037
+ if (idx === null || idx === undefined || idx === '') return;
1038
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
1039
+ card.classList.add('loading');
1040
+ showToast('Loading example...', 'info');
1041
+ triggerExampleLoad(idx);
1042
+ });
1043
+ });
1044
+
1045
+ const observerTarget = document.getElementById('example-result-data');
1046
+ if (observerTarget) {
1047
+ const obs = new MutationObserver(() => {
1048
+ const current = getValueFromContainer('example-result-data');
1049
+ if (!current || current === lastSeenExamplePayload) return;
1050
+ lastSeenExamplePayload = current;
1051
+ if (examplePoller) {
1052
+ clearInterval(examplePoller);
1053
+ examplePoller = null;
1054
+ }
1055
+ applyExamplePayload(current);
1056
+ });
1057
+ obs.observe(observerTarget, {childList:true, subtree:true, characterData:true, attributes:true});
1058
  }
 
1059
 
1060
  if (outputArea) outputArea.value = '';
1061
  const sb = document.getElementById('sb-run-state');
 
1075
 
1076
  let lastText = '';
1077
 
1078
+ function isErrorText(val) {
1079
+ return typeof val === 'string' && val.trim().startsWith('[ERROR]');
1080
+ }
1081
+
1082
  function syncOutput() {
1083
  const el = resultContainer.querySelector('textarea') || resultContainer.querySelector('input');
1084
  if (!el) return;
 
1087
  lastText = val;
1088
  outArea.value = val;
1089
  outArea.scrollTop = outArea.scrollHeight;
1090
+
1091
+ if (val.trim()) {
1092
+ if (isErrorText(val)) {
1093
+ if (window.__setRunErrorState) window.__setRunErrorState();
1094
+ if (window.__showToast) window.__showToast('Inference failed', 'error');
1095
+ } else {
1096
+ if (window.__hideLoader) window.__hideLoader();
1097
+ }
1098
+ }
1099
  }
1100
  }
1101
 
 
1290
 
1291
  run_btn = gr.Button("Run", elem_id="gradio-run-btn")
1292
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1293
  demo.load(fn=noop, inputs=None, outputs=None, js=gallery_js)
1294
  demo.load(fn=noop, inputs=None, outputs=None, js=wire_outputs_js)
1295