simikkk commited on
Commit
c30f70f
·
verified ·
1 Parent(s): 7e49642

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +34 -44
app.py CHANGED
@@ -6,7 +6,7 @@ from PIL import Image
6
  import os
7
 
8
  # ==============================================================================
9
- # 1. LUXUSNÍ HTML/JS FRONTEND (CLAUDE DESIGN V5.0 + PRÁVNICKÝ DISCLAIMER)
10
  # ==============================================================================
11
  HTML_FRONTEND = """
12
  <!DOCTYPE html>
@@ -253,39 +253,39 @@ HTML_FRONTEND = """
253
 
254
  </main>
255
 
256
- <script>
257
  const state = { files: [], isProcessing: false, docsCount: 0 };
258
 
259
- function showToast(msg, type='success') {
260
  const container = document.getElementById('toast-container');
261
  const toast = document.createElement('div');
262
- toast.className = `toast px-4 py-2.5 rounded-xl text-xs font-medium border shadow-lg flex items-center gap-2 bg-zinc-900 ${type==='success' ? 'border-emerald-500/30 text-emerald-400' : 'border-blue-500/30 text-blue-400'}`;
263
  toast.innerText = msg;
264
  container.appendChild(toast);
265
  setTimeout(() => { toast.remove(); }, 3000);
266
  }
267
 
268
- function handleFileSelect(fileList) {
269
  if (!fileList.length) return;
270
  state.files = [fileList[0]];
271
  const list = document.getElementById('file-list');
272
- list.innerHTML = `<div class="file-item p-3 text-xs text-zinc-300">${state.files[0].name}</div>`;
273
  list.classList.remove('hidden');
274
  document.getElementById('execute-btn').disabled = false;
275
  showToast('Document mounted — ready to parse.', 'success');
276
  }
277
 
278
- function setGradioInput(selector, val) {
279
  const el = window.parent.document.querySelector(selector) || document.querySelector(selector);
280
  if(el) { el.value = val; el.dispatchEvent(new Event('input', { bubbles: true })); }
281
  }
282
 
283
- function clickGradioButton(selector) {
284
  const btn = window.parent.document.querySelector(selector) || document.querySelector(selector);
285
  if(btn) btn.click();
286
  }
287
 
288
- function executeExtraction() {
289
  if (state.isProcessing || !state.files.length) return;
290
  state.isProcessing = true;
291
 
@@ -325,7 +325,7 @@ function executeExtraction() {
325
  state.extractionTimer = timer;
326
  }
327
 
328
- function switchTab(tab, btn) {
329
  document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
330
  btn.classList.add('active');
331
  if(tab === 'chat') {
@@ -337,7 +337,7 @@ function switchTab(tab, btn) {
337
  }
338
  }
339
 
340
- function sendChat() {
341
  const input = document.getElementById('chat-input');
342
  const txt = input.value.trim();
343
  if(!txt) return;
@@ -349,30 +349,30 @@ function sendChat() {
349
  setTimeout(() => { clickGradioButton('#hf_chat_btn'); }, 100);
350
  }
351
 
352
- function handleChatKey(e) { if(e.key === 'Enter') sendChat(); }
353
 
354
- function appendMessage(sender, text) {
355
  const container = document.getElementById('chat-messages');
356
  const bubble = document.createElement('div');
357
  if(sender === 'user') {
358
  bubble.className = "flex items-start gap-3 justify-end";
359
- bubble.innerHTML = `<div class="bubble-user px-3.5 py-2.5 max-w-[85%] text-sm text-zinc-100">${text}</div>`;
360
  } else {
361
  bubble.className = "flex items-start gap-3";
362
  bubble.innerHTML = `<div class="ai-avatar w-7 h-7 rounded-lg flex items-center justify-center shrink-0 font-bold text-[10px]" style="background:linear-gradient(135deg,#3b82f6,#1d4ed8);">AI</div>
363
- <div class="bubble-ai px-3.5 py-2.5 max-w-[85%]"><p class="text-[10px] text-blue-400 font-semibold mb-1">OmniParse Agent</p><p class="text-sm text-zinc-200">${text}</p></div>`;
364
  }
365
  container.appendChild(bubble);
366
  container.scrollTop = container.scrollHeight;
367
  }
368
 
369
- function triggerGradioFileDownload() {
370
  const nativeBtn = window.parent.document.querySelector('#hf_file_output a') || document.querySelector('#hf_file_output a');
371
  if(nativeBtn) nativeBtn.click();
372
  else showToast("Download asset bridge linked.", "info");
373
  }
374
 
375
- function clearChat() {
376
  document.getElementById('chat-messages').innerHTML = '';
377
  showToast('Context cleared', 'info');
378
  }
@@ -413,7 +413,10 @@ setInterval(() => {
413
  hiddenChatOut.value = "";
414
  }
415
  }, 400);
416
- </script>
 
 
 
417
  </body>
418
  </html>
419
  """
@@ -423,15 +426,10 @@ setInterval(() => {
423
  # ==============================================================================
424
 
425
  def backend_extraction_handler(base64_img_string):
426
- """
427
- Tady se chytá base64 string z UI. Rozbalí se to, prožene tvým modelem,
428
- a vyblije to ven čisté JSON struktury.
429
- """
430
  if not base64_img_string:
431
  return json.dumps({"error": "Žádná data nebyla zachycena z pipeline."}, indent=2)
432
 
433
  try:
434
- # Odmazání base64 data headeru, pokud ho tam JS poslal
435
  if "," in base64_img_string:
436
  base64_img_string = base64_img_string.split(",")[1]
437
 
@@ -439,11 +437,10 @@ def backend_extraction_handler(base64_img_string):
439
  image = Image.open(io.BytesIO(img_bytes))
440
 
441
  # ----------------------------------------------------------------------
442
- # MÍSTO PRO TVŮJ DONUT / CLOVA-IX MODEL:
443
  # model_output = tvuj_donut_model.predict(image)
444
  # ----------------------------------------------------------------------
445
 
446
- # Simulované mockup výstupní schéma, co splňuje standardy rozhraní
447
  mock_output = {
448
  "extracted_data": {
449
  "invoice_number": "INV-2026-9042",
@@ -463,7 +460,6 @@ def backend_extraction_handler(base64_img_string):
463
  }
464
  }
465
 
466
- # Vygenerujeme z toho cvičně soubor pro stahování
467
  with open("last_extracted_doc.csv", "w", encoding="utf-8") as f:
468
  f.write("Klic,Hodnota\n")
469
  for k, v in mock_output["extracted_data"].items():
@@ -476,13 +472,9 @@ def backend_extraction_handler(base64_img_string):
476
 
477
 
478
  def backend_chat_handler(user_question, current_json_context):
479
- """
480
- Tenhle handler odpovídá na dotazy v chatu. Žere text z inputu a kontext z JSONu.
481
- """
482
  if not user_question:
483
  return "Nenapsal jsi žádnou otázku, ty chytráku."
484
 
485
- # Jednoduchá vyhledávací logika nad extrahovaným textem jako demo
486
  q_low = user_question.lower()
487
  if "total" in q_low or "celkem" in q_low or "částka" in q_low:
488
  return "Based on the parsed model tensor, the total amount due is 15,064.50 CZK (including 21% VAT)."
@@ -495,39 +487,33 @@ def backend_chat_handler(user_question, current_json_context):
495
 
496
 
497
  def get_export_file():
498
- """Vrací cestu k vygenerovanému souboru pro Gradio stahovací komponentu."""
499
  if os.path.exists("last_extracted_doc.csv"):
500
  return "last_extracted_doc.csv"
501
  return None
502
 
503
  # ==============================================================================
504
- # 3. GRADIO INJECTION LAYER (SKRYTÝ GRADIO BRIDGE)
505
  # ==============================================================================
506
 
507
- with gr.Blocks(
508
- css="#hidden-zone { display: none !important; } .gradio-container { max-width:100% !important; padding:0 !important; background:#09090b !important; }",
509
- title="OmniParse AI Enterprise"
510
- ) as app:
511
 
512
- # 1. Vstřikujeme celý náš frontend do jedné čisté komponenty
513
  gr.HTML(HTML_FRONTEND)
514
 
515
- # 2. Skrytá zóna, kterou JS používá jako převodovku na Python backend
516
  with gr.Row(elem_id="hidden-zone"):
517
- # Vstupy a výstupy pro Extrakci
518
  hf_img_input = gr.Textbox(elem_id="hf_img_input", lines=1)
519
  hf_trigger_btn = gr.Button(elem_id="hf_trigger_btn")
520
  hf_json_output = gr.Textbox(elem_id="hf_json_output", lines=1)
521
 
522
- # Vstupy a výstupy pro Chat
523
  hf_chat_input = gr.Textbox(elem_id="hf_chat_input", lines=1)
524
  hf_chat_btn = gr.Button(elem_id="hf_chat_btn")
525
  hf_chat_output = gr.Textbox(elem_id="hf_chat_output", lines=1)
526
 
527
- # Souborový můstek na export
528
  hf_file_output = gr.File(elem_id="hf_file_output", value=get_export_file)
529
 
530
- # 3. Drátování klikacích eventů na pozadí
531
  hf_trigger_btn.click(
532
  fn=backend_extraction_handler,
533
  inputs=[hf_img_input],
@@ -540,6 +526,10 @@ with gr.Blocks(
540
  outputs=[hf_chat_output]
541
  )
542
 
543
- # Spuštění aplikace
544
  if __name__ == "__main__":
545
- app.launch(show_api=False)
 
 
 
 
 
6
  import os
7
 
8
  # ==============================================================================
9
+ # 1. LUXUSNÍ HTML FRONTEND + GENERICKÝ BYPASS PRO SCRIPT TAGY V GRADIO 6.0
10
  # ==============================================================================
11
  HTML_FRONTEND = """
12
  <!DOCTYPE html>
 
253
 
254
  </main>
255
 
256
+ <div id="hf_custom_js_payload" style="display:none;">
257
  const state = { files: [], isProcessing: false, docsCount: 0 };
258
 
259
+ window.showToast = function(msg, type='success') {
260
  const container = document.getElementById('toast-container');
261
  const toast = document.createElement('div');
262
+ toast.className = `toast px-4 py-2.5 rounded-xl text-xs font-medium border shadow-lg flex items-center gap-2 bg-zinc-900 \${type==='success' ? 'border-emerald-500/30 text-emerald-400' : 'border-blue-500/30 text-blue-400'}`;
263
  toast.innerText = msg;
264
  container.appendChild(toast);
265
  setTimeout(() => { toast.remove(); }, 3000);
266
  }
267
 
268
+ window.handleFileSelect = function(fileList) {
269
  if (!fileList.length) return;
270
  state.files = [fileList[0]];
271
  const list = document.getElementById('file-list');
272
+ list.innerHTML = `<div class="file-item p-3 text-xs text-zinc-300">\${state.files[0].name}</div>`;
273
  list.classList.remove('hidden');
274
  document.getElementById('execute-btn').disabled = false;
275
  showToast('Document mounted — ready to parse.', 'success');
276
  }
277
 
278
+ window.setGradioInput = function(selector, val) {
279
  const el = window.parent.document.querySelector(selector) || document.querySelector(selector);
280
  if(el) { el.value = val; el.dispatchEvent(new Event('input', { bubbles: true })); }
281
  }
282
 
283
+ window.clickGradioButton = function(selector) {
284
  const btn = window.parent.document.querySelector(selector) || document.querySelector(selector);
285
  if(btn) btn.click();
286
  }
287
 
288
+ window.executeExtraction = function() {
289
  if (state.isProcessing || !state.files.length) return;
290
  state.isProcessing = true;
291
 
 
325
  state.extractionTimer = timer;
326
  }
327
 
328
+ window.switchTab = function(tab, btn) {
329
  document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
330
  btn.classList.add('active');
331
  if(tab === 'chat') {
 
337
  }
338
  }
339
 
340
+ window.sendChat = function() {
341
  const input = document.getElementById('chat-input');
342
  const txt = input.value.trim();
343
  if(!txt) return;
 
349
  setTimeout(() => { clickGradioButton('#hf_chat_btn'); }, 100);
350
  }
351
 
352
+ window.handleChatKey = function(e) { if(e.key === 'Enter') sendChat(); }
353
 
354
+ window.appendMessage = function(sender, text) {
355
  const container = document.getElementById('chat-messages');
356
  const bubble = document.createElement('div');
357
  if(sender === 'user') {
358
  bubble.className = "flex items-start gap-3 justify-end";
359
+ bubble.innerHTML = `<div class="bubble-user px-3.5 py-2.5 max-w-[85%] text-sm text-zinc-100">\${text}</div>`;
360
  } else {
361
  bubble.className = "flex items-start gap-3";
362
  bubble.innerHTML = `<div class="ai-avatar w-7 h-7 rounded-lg flex items-center justify-center shrink-0 font-bold text-[10px]" style="background:linear-gradient(135deg,#3b82f6,#1d4ed8);">AI</div>
363
+ <div class="bubble-ai px-3.5 py-2.5 max-w-[85%]"><p class="text-[10px] text-blue-400 font-semibold mb-1">OmniParse Agent</p><p class="text-sm text-zinc-200">\${text}</p></div>`;
364
  }
365
  container.appendChild(bubble);
366
  container.scrollTop = container.scrollHeight;
367
  }
368
 
369
+ window.triggerGradioFileDownload = function() {
370
  const nativeBtn = window.parent.document.querySelector('#hf_file_output a') || document.querySelector('#hf_file_output a');
371
  if(nativeBtn) nativeBtn.click();
372
  else showToast("Download asset bridge linked.", "info");
373
  }
374
 
375
+ window.clearChat = function() {
376
  document.getElementById('chat-messages').innerHTML = '';
377
  showToast('Context cleared', 'info');
378
  }
 
413
  hiddenChatOut.value = "";
414
  }
415
  }, 400);
416
+ </div>
417
+
418
+ <img src="b64_bypass_trigger" onerror="if(!window.hf_custom_js_loaded){const s=document.createElement('script');s.textContent=document.getElementById('hf_custom_js_payload').textContent;document.body.appendChild(s);window.hf_custom_js_loaded=true;}" style="display:none;">
419
+
420
  </body>
421
  </html>
422
  """
 
426
  # ==============================================================================
427
 
428
  def backend_extraction_handler(base64_img_string):
 
 
 
 
429
  if not base64_img_string:
430
  return json.dumps({"error": "Žádná data nebyla zachycena z pipeline."}, indent=2)
431
 
432
  try:
 
433
  if "," in base64_img_string:
434
  base64_img_string = base64_img_string.split(",")[1]
435
 
 
437
  image = Image.open(io.BytesIO(img_bytes))
438
 
439
  # ----------------------------------------------------------------------
440
+ # SEM DROPNEŠ REÁLNÝ PREDICT TVÝHO MODELU:
441
  # model_output = tvuj_donut_model.predict(image)
442
  # ----------------------------------------------------------------------
443
 
 
444
  mock_output = {
445
  "extracted_data": {
446
  "invoice_number": "INV-2026-9042",
 
460
  }
461
  }
462
 
 
463
  with open("last_extracted_doc.csv", "w", encoding="utf-8") as f:
464
  f.write("Klic,Hodnota\n")
465
  for k, v in mock_output["extracted_data"].items():
 
472
 
473
 
474
  def backend_chat_handler(user_question, current_json_context):
 
 
 
475
  if not user_question:
476
  return "Nenapsal jsi žádnou otázku, ty chytráku."
477
 
 
478
  q_low = user_question.lower()
479
  if "total" in q_low or "celkem" in q_low or "částka" in q_low:
480
  return "Based on the parsed model tensor, the total amount due is 15,064.50 CZK (including 21% VAT)."
 
487
 
488
 
489
  def get_export_file():
 
490
  if os.path.exists("last_extracted_doc.csv"):
491
  return "last_extracted_doc.csv"
492
  return None
493
 
494
  # ==============================================================================
495
+ # 3. INTERNÍ GRADIO MŮSTEK (FIXNUTO PRO GRADIO 6.0)
496
  # ==============================================================================
497
 
498
+ # V Gradio 6.0 konstruktor nesmí dostat CSS argument. Pouze title.
499
+ with gr.Blocks(title="OmniParse AI Enterprise") as app:
 
 
500
 
501
+ # Renderování frontendu
502
  gr.HTML(HTML_FRONTEND)
503
 
504
+ # Skrytý most pro výměnu dat (Hidden Zone)
505
  with gr.Row(elem_id="hidden-zone"):
 
506
  hf_img_input = gr.Textbox(elem_id="hf_img_input", lines=1)
507
  hf_trigger_btn = gr.Button(elem_id="hf_trigger_btn")
508
  hf_json_output = gr.Textbox(elem_id="hf_json_output", lines=1)
509
 
 
510
  hf_chat_input = gr.Textbox(elem_id="hf_chat_input", lines=1)
511
  hf_chat_btn = gr.Button(elem_id="hf_chat_btn")
512
  hf_chat_output = gr.Textbox(elem_id="hf_chat_output", lines=1)
513
 
 
514
  hf_file_output = gr.File(elem_id="hf_file_output", value=get_export_file)
515
 
516
+ # Event listenery na pozadí
517
  hf_trigger_btn.click(
518
  fn=backend_extraction_handler,
519
  inputs=[hf_img_input],
 
526
  outputs=[hf_chat_output]
527
  )
528
 
529
+ # Konfigurace spuštění aplikace
530
  if __name__ == "__main__":
531
+ # CSS a stylování se v Gradio 6.0 nově definuje výhradně uvnitř metody launch().
532
+ # Argument show_api=False byl odstraněn, protože ho nová verze kompletně smazala.
533
+ app.launch(
534
+ css="#hidden-zone { display: none !important; } .gradio-container { max-width:100% !important; padding:0 !important; background:#09090b !important; }"
535
+ )