prithivMLmods commited on
Commit
45cc183
·
verified ·
1 Parent(s): 3ba4705

update app

Browse files
Files changed (1) hide show
  1. app.py +260 -132
app.py CHANGED
@@ -98,6 +98,12 @@ image_examples = [
98
  ]
99
 
100
 
 
 
 
 
 
 
101
  def pil_to_data_url(img: Image.Image, fmt="PNG"):
102
  buf = BytesIO()
103
  img.save(buf, format=fmt)
@@ -155,25 +161,31 @@ EXAMPLE_CARDS_HTML = build_example_cards_html()
155
 
156
  def load_example_data(idx_str):
157
  try:
158
- idx = int(float(idx_str))
159
  except Exception:
160
- return json.dumps({"status": "error", "message": "Invalid example index"})
 
161
  if idx < 0 or idx >= len(image_examples):
162
- return json.dumps({"status": "error", "message": "Example index out of range"})
 
163
  ex = image_examples[idx]
164
  img_b64 = file_to_data_url(ex["image"])
165
  if not img_b64:
166
- return json.dumps({"status": "error", "message": "Could not load example image"})
167
- return json.dumps({
 
168
  "status": "ok",
169
  "query": ex["query"],
170
  "image": img_b64,
171
  "model": ex["model"],
172
  "name": os.path.basename(ex["image"]),
173
- })
174
 
175
 
176
- def calc_timeout_duration(model_name, text, image, max_new_tokens, temperature, top_p, top_k, repetition_penalty, gpu_timeout):
 
 
 
177
  try:
178
  return int(gpu_timeout)
179
  except Exception:
@@ -182,74 +194,103 @@ def calc_timeout_duration(model_name, text, image, max_new_tokens, temperature,
182
 
183
  @spaces.GPU(duration=calc_timeout_duration)
184
  def generate_image(model_name, text, image, max_new_tokens, temperature, top_p, top_k, repetition_penalty, gpu_timeout):
185
- if not model_name or model_name not in MODEL_MAP:
186
- raise gr.Error("Please select a valid model.")
187
- if image is None:
188
- raise gr.Error("Please upload an image.")
189
- if not text or not str(text).strip():
190
- raise gr.Error("Please enter your OCR/query instruction.")
191
- if len(str(text)) > MAX_INPUT_TOKEN_LENGTH * 8:
192
- raise gr.Error("Query is too long. Please shorten your input.")
193
-
194
- processor, model = MODEL_MAP[model_name]
195
-
196
- messages = [{
197
- "role": "user",
198
- "content": [
199
- {"type": "image"},
200
- {"type": "text", "text": text},
201
- ]
202
- }]
203
- prompt_full = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
204
-
205
- inputs = processor(
206
- text=[prompt_full],
207
- images=[image],
208
- return_tensors="pt",
209
- padding=True,
210
- truncation=True,
211
- max_length=MAX_INPUT_TOKEN_LENGTH
212
- ).to(device)
213
-
214
- tokenizer = processor.tokenizer if hasattr(processor, "tokenizer") else processor
215
- streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
216
-
217
- generation_kwargs = {
218
- **inputs,
219
- "streamer": streamer,
220
- "max_new_tokens": int(max_new_tokens),
221
- "do_sample": True,
222
- "temperature": float(temperature),
223
- "top_p": float(top_p),
224
- "top_k": int(top_k),
225
- "repetition_penalty": float(repetition_penalty),
226
- }
227
-
228
- err_box = {"error": None}
229
-
230
- def _worker():
231
- try:
232
- model.generate(**generation_kwargs)
233
- except Exception as e:
234
- err_box["error"] = e
235
-
236
- thread = Thread(target=_worker)
237
- thread.start()
238
-
239
- buffer = ""
240
- for new_text in streamer:
241
- buffer += new_text
242
- buffer = buffer.replace("<|im_end|>", "")
243
- time.sleep(0.01)
244
- yield buffer
245
 
246
- thread.join()
247
- if err_box["error"] is not None:
248
- raise gr.Error(f"Generation failed: {err_box['error']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
- gc.collect()
251
- if torch.cuda.is_available():
252
- torch.cuda.empty_cache()
 
 
 
253
 
254
 
255
  def noop():
@@ -260,7 +301,7 @@ def b64_to_pil(b64_str):
260
  if not b64_str:
261
  return None
262
  try:
263
- if b64_str.startswith("data:image"):
264
  _, data = b64_str.split(",", 1)
265
  else:
266
  data = b64_str
@@ -271,18 +312,21 @@ def b64_to_pil(b64_str):
271
 
272
 
273
  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):
274
- image = b64_to_pil(image_b64)
275
- yield from generate_image(
276
- model_name=model_name,
277
- text=text,
278
- image=image,
279
- max_new_tokens=max_new_tokens_v,
280
- temperature=temperature_v,
281
- top_p=top_p_v,
282
- top_k=top_k_v,
283
- repetition_penalty=repetition_penalty_v,
284
- gpu_timeout=gpu_timeout_v,
285
- )
 
 
 
286
 
287
 
288
  css = r"""
@@ -613,7 +657,6 @@ function init() {
613
  const runBtnEl = document.getElementById('custom-run-btn');
614
  const outputArea = document.getElementById('custom-output-textarea');
615
  const imgStatus = document.getElementById('sb-image-status');
616
- const exampleResultContainer = document.getElementById('example-result-data');
617
 
618
  if (!dropZone || !fileInput || !promptInput || !previewWrap || !previewImg) {
619
  setTimeout(init, 250);
@@ -623,6 +666,8 @@ function init() {
623
  window.__coreOcrPinkInitDone = true;
624
  let imageState = null;
625
  let toastTimer = null;
 
 
626
 
627
  function showToast(message, type) {
628
  let toast = document.getElementById('app-toast');
@@ -645,7 +690,6 @@ function init() {
645
  toast.classList.add('visible');
646
  toastTimer = setTimeout(() => toast.classList.remove('visible'), 3500);
647
  }
648
- window.__showToast = showToast;
649
 
650
  function showLoader() {
651
  const l = document.getElementById('output-loader');
@@ -659,8 +703,17 @@ function init() {
659
  const sb = document.getElementById('sb-run-state');
660
  if (sb) sb.textContent = 'Done';
661
  }
 
 
 
 
 
 
 
 
662
  window.__showLoader = showLoader;
663
  window.__hideLoader = hideLoader;
 
664
 
665
  function flashPromptError() {
666
  promptInput.classList.add('error-flash');
@@ -674,19 +727,27 @@ function init() {
674
  setTimeout(() => outputArea.classList.remove('error-flash'), 800);
675
  }
676
 
 
 
 
 
 
 
 
677
  function setGradioValue(containerId, value) {
678
  const container = document.getElementById(containerId);
679
- if (!container) return;
680
- container.querySelectorAll('input, textarea').forEach(el => {
681
- if (el.type === 'file' || el.type === 'range' || el.type === 'checkbox') return;
682
- const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
683
- const ns = Object.getOwnPropertyDescriptor(proto, 'value');
684
- if (ns && ns.set) {
685
- ns.set.call(el, value);
686
- el.dispatchEvent(new Event('input', {bubbles:true, composed:true}));
687
- el.dispatchEvent(new Event('change', {bubbles:true, composed:true}));
688
- }
689
- });
 
690
  }
691
 
692
  function syncImageToGradio() {
@@ -842,7 +903,12 @@ function init() {
842
  showLoader();
843
  setTimeout(() => {
844
  const gradioBtn = document.getElementById('gradio-run-btn');
845
- if (!gradioBtn) return;
 
 
 
 
 
846
  const btn = gradioBtn.querySelector('button');
847
  if (btn) btn.click(); else gradioBtn.click();
848
  }, 180);
@@ -891,34 +957,10 @@ function init() {
891
  });
892
  }
893
 
894
- document.querySelectorAll('.example-card[data-idx]').forEach(card => {
895
- card.addEventListener('click', () => {
896
- const idx = card.getAttribute('data-idx');
897
- document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
898
- card.classList.add('loading');
899
- showToast('Loading example...', 'info');
900
- setGradioValue('example-result-data', '');
901
- setGradioValue('example-idx-input', idx);
902
- setTimeout(() => {
903
- const btn = document.getElementById('example-load-btn');
904
- if (btn) {
905
- const b = btn.querySelector('button');
906
- if (b) b.click(); else btn.click();
907
- }
908
- }, 150);
909
- setTimeout(() => card.classList.remove('loading'), 12000);
910
- });
911
- });
912
-
913
- function checkExampleResult() {
914
- if (!exampleResultContainer) return;
915
- const el = exampleResultContainer.querySelector('textarea') || exampleResultContainer.querySelector('input');
916
- if (!el || !el.value) return;
917
- if (window.__lastExampleValCore === el.value) return;
918
  try {
919
- const data = JSON.parse(el.value);
920
  if (data.status === 'ok') {
921
- window.__lastExampleValCore = el.value;
922
  if (data.image) setPreview(data.image, data.name || 'example.jpg');
923
  if (data.query) {
924
  promptInput.value = data.query;
@@ -931,14 +973,88 @@ function init() {
931
  document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
932
  showToast(data.message || 'Failed to load example', 'error');
933
  }
934
- } catch(e) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
935
  }
936
 
937
- const obsExample = new MutationObserver(checkExampleResult);
938
- if (exampleResultContainer) {
939
- obsExample.observe(exampleResultContainer, {childList:true, subtree:true, characterData:true, attributes:true});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
940
  }
941
- setInterval(checkExampleResult, 500);
942
 
943
  if (outputArea) outputArea.value = '';
944
  const sb = document.getElementById('sb-run-state');
@@ -958,6 +1074,10 @@ function watchOutputs() {
958
 
959
  let lastText = '';
960
 
 
 
 
 
961
  function syncOutput() {
962
  const el = resultContainer.querySelector('textarea') || resultContainer.querySelector('input');
963
  if (!el) return;
@@ -966,7 +1086,15 @@ function watchOutputs() {
966
  lastText = val;
967
  outArea.value = val;
968
  outArea.scrollTop = outArea.scrollHeight;
969
- if (window.__hideLoader && val.trim()) window.__hideLoader();
 
 
 
 
 
 
 
 
970
  }
971
  }
972
 
 
98
  ]
99
 
100
 
101
+ def select_model(model_name: str):
102
+ if model_name not in MODEL_MAP:
103
+ raise ValueError("Invalid model selected.")
104
+ return MODEL_MAP[model_name]
105
+
106
+
107
  def pil_to_data_url(img: Image.Image, fmt="PNG"):
108
  buf = BytesIO()
109
  img.save(buf, format=fmt)
 
161
 
162
  def load_example_data(idx_str):
163
  try:
164
+ idx = int(str(idx_str).strip())
165
  except Exception:
166
+ return gr.update(value=json.dumps({"status": "error", "message": "Invalid example index"}))
167
+
168
  if idx < 0 or idx >= len(image_examples):
169
+ return gr.update(value=json.dumps({"status": "error", "message": "Example index out of range"}))
170
+
171
  ex = image_examples[idx]
172
  img_b64 = file_to_data_url(ex["image"])
173
  if not img_b64:
174
+ return gr.update(value=json.dumps({"status": "error", "message": "Could not load example image"}))
175
+
176
+ return gr.update(value=json.dumps({
177
  "status": "ok",
178
  "query": ex["query"],
179
  "image": img_b64,
180
  "model": ex["model"],
181
  "name": os.path.basename(ex["image"]),
182
+ }))
183
 
184
 
185
+ def calc_timeout_duration(*args, **kwargs):
186
+ gpu_timeout = kwargs.get("gpu_timeout", None)
187
+ if gpu_timeout is None and args:
188
+ gpu_timeout = args[-1]
189
  try:
190
  return int(gpu_timeout)
191
  except Exception:
 
194
 
195
  @spaces.GPU(duration=calc_timeout_duration)
196
  def generate_image(model_name, text, image, max_new_tokens, temperature, top_p, top_k, repetition_penalty, gpu_timeout):
197
+ try:
198
+ if not model_name or model_name not in MODEL_MAP:
199
+ yield "[ERROR] Please select a valid model."
200
+ return
201
+ if image is None:
202
+ yield "[ERROR] Please upload an image."
203
+ return
204
+ if not text or not str(text).strip():
205
+ yield "[ERROR] Please enter your OCR/query instruction."
206
+ return
207
+ if len(str(text)) > MAX_INPUT_TOKEN_LENGTH * 8:
208
+ yield "[ERROR] Query is too long. Please shorten your input."
209
+ return
210
+
211
+ processor, model = select_model(model_name)
212
+
213
+ messages = [{
214
+ "role": "user",
215
+ "content": [
216
+ {"type": "image"},
217
+ {"type": "text", "text": text},
218
+ ]
219
+ }]
220
+
221
+ prompt_full = processor.apply_chat_template(
222
+ messages,
223
+ tokenize=False,
224
+ add_generation_prompt=True
225
+ )
226
+
227
+ inputs = processor(
228
+ text=[prompt_full],
229
+ images=[image],
230
+ return_tensors="pt",
231
+ padding=True,
232
+ truncation=True,
233
+ max_length=MAX_INPUT_TOKEN_LENGTH
234
+ ).to(device)
235
+
236
+ tokenizer = processor.tokenizer if hasattr(processor, "tokenizer") else processor
237
+ streamer = TextIteratorStreamer(
238
+ tokenizer,
239
+ skip_prompt=True,
240
+ skip_special_tokens=True
241
+ )
242
+
243
+ err_box = {"error": None}
244
+
245
+ generation_kwargs = {
246
+ **inputs,
247
+ "streamer": streamer,
248
+ "max_new_tokens": int(max_new_tokens),
249
+ "do_sample": True,
250
+ "temperature": float(temperature),
251
+ "top_p": float(top_p),
252
+ "top_k": int(top_k),
253
+ "repetition_penalty": float(repetition_penalty),
254
+ }
 
 
255
 
256
+ def _worker():
257
+ try:
258
+ model.generate(**generation_kwargs)
259
+ except Exception as e:
260
+ err_box["error"] = e
261
+ try:
262
+ streamer.end()
263
+ except Exception:
264
+ pass
265
+
266
+ thread = Thread(target=_worker, daemon=True)
267
+ thread.start()
268
+
269
+ buffer = ""
270
+ for new_text in streamer:
271
+ buffer += new_text.replace("<|im_end|>", "")
272
+ time.sleep(0.01)
273
+ yield buffer
274
+
275
+ thread.join(timeout=1.0)
276
+
277
+ if err_box["error"] is not None:
278
+ err_msg = f"[ERROR] Inference failed: {str(err_box['error'])}"
279
+ if buffer.strip():
280
+ yield buffer + "\n\n" + err_msg
281
+ else:
282
+ yield err_msg
283
+ return
284
+
285
+ if not buffer.strip():
286
+ yield "[ERROR] No output was generated."
287
 
288
+ except Exception as e:
289
+ yield f"[ERROR] {str(e)}"
290
+ finally:
291
+ gc.collect()
292
+ if torch.cuda.is_available():
293
+ torch.cuda.empty_cache()
294
 
295
 
296
  def noop():
 
301
  if not b64_str:
302
  return None
303
  try:
304
+ if b64_str.startswith("data:"):
305
  _, data = b64_str.split(",", 1)
306
  else:
307
  data = b64_str
 
312
 
313
 
314
  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):
315
+ try:
316
+ image = b64_to_pil(image_b64)
317
+ yield from generate_image(
318
+ model_name=model_name,
319
+ text=text,
320
+ image=image,
321
+ max_new_tokens=max_new_tokens_v,
322
+ temperature=temperature_v,
323
+ top_p=top_p_v,
324
+ top_k=top_k_v,
325
+ repetition_penalty=repetition_penalty_v,
326
+ gpu_timeout=gpu_timeout_v,
327
+ )
328
+ except Exception as e:
329
+ yield f"[ERROR] {str(e)}"
330
 
331
 
332
  css = r"""
 
657
  const runBtnEl = document.getElementById('custom-run-btn');
658
  const outputArea = document.getElementById('custom-output-textarea');
659
  const imgStatus = document.getElementById('sb-image-status');
 
660
 
661
  if (!dropZone || !fileInput || !promptInput || !previewWrap || !previewImg) {
662
  setTimeout(init, 250);
 
666
  window.__coreOcrPinkInitDone = true;
667
  let imageState = null;
668
  let toastTimer = null;
669
+ let examplePoller = null;
670
+ let lastSeenExamplePayload = null;
671
 
672
  function showToast(message, type) {
673
  let toast = document.getElementById('app-toast');
 
690
  toast.classList.add('visible');
691
  toastTimer = setTimeout(() => toast.classList.remove('visible'), 3500);
692
  }
 
693
 
694
  function showLoader() {
695
  const l = document.getElementById('output-loader');
 
703
  const sb = document.getElementById('sb-run-state');
704
  if (sb) sb.textContent = 'Done';
705
  }
706
+ function setRunErrorState() {
707
+ const l = document.getElementById('output-loader');
708
+ if (l) l.classList.remove('active');
709
+ const sb = document.getElementById('sb-run-state');
710
+ if (sb) sb.textContent = 'Error';
711
+ }
712
+
713
+ window.__showToast = showToast;
714
  window.__showLoader = showLoader;
715
  window.__hideLoader = hideLoader;
716
+ window.__setRunErrorState = setRunErrorState;
717
 
718
  function flashPromptError() {
719
  promptInput.classList.add('error-flash');
 
727
  setTimeout(() => outputArea.classList.remove('error-flash'), 800);
728
  }
729
 
730
+ function getValueFromContainer(containerId) {
731
+ const container = document.getElementById(containerId);
732
+ if (!container) return '';
733
+ const el = container.querySelector('textarea, input');
734
+ return el ? (el.value || '') : '';
735
+ }
736
+
737
  function setGradioValue(containerId, value) {
738
  const container = document.getElementById(containerId);
739
+ if (!container) return false;
740
+ const el = container.querySelector('textarea, input');
741
+ if (!el) return false;
742
+ const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
743
+ const ns = Object.getOwnPropertyDescriptor(proto, 'value');
744
+ if (ns && ns.set) {
745
+ ns.set.call(el, value);
746
+ el.dispatchEvent(new Event('input', {bubbles:true, composed:true}));
747
+ el.dispatchEvent(new Event('change', {bubbles:true, composed:true}));
748
+ return true;
749
+ }
750
+ return false;
751
  }
752
 
753
  function syncImageToGradio() {
 
903
  showLoader();
904
  setTimeout(() => {
905
  const gradioBtn = document.getElementById('gradio-run-btn');
906
+ if (!gradioBtn) {
907
+ setRunErrorState();
908
+ if (outputArea) outputArea.value = '[ERROR] Run button not found.';
909
+ showToast('Run button not found', 'error');
910
+ return;
911
+ }
912
  const btn = gradioBtn.querySelector('button');
913
  if (btn) btn.click(); else gradioBtn.click();
914
  }, 180);
 
957
  });
958
  }
959
 
960
+ function applyExamplePayload(raw) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
961
  try {
962
+ const data = JSON.parse(raw);
963
  if (data.status === 'ok') {
 
964
  if (data.image) setPreview(data.image, data.name || 'example.jpg');
965
  if (data.query) {
966
  promptInput.value = data.query;
 
973
  document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
974
  showToast(data.message || 'Failed to load example', 'error');
975
  }
976
+ } catch (e) {
977
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
978
+ }
979
+ }
980
+
981
+ function startExamplePolling() {
982
+ if (examplePoller) clearInterval(examplePoller);
983
+ let attempts = 0;
984
+ examplePoller = setInterval(() => {
985
+ attempts += 1;
986
+ const current = getValueFromContainer('example-result-data');
987
+ if (current && current !== lastSeenExamplePayload) {
988
+ lastSeenExamplePayload = current;
989
+ clearInterval(examplePoller);
990
+ examplePoller = null;
991
+ applyExamplePayload(current);
992
+ return;
993
+ }
994
+ if (attempts >= 100) {
995
+ clearInterval(examplePoller);
996
+ examplePoller = null;
997
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
998
+ showToast('Example load timed out', 'error');
999
+ }
1000
+ }, 120);
1001
+ }
1002
+
1003
+ function triggerExampleLoad(idx) {
1004
+ const btnWrap = document.getElementById('example-load-btn');
1005
+ const btn = btnWrap ? (btnWrap.querySelector('button') || btnWrap) : null;
1006
+ if (!btn) return;
1007
+
1008
+ let attempts = 0;
1009
+
1010
+ function writeIdxAndClick() {
1011
+ attempts += 1;
1012
+ const ok1 = setGradioValue('example-idx-input', String(idx));
1013
+ setGradioValue('example-result-data', '');
1014
+ const currentVal = getValueFromContainer('example-idx-input');
1015
+
1016
+ if (ok1 && currentVal === String(idx)) {
1017
+ btn.click();
1018
+ startExamplePolling();
1019
+ return;
1020
+ }
1021
+
1022
+ if (attempts < 30) {
1023
+ setTimeout(writeIdxAndClick, 100);
1024
+ } else {
1025
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
1026
+ showToast('Failed to initialize example loader', 'error');
1027
+ }
1028
+ }
1029
+
1030
+ writeIdxAndClick();
1031
  }
1032
 
1033
+ document.querySelectorAll('.example-card[data-idx]').forEach(card => {
1034
+ card.addEventListener('click', () => {
1035
+ const idx = card.getAttribute('data-idx');
1036
+ if (idx === null || idx === undefined || idx === '') return;
1037
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
1038
+ card.classList.add('loading');
1039
+ showToast('Loading example...', 'info');
1040
+ triggerExampleLoad(idx);
1041
+ });
1042
+ });
1043
+
1044
+ const observerTarget = document.getElementById('example-result-data');
1045
+ if (observerTarget) {
1046
+ const obs = new MutationObserver(() => {
1047
+ const current = getValueFromContainer('example-result-data');
1048
+ if (!current || current === lastSeenExamplePayload) return;
1049
+ lastSeenExamplePayload = current;
1050
+ if (examplePoller) {
1051
+ clearInterval(examplePoller);
1052
+ examplePoller = null;
1053
+ }
1054
+ applyExamplePayload(current);
1055
+ });
1056
+ obs.observe(observerTarget, {childList:true, subtree:true, characterData:true, attributes:true});
1057
  }
 
1058
 
1059
  if (outputArea) outputArea.value = '';
1060
  const sb = document.getElementById('sb-run-state');
 
1074
 
1075
  let lastText = '';
1076
 
1077
+ function isErrorText(val) {
1078
+ return typeof val === 'string' && val.trim().startsWith('[ERROR]');
1079
+ }
1080
+
1081
  function syncOutput() {
1082
  const el = resultContainer.querySelector('textarea') || resultContainer.querySelector('input');
1083
  if (!el) return;
 
1086
  lastText = val;
1087
  outArea.value = val;
1088
  outArea.scrollTop = outArea.scrollHeight;
1089
+
1090
+ if (val.trim()) {
1091
+ if (isErrorText(val)) {
1092
+ if (window.__setRunErrorState) window.__setRunErrorState();
1093
+ if (window.__showToast) window.__showToast('Inference failed', 'error');
1094
+ } else {
1095
+ if (window.__hideLoader) window.__hideLoader();
1096
+ }
1097
+ }
1098
  }
1099
  }
1100