prithivMLmods commited on
Commit
e22766b
·
verified ·
1 Parent(s): c59d793

update app

Browse files
Files changed (1) hide show
  1. app.py +312 -152
app.py CHANGED
@@ -101,6 +101,12 @@ image_examples = [
101
  ]
102
 
103
 
 
 
 
 
 
 
104
  def pil_to_data_url(img: Image.Image, fmt="PNG"):
105
  buf = BytesIO()
106
  img.save(buf, format=fmt)
@@ -158,85 +164,167 @@ EXAMPLE_CARDS_HTML = build_example_cards_html()
158
 
159
  def load_example_data(idx_str):
160
  try:
161
- idx = int(float(idx_str))
162
  except Exception:
163
- return json.dumps({"status": "error", "message": "Invalid example index"})
 
164
  if idx < 0 or idx >= len(image_examples):
165
- return json.dumps({"status": "error", "message": "Example index out of range"})
 
166
  ex = image_examples[idx]
167
  img_b64 = file_to_data_url(ex["image"])
168
  if not img_b64:
169
- return json.dumps({"status": "error", "message": "Could not load example image"})
170
- return json.dumps({
 
171
  "status": "ok",
172
  "query": ex["query"],
173
  "image": img_b64,
174
  "model": ex["model"],
175
  "name": os.path.basename(ex["image"]),
176
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
 
179
- def calc_timeout_duration(model_name, text, image, max_new_tokens, temperature, top_p, top_k, repetition_penalty, gpu_timeout):
 
 
 
180
  try:
181
  return int(gpu_timeout)
182
  except Exception:
183
  return 60
184
 
185
 
186
- @spaces.GPU(duration=calc_timeout_duration)
187
- def generate_image(model_name, text, image, max_new_tokens, temperature, top_p, top_k, repetition_penalty, gpu_timeout):
188
- if not model_name or model_name not in MODEL_MAP:
189
- raise gr.Error("Please select a valid model.")
190
- if image is None:
191
- raise gr.Error("Please upload an image.")
192
- if not text or not str(text).strip():
193
- raise gr.Error("Please enter your OCR/query instruction.")
194
- if len(str(text)) > MAX_INPUT_TOKEN_LENGTH * 8:
195
- raise gr.Error("Query is too long. Please shorten your input.")
196
-
197
- processor, model = MODEL_MAP[model_name]
198
-
199
- messages = [{
200
- "role": "user",
201
- "content": [
202
- {"type": "image"},
203
- {"type": "text", "text": text},
204
- ]
205
- }]
206
- prompt_full = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
207
-
208
- inputs = processor(
209
- text=[prompt_full],
210
- images=[image],
211
- return_tensors="pt",
212
- padding=True
213
- ).to(device)
214
-
215
- streamer = TextIteratorStreamer(processor, skip_prompt=True, skip_special_tokens=True)
216
- generation_kwargs = {
217
- **inputs,
218
- "streamer": streamer,
219
- "max_new_tokens": int(max_new_tokens),
220
- "do_sample": True,
221
- "temperature": float(temperature),
222
- "top_p": float(top_p),
223
- "top_k": int(top_k),
224
- "repetition_penalty": float(repetition_penalty),
225
- }
226
 
227
- thread = Thread(target=model.generate, kwargs=generation_kwargs)
228
- thread.start()
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
- buffer = ""
231
- for new_text in streamer:
232
- buffer += new_text
233
- buffer = buffer.replace("<|im_end|>", "")
234
- time.sleep(0.01)
235
- yield buffer
 
 
 
 
 
 
236
 
237
- gc.collect()
238
- if torch.cuda.is_available():
239
- torch.cuda.empty_cache()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
 
242
  def noop():
@@ -578,7 +666,6 @@ function init() {
578
  const runBtnEl = document.getElementById('custom-run-btn');
579
  const outputArea = document.getElementById('custom-output-textarea');
580
  const imgStatus = document.getElementById('sb-image-status');
581
- const exampleResultContainer = document.getElementById('example-result-data');
582
 
583
  if (!dropZone || !fileInput || !promptInput || !previewWrap || !previewImg) {
584
  setTimeout(init, 250);
@@ -588,6 +675,8 @@ function init() {
588
  window.__ocr2GreenInitDone = true;
589
  let imageState = null;
590
  let toastTimer = null;
 
 
591
 
592
  function showToast(message, type) {
593
  let toast = document.getElementById('app-toast');
@@ -610,7 +699,6 @@ function init() {
610
  toast.classList.add('visible');
611
  toastTimer = setTimeout(() => toast.classList.remove('visible'), 3500);
612
  }
613
- window.__showToast = showToast;
614
 
615
  function showLoader() {
616
  const l = document.getElementById('output-loader');
@@ -624,8 +712,17 @@ function init() {
624
  const sb = document.getElementById('sb-run-state');
625
  if (sb) sb.textContent = 'Done';
626
  }
 
 
 
 
 
 
 
 
627
  window.__showLoader = showLoader;
628
  window.__hideLoader = hideLoader;
 
629
 
630
  function flashPromptError() {
631
  promptInput.classList.add('error-flash');
@@ -639,19 +736,27 @@ function init() {
639
  setTimeout(() => outputArea.classList.remove('error-flash'), 800);
640
  }
641
 
 
 
 
 
 
 
 
642
  function setGradioValue(containerId, value) {
643
  const container = document.getElementById(containerId);
644
- if (!container) return;
645
- container.querySelectorAll('input, textarea').forEach(el => {
646
- if (el.type === 'file' || el.type === 'range' || el.type === 'checkbox') return;
647
- const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
648
- const ns = Object.getOwnPropertyDescriptor(proto, 'value');
649
- if (ns && ns.set) {
650
- ns.set.call(el, value);
651
- el.dispatchEvent(new Event('input', {bubbles:true, composed:true}));
652
- el.dispatchEvent(new Event('change', {bubbles:true, composed:true}));
653
- }
654
- });
 
655
  }
656
 
657
  function syncImageToGradio() {
@@ -668,21 +773,31 @@ function init() {
668
  setGradioValue('hidden-model-name', name);
669
  }
670
 
671
- function setPreview(b64, name) {
672
- imageState = {b64, name: name || 'image'};
673
- previewImg.src = b64;
 
 
 
 
 
 
 
674
  previewWrap.style.display = 'flex';
675
  if (uploadPrompt) uploadPrompt.style.display = 'none';
 
 
676
  syncImageToGradio();
677
  }
678
- window.__setPreview = setPreview;
 
 
 
 
679
 
680
  function clearPreview() {
681
  imageState = null;
682
- previewImg.src = '';
683
- previewWrap.style.display = 'none';
684
- if (uploadPrompt) uploadPrompt.style.display = 'flex';
685
- syncImageToGradio();
686
  }
687
  window.__clearPreview = clearPreview;
688
 
@@ -693,7 +808,7 @@ function init() {
693
  return;
694
  }
695
  const reader = new FileReader();
696
- reader.onload = (e) => setPreview(e.target.result, file.name);
697
  reader.readAsDataURL(file);
698
  }
699
 
@@ -732,10 +847,7 @@ function init() {
732
  window.__activateModelTab = activateModelTab;
733
 
734
  document.querySelectorAll('.model-tab[data-model]').forEach(btn => {
735
- btn.addEventListener('click', () => {
736
- const model = btn.getAttribute('data-model');
737
- activateModelTab(model);
738
- });
739
  });
740
 
741
  activateModelTab('Nanonets-OCR2-3B');
@@ -800,7 +912,12 @@ function init() {
800
  showLoader();
801
  setTimeout(() => {
802
  const gradioBtn = document.getElementById('gradio-run-btn');
803
- if (!gradioBtn) return;
 
 
 
 
 
804
  const btn = gradioBtn.querySelector('button');
805
  if (btn) btn.click(); else gradioBtn.click();
806
  }, 180);
@@ -849,54 +966,110 @@ function init() {
849
  });
850
  }
851
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
852
  document.querySelectorAll('.example-card[data-idx]').forEach(card => {
853
  card.addEventListener('click', () => {
854
  const idx = card.getAttribute('data-idx');
 
855
  document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
856
  card.classList.add('loading');
857
  showToast('Loading example...', 'info');
858
- setGradioValue('example-result-data', '');
859
- setGradioValue('example-idx-input', idx);
860
- setTimeout(() => {
861
- const btn = document.getElementById('example-load-btn');
862
- if (btn) {
863
- const b = btn.querySelector('button');
864
- if (b) b.click(); else btn.click();
865
- }
866
- }, 150);
867
- setTimeout(() => card.classList.remove('loading'), 12000);
868
  });
869
  });
870
 
871
- function checkExampleResult() {
872
- if (!exampleResultContainer) return;
873
- const el = exampleResultContainer.querySelector('textarea') || exampleResultContainer.querySelector('input');
874
- if (!el || !el.value) return;
875
- if (window.__lastExampleVal2 === el.value) return;
876
- try {
877
- const data = JSON.parse(el.value);
878
- if (data.status === 'ok') {
879
- window.__lastExampleVal2 = el.value;
880
- if (data.image) setPreview(data.image, data.name || 'example.jpg');
881
- if (data.query) {
882
- promptInput.value = data.query;
883
- syncPromptToGradio();
884
- }
885
- if (data.model) activateModelTab(data.model);
886
- document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
887
- showToast('Example loaded', 'info');
888
- } else if (data.status === 'error') {
889
- document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
890
- showToast(data.message || 'Failed to load example', 'error');
891
  }
892
- } catch(e) {}
893
- }
894
-
895
- const obsExample = new MutationObserver(checkExampleResult);
896
- if (exampleResultContainer) {
897
- obsExample.observe(exampleResultContainer, {childList:true, subtree:true, characterData:true, attributes:true});
898
  }
899
- setInterval(checkExampleResult, 500);
900
 
901
  if (outputArea) outputArea.value = '';
902
  const sb = document.getElementById('sb-run-state');
@@ -916,6 +1089,10 @@ function watchOutputs() {
916
 
917
  let lastText = '';
918
 
 
 
 
 
919
  function syncOutput() {
920
  const el = resultContainer.querySelector('textarea') || resultContainer.querySelector('input');
921
  if (!el) return;
@@ -924,7 +1101,15 @@ function watchOutputs() {
924
  lastText = val;
925
  outArea.value = val;
926
  outArea.scrollTop = outArea.scrollHeight;
927
- if (window.__hideLoader && val.trim()) window.__hideLoader();
 
 
 
 
 
 
 
 
928
  }
929
  }
930
 
@@ -1010,7 +1195,7 @@ with gr.Blocks() as demo:
1010
 
1011
  <div id="single-preview-wrap" class="single-preview-wrap">
1012
  <div class="single-preview-card">
1013
- <img id="single-preview-img" src="" alt="Preview">
1014
  <div class="preview-overlay-actions">
1015
  <button id="preview-upload-btn" class="preview-action-btn" title="Replace">Upload</button>
1016
  <button id="preview-clear-btn" class="preview-action-btn" title="Clear">Clear</button>
@@ -1119,33 +1304,6 @@ with gr.Blocks() as demo:
1119
 
1120
  run_btn = gr.Button("Run", elem_id="gradio-run-btn")
1121
 
1122
- def b64_to_pil(b64_str):
1123
- if not b64_str:
1124
- return None
1125
- try:
1126
- if b64_str.startswith("data:image"):
1127
- _, data = b64_str.split(",", 1)
1128
- else:
1129
- data = b64_str
1130
- image_data = base64.b64decode(data)
1131
- return Image.open(BytesIO(image_data)).convert("RGB")
1132
- except Exception:
1133
- return None
1134
-
1135
- 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):
1136
- image = b64_to_pil(image_b64)
1137
- yield from generate_image(
1138
- model_name=model_name,
1139
- text=text,
1140
- image=image,
1141
- max_new_tokens=max_new_tokens_v,
1142
- temperature=temperature_v,
1143
- top_p=top_p_v,
1144
- top_k=top_k_v,
1145
- repetition_penalty=repetition_penalty_v,
1146
- gpu_timeout=gpu_timeout_v,
1147
- )
1148
-
1149
  demo.load(fn=noop, inputs=None, outputs=None, js=gallery_js)
1150
  demo.load(fn=noop, inputs=None, outputs=None, js=wire_outputs_js)
1151
 
@@ -1168,12 +1326,14 @@ with gr.Blocks() as demo:
1168
  const model = modelEl ? modelEl.getAttribute('data-model') : m;
1169
  const promptEl = document.getElementById('custom-query-input');
1170
  const promptVal = promptEl ? promptEl.value : p;
1171
- const imgContainer = document.getElementById('hidden-image-b64');
1172
  let imgVal = img;
 
1173
  if (imgContainer) {
1174
  const inner = imgContainer.querySelector('textarea, input');
1175
  if (inner) imgVal = inner.value;
1176
  }
 
1177
  return [model, promptVal, imgVal, mnt, t, tp, tk, rp, gd];
1178
  }""",
1179
  )
 
101
  ]
102
 
103
 
104
+ def select_model(model_name: str):
105
+ if model_name not in MODEL_MAP:
106
+ raise ValueError("Invalid model selected.")
107
+ return MODEL_MAP[model_name]
108
+
109
+
110
  def pil_to_data_url(img: Image.Image, fmt="PNG"):
111
  buf = BytesIO()
112
  img.save(buf, format=fmt)
 
164
 
165
  def load_example_data(idx_str):
166
  try:
167
+ idx = int(str(idx_str).strip())
168
  except Exception:
169
+ return gr.update(value="")
170
+
171
  if idx < 0 or idx >= len(image_examples):
172
+ return gr.update(value="")
173
+
174
  ex = image_examples[idx]
175
  img_b64 = file_to_data_url(ex["image"])
176
  if not img_b64:
177
+ return gr.update(value=json.dumps({"status": "error", "message": "Could not load example image"}))
178
+
179
+ return gr.update(value=json.dumps({
180
  "status": "ok",
181
  "query": ex["query"],
182
  "image": img_b64,
183
  "model": ex["model"],
184
  "name": os.path.basename(ex["image"]),
185
+ }))
186
+
187
+
188
+ def b64_to_pil(b64_str):
189
+ if not b64_str:
190
+ return None
191
+ try:
192
+ if b64_str.startswith("data:"):
193
+ _, data = b64_str.split(",", 1)
194
+ else:
195
+ data = b64_str
196
+ image_data = base64.b64decode(data)
197
+ return Image.open(BytesIO(image_data)).convert("RGB")
198
+ except Exception:
199
+ return None
200
 
201
 
202
+ def calc_timeout_generic(*args, **kwargs):
203
+ gpu_timeout = kwargs.get("gpu_timeout", None)
204
+ if gpu_timeout is None and args:
205
+ gpu_timeout = args[-1]
206
  try:
207
  return int(gpu_timeout)
208
  except Exception:
209
  return 60
210
 
211
 
212
+ @spaces.GPU(duration=calc_timeout_generic)
213
+ def generate_image(model_name, text, image, max_new_tokens=1024, temperature=0.7, top_p=0.9, top_k=50, repetition_penalty=1.1, gpu_timeout=60):
214
+ try:
215
+ if not model_name or model_name not in MODEL_MAP:
216
+ yield "[ERROR] Please select a valid model."
217
+ return
218
+ if image is None:
219
+ yield "[ERROR] Please upload an image."
220
+ return
221
+ if not text or not str(text).strip():
222
+ yield "[ERROR] Please enter your OCR/query instruction."
223
+ return
224
+ if len(str(text)) > MAX_INPUT_TOKEN_LENGTH * 8:
225
+ yield "[ERROR] Query is too long. Please shorten your input."
226
+ return
227
+
228
+ processor, model = select_model(model_name)
229
+
230
+ messages = [{
231
+ "role": "user",
232
+ "content": [
233
+ {"type": "image"},
234
+ {"type": "text", "text": text},
235
+ ]
236
+ }]
237
+
238
+ prompt_full = processor.apply_chat_template(
239
+ messages,
240
+ tokenize=False,
241
+ add_generation_prompt=True
242
+ )
 
 
 
 
 
 
 
 
 
243
 
244
+ inputs = processor(
245
+ text=[prompt_full],
246
+ images=[image],
247
+ return_tensors="pt",
248
+ padding=True,
249
+ truncation=True,
250
+ max_length=MAX_INPUT_TOKEN_LENGTH
251
+ ).to(device)
252
+
253
+ streamer = TextIteratorStreamer(
254
+ processor.tokenizer if hasattr(processor, "tokenizer") else processor,
255
+ skip_prompt=True,
256
+ skip_special_tokens=True
257
+ )
258
 
259
+ generation_error = {"error": None}
260
+
261
+ generation_kwargs = {
262
+ **inputs,
263
+ "streamer": streamer,
264
+ "max_new_tokens": int(max_new_tokens),
265
+ "do_sample": True,
266
+ "temperature": float(temperature),
267
+ "top_p": float(top_p),
268
+ "top_k": int(top_k),
269
+ "repetition_penalty": float(repetition_penalty),
270
+ }
271
 
272
+ def _run_generation():
273
+ try:
274
+ model.generate(**generation_kwargs)
275
+ except Exception as e:
276
+ generation_error["error"] = e
277
+ try:
278
+ streamer.end()
279
+ except Exception:
280
+ pass
281
+
282
+ thread = Thread(target=_run_generation, daemon=True)
283
+ thread.start()
284
+
285
+ buffer = ""
286
+ for new_text in streamer:
287
+ buffer += new_text.replace("<|im_end|>", "")
288
+ time.sleep(0.01)
289
+ yield buffer
290
+
291
+ thread.join(timeout=1.0)
292
+
293
+ if generation_error["error"] is not None:
294
+ err_msg = f"[ERROR] Inference failed: {str(generation_error['error'])}"
295
+ if buffer.strip():
296
+ yield buffer + "\n\n" + err_msg
297
+ else:
298
+ yield err_msg
299
+ return
300
+
301
+ if not buffer.strip():
302
+ yield "[ERROR] No output was generated."
303
+
304
+ except Exception as e:
305
+ yield f"[ERROR] {str(e)}"
306
+ finally:
307
+ gc.collect()
308
+ if torch.cuda.is_available():
309
+ torch.cuda.empty_cache()
310
+
311
+
312
+ 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):
313
+ try:
314
+ image = b64_to_pil(image_b64)
315
+ yield from generate_image(
316
+ model_name=model_name,
317
+ text=text,
318
+ image=image,
319
+ max_new_tokens=max_new_tokens_v,
320
+ temperature=temperature_v,
321
+ top_p=top_p_v,
322
+ top_k=top_k_v,
323
+ repetition_penalty=repetition_penalty_v,
324
+ gpu_timeout=gpu_timeout_v,
325
+ )
326
+ except Exception as e:
327
+ yield f"[ERROR] {str(e)}"
328
 
329
 
330
  def noop():
 
666
  const runBtnEl = document.getElementById('custom-run-btn');
667
  const outputArea = document.getElementById('custom-output-textarea');
668
  const imgStatus = document.getElementById('sb-image-status');
 
669
 
670
  if (!dropZone || !fileInput || !promptInput || !previewWrap || !previewImg) {
671
  setTimeout(init, 250);
 
675
  window.__ocr2GreenInitDone = true;
676
  let imageState = null;
677
  let toastTimer = null;
678
+ let examplePoller = null;
679
+ let lastSeenExamplePayload = null;
680
 
681
  function showToast(message, type) {
682
  let toast = document.getElementById('app-toast');
 
699
  toast.classList.add('visible');
700
  toastTimer = setTimeout(() => toast.classList.remove('visible'), 3500);
701
  }
 
702
 
703
  function showLoader() {
704
  const l = document.getElementById('output-loader');
 
712
  const sb = document.getElementById('sb-run-state');
713
  if (sb) sb.textContent = 'Done';
714
  }
715
+ function setRunErrorState() {
716
+ const l = document.getElementById('output-loader');
717
+ if (l) l.classList.remove('active');
718
+ const sb = document.getElementById('sb-run-state');
719
+ if (sb) sb.textContent = 'Error';
720
+ }
721
+
722
+ window.__showToast = showToast;
723
  window.__showLoader = showLoader;
724
  window.__hideLoader = hideLoader;
725
+ window.__setRunErrorState = setRunErrorState;
726
 
727
  function flashPromptError() {
728
  promptInput.classList.add('error-flash');
 
736
  setTimeout(() => outputArea.classList.remove('error-flash'), 800);
737
  }
738
 
739
+ function getValueFromContainer(containerId) {
740
+ const container = document.getElementById(containerId);
741
+ if (!container) return '';
742
+ const el = container.querySelector('textarea, input');
743
+ return el ? (el.value || '') : '';
744
+ }
745
+
746
  function setGradioValue(containerId, value) {
747
  const container = document.getElementById(containerId);
748
+ if (!container) return false;
749
+ const el = container.querySelector('textarea, input');
750
+ if (!el) return false;
751
+ const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
752
+ const ns = Object.getOwnPropertyDescriptor(proto, 'value');
753
+ if (ns && ns.set) {
754
+ ns.set.call(el, value);
755
+ el.dispatchEvent(new Event('input', {bubbles:true, composed:true}));
756
+ el.dispatchEvent(new Event('change', {bubbles:true, composed:true}));
757
+ return true;
758
+ }
759
+ return false;
760
  }
761
 
762
  function syncImageToGradio() {
 
773
  setGradioValue('hidden-model-name', name);
774
  }
775
 
776
+ function renderPreview() {
777
+ if (!imageState) {
778
+ previewImg.src = '';
779
+ previewImg.style.display = 'none';
780
+ previewWrap.style.display = 'none';
781
+ if (uploadPrompt) uploadPrompt.style.display = 'flex';
782
+ syncImageToGradio();
783
+ return;
784
+ }
785
+
786
  previewWrap.style.display = 'flex';
787
  if (uploadPrompt) uploadPrompt.style.display = 'none';
788
+ previewImg.src = imageState.preview || imageState.b64;
789
+ previewImg.style.display = 'block';
790
  syncImageToGradio();
791
  }
792
+
793
+ function setPreviewFromFileReader(b64, name) {
794
+ imageState = {b64, name: name || 'image', mode: 'image'};
795
+ renderPreview();
796
+ }
797
 
798
  function clearPreview() {
799
  imageState = null;
800
+ renderPreview();
 
 
 
801
  }
802
  window.__clearPreview = clearPreview;
803
 
 
808
  return;
809
  }
810
  const reader = new FileReader();
811
+ reader.onload = (e) => setPreviewFromFileReader(e.target.result, file.name);
812
  reader.readAsDataURL(file);
813
  }
814
 
 
847
  window.__activateModelTab = activateModelTab;
848
 
849
  document.querySelectorAll('.model-tab[data-model]').forEach(btn => {
850
+ btn.addEventListener('click', () => activateModelTab(btn.getAttribute('data-model')));
 
 
 
851
  });
852
 
853
  activateModelTab('Nanonets-OCR2-3B');
 
912
  showLoader();
913
  setTimeout(() => {
914
  const gradioBtn = document.getElementById('gradio-run-btn');
915
+ if (!gradioBtn) {
916
+ setRunErrorState();
917
+ if (outputArea) outputArea.value = '[ERROR] Run button not found.';
918
+ showToast('Run button not found', 'error');
919
+ return;
920
+ }
921
  const btn = gradioBtn.querySelector('button');
922
  if (btn) btn.click(); else gradioBtn.click();
923
  }, 180);
 
966
  });
967
  }
968
 
969
+ function applyExamplePayload(raw) {
970
+ try {
971
+ const data = JSON.parse(raw);
972
+ if (data.status !== 'ok') return;
973
+
974
+ if (data.model) activateModelTab(data.model);
975
+ if (data.query) {
976
+ promptInput.value = data.query;
977
+ syncPromptToGradio();
978
+ }
979
+
980
+ imageState = {
981
+ b64: data.image || '',
982
+ preview: data.image || '',
983
+ name: data.name || 'example.jpg',
984
+ mode: 'image'
985
+ };
986
+ renderPreview();
987
+
988
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
989
+ showToast('Example loaded', 'info');
990
+ } catch (e) {
991
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
992
+ }
993
+ }
994
+
995
+ function startExamplePolling() {
996
+ if (examplePoller) clearInterval(examplePoller);
997
+ let attempts = 0;
998
+ examplePoller = setInterval(() => {
999
+ attempts += 1;
1000
+ const current = getValueFromContainer('example-result-data');
1001
+ if (current && current !== lastSeenExamplePayload) {
1002
+ lastSeenExamplePayload = current;
1003
+ clearInterval(examplePoller);
1004
+ examplePoller = null;
1005
+ applyExamplePayload(current);
1006
+ return;
1007
+ }
1008
+ if (attempts >= 100) {
1009
+ clearInterval(examplePoller);
1010
+ examplePoller = null;
1011
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
1012
+ showToast('Example load timed out', 'error');
1013
+ }
1014
+ }, 120);
1015
+ }
1016
+
1017
+ function triggerExampleLoad(idx) {
1018
+ const btnWrap = document.getElementById('example-load-btn');
1019
+ const btn = btnWrap ? (btnWrap.querySelector('button') || btnWrap) : null;
1020
+ if (!btn) return;
1021
+
1022
+ let attempts = 0;
1023
+
1024
+ function writeIdxAndClick() {
1025
+ attempts += 1;
1026
+
1027
+ const ok1 = setGradioValue('example-idx-input', String(idx));
1028
+ setGradioValue('example-result-data', '');
1029
+ const currentVal = getValueFromContainer('example-idx-input');
1030
+
1031
+ if (ok1 && currentVal === String(idx)) {
1032
+ btn.click();
1033
+ startExamplePolling();
1034
+ return;
1035
+ }
1036
+
1037
+ if (attempts < 30) {
1038
+ setTimeout(writeIdxAndClick, 100);
1039
+ } else {
1040
+ document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
1041
+ showToast('Failed to initialize example loader', 'error');
1042
+ }
1043
+ }
1044
+
1045
+ writeIdxAndClick();
1046
+ }
1047
+
1048
  document.querySelectorAll('.example-card[data-idx]').forEach(card => {
1049
  card.addEventListener('click', () => {
1050
  const idx = card.getAttribute('data-idx');
1051
+ if (idx === null || idx === undefined || idx === '') return;
1052
  document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
1053
  card.classList.add('loading');
1054
  showToast('Loading example...', 'info');
1055
+ triggerExampleLoad(idx);
 
 
 
 
 
 
 
 
 
1056
  });
1057
  });
1058
 
1059
+ const observerTarget = document.getElementById('example-result-data');
1060
+ if (observerTarget) {
1061
+ const obs = new MutationObserver(() => {
1062
+ const current = getValueFromContainer('example-result-data');
1063
+ if (!current || current === lastSeenExamplePayload) return;
1064
+ lastSeenExamplePayload = current;
1065
+ if (examplePoller) {
1066
+ clearInterval(examplePoller);
1067
+ examplePoller = null;
 
 
 
 
 
 
 
 
 
 
 
1068
  }
1069
+ applyExamplePayload(current);
1070
+ });
1071
+ obs.observe(observerTarget, {childList:true, subtree:true, characterData:true, attributes:true});
 
 
 
1072
  }
 
1073
 
1074
  if (outputArea) outputArea.value = '';
1075
  const sb = document.getElementById('sb-run-state');
 
1089
 
1090
  let lastText = '';
1091
 
1092
+ function isErrorText(val) {
1093
+ return typeof val === 'string' && val.trim().startsWith('[ERROR]');
1094
+ }
1095
+
1096
  function syncOutput() {
1097
  const el = resultContainer.querySelector('textarea') || resultContainer.querySelector('input');
1098
  if (!el) return;
 
1101
  lastText = val;
1102
  outArea.value = val;
1103
  outArea.scrollTop = outArea.scrollHeight;
1104
+
1105
+ if (val.trim()) {
1106
+ if (isErrorText(val)) {
1107
+ if (window.__setRunErrorState) window.__setRunErrorState();
1108
+ if (window.__showToast) window.__showToast('Inference failed', 'error');
1109
+ } else {
1110
+ if (window.__hideLoader) window.__hideLoader();
1111
+ }
1112
+ }
1113
  }
1114
  }
1115
 
 
1195
 
1196
  <div id="single-preview-wrap" class="single-preview-wrap">
1197
  <div class="single-preview-card">
1198
+ <img id="single-preview-img" src="" alt="Preview" style="display:none;">
1199
  <div class="preview-overlay-actions">
1200
  <button id="preview-upload-btn" class="preview-action-btn" title="Replace">Upload</button>
1201
  <button id="preview-clear-btn" class="preview-action-btn" title="Clear">Clear</button>
 
1304
 
1305
  run_btn = gr.Button("Run", elem_id="gradio-run-btn")
1306
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1307
  demo.load(fn=noop, inputs=None, outputs=None, js=gallery_js)
1308
  demo.load(fn=noop, inputs=None, outputs=None, js=wire_outputs_js)
1309
 
 
1326
  const model = modelEl ? modelEl.getAttribute('data-model') : m;
1327
  const promptEl = document.getElementById('custom-query-input');
1328
  const promptVal = promptEl ? promptEl.value : p;
1329
+
1330
  let imgVal = img;
1331
+ const imgContainer = document.getElementById('hidden-image-b64');
1332
  if (imgContainer) {
1333
  const inner = imgContainer.querySelector('textarea, input');
1334
  if (inner) imgVal = inner.value;
1335
  }
1336
+
1337
  return [model, promptVal, imgVal, mnt, t, tp, tk, rp, gd];
1338
  }""",
1339
  )