Abrar55 commited on
Commit
e2c7d7e
Β·
verified Β·
1 Parent(s): 5ebb353

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +639 -266
app.py CHANGED
@@ -301,14 +301,14 @@ BENCHMARK_ROWS = [
301
  BENCHMARK_DF = pd.DataFrame(BENCHMARK_ROWS)
302
 
303
  # ---------------------------------------------------------------------------
304
- # Gradio UI
305
  # ---------------------------------------------------------------------------
306
 
307
  WARNING_HTML = ""
308
  if model_load_error:
309
  WARNING_HTML = (
310
  '<div class="chex-banner">'
311
- f'<span class="chex-banner-icon">⚠</span>'
312
  f'<div class="chex-banner-body"><strong>Model not loaded</strong> Β· '
313
  f'{model_load_error} β€” set <code>HF_MODEL_REPO</code> in Space secrets.</div>'
314
  '</div>'
@@ -321,9 +321,10 @@ if model_load_error:
321
  CHEX_CSS = """
322
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
323
 
324
- /* ── Reset & base ── */
325
  *, *::before, *::after { box-sizing: border-box; }
326
 
 
327
  :root {
328
  --bg-base: #f3f4f7;
329
  --bg-grad: radial-gradient(ellipse 1200px 700px at 18% -10%, rgba(120,150,200,0.18), transparent 60%),
@@ -340,72 +341,161 @@ CHEX_CSS = """
340
  --fg-muted: #5b6275;
341
  --fg-subtle: #8a91a3;
342
  --green: #0f9d58;
 
 
 
 
 
343
  --amber: #b87800;
344
  --amber-bg: rgba(245,158,11,0.10);
345
  --amber-border: rgba(245,158,11,0.30);
346
  --blur: 22px;
347
  --blur-strong: 32px;
348
- --shadow-md: 0 1px 0 rgba(255,255,255,0.6) inset, 0 8px 24px rgba(15,18,30,0.06), 0 1px 2px rgba(15,18,30,0.04);
 
 
349
  --radius: 10px;
350
  --radius-lg: 16px;
351
  }
352
 
353
- .dark, [data-theme="dark"] {
354
- --bg-base: #07090e;
355
- --bg-grad: radial-gradient(ellipse 1100px 700px at 15% -5%, rgba(70,110,200,0.20), transparent 60%),
356
- radial-gradient(ellipse 900px 600px at 95% 110%, rgba(150,90,220,0.14), transparent 55%),
357
- linear-gradient(180deg, #0a0d14 0%, #06080d 100%);
358
- --bg-elev: rgba(22,26,36,0.55);
359
- --bg-elev-strong: rgba(28,32,44,0.72);
360
- --bg-sunken: rgba(12,14,20,0.55);
361
- --bg-input: rgba(14,17,25,0.55);
362
- --border: rgba(255,255,255,0.07);
363
- --border-strong: rgba(255,255,255,0.13);
364
- --hairline: rgba(255,255,255,0.05);
365
- --fg: #eceef4;
366
- --fg-muted: #9ba3b6;
367
- --fg-subtle: #6a7188;
368
- --green: #4ade80;
369
- --amber: #fbbf24;
370
- }
371
-
372
- /* ── App shell ── */
373
  .gradio-container {
374
  font-family: 'Inter', system-ui, -apple-system, sans-serif !important;
375
  font-size: 14px !important;
376
  line-height: 1.55 !important;
377
  color: var(--fg) !important;
378
- background: var(--bg-grad) !important;
379
- background-attachment: fixed !important;
380
- background-color: var(--bg-base) !important;
381
  -webkit-font-smoothing: antialiased !important;
 
382
  letter-spacing: -0.006em !important;
383
  max-width: 1480px !important;
 
 
384
  }
385
 
386
- /* ── Topbar / header ── */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  .chex-topbar {
388
  display: flex;
389
  align-items: center;
390
  gap: 16px;
391
  padding: 0 28px;
392
  height: 60px;
 
 
 
393
  background: var(--bg-elev);
394
  backdrop-filter: blur(var(--blur-strong)) saturate(160%);
395
  -webkit-backdrop-filter: blur(var(--blur-strong)) saturate(160%);
396
  border-bottom: 1px solid var(--hairline);
397
- margin-bottom: 0;
398
- }
399
- .chex-brand {
400
- display: flex;
401
- align-items: center;
402
- gap: 12px;
403
  }
 
404
  .chex-logo {
405
- width: 26px; height: 26px;
 
406
  border-radius: 8px;
407
- background: linear-gradient(135deg, var(--fg), rgba(13,18,32,0.7));
408
- color: var(--bg-base);
409
  display: grid;
410
  place-items: center;
411
  font-family: 'JetBrains Mono', monospace;
@@ -413,21 +503,27 @@ CHEX_CSS = """
413
  font-size: 11px;
414
  letter-spacing: -0.05em;
415
  box-shadow: 0 4px 14px rgba(15,18,30,0.18), 0 1px 0 rgba(255,255,255,0.25) inset;
 
416
  }
 
417
  .chex-name {
418
  font-size: 15px;
419
  font-weight: 600;
420
  letter-spacing: -0.01em;
421
  color: var(--fg);
 
422
  }
 
423
  .chex-tag {
424
  font-size: 12px;
425
  color: var(--fg-muted);
426
  font-weight: 400;
427
  padding-left: 12px;
428
  border-left: 1px solid var(--hairline);
 
429
  }
430
- .chex-status-pill {
 
431
  display: inline-flex;
432
  align-items: center;
433
  gap: 8px;
@@ -441,17 +537,19 @@ CHEX_CSS = """
441
  -webkit-backdrop-filter: blur(12px);
442
  font-family: 'JetBrains Mono', monospace;
443
  white-space: nowrap;
444
- margin-left: auto;
445
  }
446
- .chex-status-dot {
447
- width: 6px; height: 6px;
 
 
448
  border-radius: 50%;
449
  background: var(--green);
450
  box-shadow: 0 0 0 3px rgba(15,157,88,0.22);
451
  display: inline-block;
 
452
  }
453
 
454
- /* ── Banner ── */
455
  .chex-banner {
456
  display: flex;
457
  align-items: center;
@@ -464,10 +562,10 @@ CHEX_CSS = """
464
  color: var(--amber);
465
  font-size: 13px;
466
  font-family: 'Inter', sans-serif;
467
- margin-bottom: 0;
468
  }
469
- .chex-banner-icon { font-size: 14px; }
470
- .chex-banner-body { color: var(--fg); font-weight: 400; }
471
  .chex-banner-body strong { color: var(--fg); font-weight: 600; }
472
  .chex-banner code {
473
  font-family: 'JetBrains Mono', monospace;
@@ -477,15 +575,21 @@ CHEX_CSS = """
477
  border-radius: 4px;
478
  }
479
 
480
- /* ── Tabs ── */
481
  .tab-nav {
482
  background: var(--bg-elev) !important;
483
  backdrop-filter: blur(var(--blur)) saturate(160%) !important;
484
  -webkit-backdrop-filter: blur(var(--blur)) saturate(160%) !important;
485
  border-bottom: 1px solid var(--hairline) !important;
 
486
  padding: 0 20px !important;
487
- gap: 2px !important;
 
 
 
 
488
  }
 
489
  .tab-nav button {
490
  background: transparent !important;
491
  border: none !important;
@@ -495,35 +599,82 @@ CHEX_CSS = """
495
  font-size: 13px !important;
496
  font-weight: 500 !important;
497
  font-family: 'Inter', sans-serif !important;
 
498
  position: relative !important;
499
  white-space: nowrap !important;
500
- transition: color 0.15s !important;
 
 
 
501
  }
502
- .tab-nav button:hover { color: var(--fg) !important; }
503
- .tab-nav button.selected {
504
  color: var(--fg) !important;
505
  background: transparent !important;
506
  }
507
- .tab-nav button.selected::after {
 
 
 
 
 
 
 
 
 
 
508
  content: "";
509
  position: absolute;
510
- left: 12px; right: 12px; bottom: -1px;
 
 
511
  height: 1.5px;
512
  background: var(--fg);
513
  border-radius: 2px 2px 0 0;
514
  }
515
 
516
- /* ── Glass cards ── */
517
- .chex-card {
518
- background: var(--bg-elev);
519
- backdrop-filter: blur(var(--blur)) saturate(180%);
520
- -webkit-backdrop-filter: blur(var(--blur)) saturate(180%);
521
- border: 1px solid var(--border);
522
- border-radius: var(--radius-lg);
523
- box-shadow: var(--shadow-md);
524
- overflow: hidden;
525
- margin-bottom: 0;
526
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
527
  .chex-card-header {
528
  padding: 16px 20px;
529
  display: flex;
@@ -532,6 +683,7 @@ CHEX_CSS = """
532
  gap: 12px;
533
  border-bottom: 1px solid var(--hairline);
534
  }
 
535
  .chex-card-title {
536
  font-size: 13.5px;
537
  font-weight: 600;
@@ -541,21 +693,84 @@ CHEX_CSS = """
541
  gap: 10px;
542
  color: var(--fg);
543
  white-space: nowrap;
 
544
  }
 
545
  .chex-card-kicker {
546
  font-family: 'JetBrains Mono', monospace;
547
  font-size: 11px;
548
  color: var(--fg-subtle);
549
  font-weight: 400;
 
550
  }
551
 
552
- /* ── Override Gradio inputs to match design ── */
553
- .gradio-container input[type="text"],
554
- .gradio-container textarea,
555
- .gradio-container select,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
  .gradio-container .gr-input,
557
- label.block textarea,
558
- label.block input {
 
559
  background: var(--bg-input) !important;
560
  backdrop-filter: blur(10px) !important;
561
  -webkit-backdrop-filter: blur(10px) !important;
@@ -564,88 +779,124 @@ label.block input {
564
  color: var(--fg) !important;
565
  font-family: 'Inter', sans-serif !important;
566
  font-size: 13px !important;
567
- transition: border-color 0.18s, box-shadow 0.18s !important;
 
 
 
568
  }
569
- label.block textarea:focus,
570
- label.block input:focus {
 
 
 
571
  border-color: var(--border-strong) !important;
572
  background: var(--bg-elev-strong) !important;
573
  box-shadow: 0 0 0 4px rgba(13,18,32,0.08) !important;
574
  outline: none !important;
575
  }
576
 
577
- /* Labels */
578
- label.block > span,
579
- .gr-form > label > span {
580
- font-family: 'JetBrains Mono', monospace !important;
581
- font-size: 10.5px !important;
582
- font-weight: 500 !important;
583
- text-transform: uppercase !important;
584
- letter-spacing: 0.08em !important;
585
  color: var(--fg-subtle) !important;
586
  }
587
 
 
 
 
 
 
 
 
 
 
588
  /* ── Buttons ── */
 
 
 
 
 
 
 
 
 
 
 
589
  .gradio-container button.primary,
590
- .gradio-container .gr-button-primary {
 
591
  background: var(--fg) !important;
592
  color: var(--bg-base) !important;
593
  border: 1px solid var(--fg) !important;
594
- border-radius: var(--radius) !important;
595
- font-family: 'Inter', sans-serif !important;
596
- font-weight: 500 !important;
597
- font-size: 13px !important;
598
- padding: 10px 16px !important;
599
- box-shadow: 0 6px 18px rgba(13,18,32,0.28), 0 1px 0 rgba(255,255,255,0.15) inset !important;
600
- transition: opacity 0.18s !important;
601
  }
 
602
  .gradio-container button.primary:hover,
603
- .gradio-container .gr-button-primary:hover { opacity: 0.92 !important; }
 
 
 
604
 
605
  .gradio-container button.secondary,
606
- .gradio-container .gr-button-secondary {
607
  background: var(--bg-elev) !important;
608
  backdrop-filter: blur(10px) !important;
 
609
  color: var(--fg) !important;
610
  border: 1px solid var(--border) !important;
611
- border-radius: var(--radius) !important;
612
- font-family: 'Inter', sans-serif !important;
613
- font-weight: 500 !important;
614
- font-size: 13px !important;
615
- padding: 10px 16px !important;
616
- transition: background 0.18s, border-color 0.18s !important;
617
  }
 
618
  .gradio-container button.secondary:hover,
619
- .gradio-container .gr-button-secondary:hover {
620
  background: var(--bg-elev-strong) !important;
621
  border-color: var(--border-strong) !important;
622
  }
623
 
624
- /* Small ghost buttons (load sample etc.) */
625
- button.lg.secondary.svelte-cmf5ev,
626
- button[class*="sm"] {
 
627
  font-size: 12px !important;
628
  padding: 7px 11px !important;
629
  }
630
 
 
 
 
 
 
 
 
 
631
  /* ── Dataframe / benchmark table ── */
632
- .gradio-container table,
633
- .gradio-container .gr-dataframe table {
 
634
  background: var(--bg-elev) !important;
635
  backdrop-filter: blur(var(--blur)) saturate(180%) !important;
 
636
  border: 1px solid var(--border) !important;
637
  border-radius: var(--radius-lg) !important;
638
  box-shadow: var(--shadow-md) !important;
 
 
 
 
 
639
  font-size: 13px !important;
640
  font-family: 'Inter', sans-serif !important;
641
  border-collapse: separate !important;
642
  border-spacing: 0 !important;
643
- overflow: hidden !important;
644
  width: 100% !important;
 
 
 
645
  }
 
646
  .gradio-container th {
647
  background: var(--bg-sunken) !important;
648
  border-bottom: 1px solid var(--hairline) !important;
 
649
  padding: 14px 18px !important;
650
  font-family: 'JetBrains Mono', monospace !important;
651
  font-size: 10.5px !important;
@@ -655,39 +906,78 @@ button[class*="sm"] {
655
  font-weight: 500 !important;
656
  text-align: left !important;
657
  }
 
658
  .gradio-container td {
659
  padding: 16px 18px !important;
660
  border-top: 1px solid var(--hairline) !important;
 
661
  vertical-align: top !important;
662
  line-height: 1.6 !important;
663
  color: var(--fg) !important;
 
664
  }
 
665
  .gradio-container tr:first-child td { border-top: none !important; }
666
 
667
- /* ── Markdown inside Gradio ── */
 
 
 
 
 
 
 
668
  .gradio-container .prose,
669
- .gradio-container .md {
 
670
  color: var(--fg) !important;
671
  font-family: 'Inter', sans-serif !important;
 
 
672
  }
673
- .gradio-container .prose h2 {
674
- font-size: 19px !important;
 
 
675
  font-weight: 600 !important;
676
  letter-spacing: -0.02em !important;
677
  color: var(--fg) !important;
678
  margin-bottom: 10px !important;
 
679
  }
680
- .gradio-container .prose h3 {
681
- font-size: 14px !important;
 
 
682
  font-weight: 600 !important;
683
  letter-spacing: -0.01em !important;
684
  color: var(--fg) !important;
685
  margin-bottom: 8px !important;
 
686
  }
687
- .gradio-container .prose p {
 
 
688
  color: var(--fg-muted) !important;
689
  font-size: 13px !important;
690
  line-height: 1.65 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
691
  }
692
 
693
  /* ── Bench intro card ── */
@@ -699,15 +989,18 @@ button[class*="sm"] {
699
  border-radius: var(--radius-lg);
700
  box-shadow: var(--shadow-md);
701
  padding: 24px 28px;
702
- margin-bottom: 22px;
703
  }
 
704
  .chex-bench-intro h2 {
705
  margin: 0 0 10px;
706
  font-size: 19px;
707
  font-weight: 600;
708
  letter-spacing: -0.02em;
709
  color: var(--fg);
 
710
  }
 
711
  .chex-bench-intro p {
712
  margin: 0;
713
  color: var(--fg-muted);
@@ -715,27 +1008,34 @@ button[class*="sm"] {
715
  line-height: 1.65;
716
  font-family: 'Inter', sans-serif;
717
  }
 
718
  .chex-bench-stats {
719
  display: grid;
720
  grid-template-columns: repeat(3, 1fr);
721
  gap: 8px;
722
  margin-top: 18px;
723
  }
 
724
  .chex-bench-stat {
725
  background: var(--bg-sunken);
726
  border: 1px solid var(--hairline);
727
  border-radius: var(--radius);
728
  padding: 12px 14px;
729
  }
 
730
  .chex-bench-stat .v {
731
  font-family: 'Inter', sans-serif;
732
  font-size: 20px;
733
  font-weight: 600;
734
  letter-spacing: -0.025em;
735
  color: var(--fg);
 
 
736
  }
737
- .chex-bench-stat .v.red { color: #d23131; }
738
- .chex-bench-stat .v.green { color: #0f9d58; }
 
 
739
  .chex-bench-stat .k {
740
  font-size: 10px;
741
  text-transform: uppercase;
@@ -757,22 +1057,44 @@ button[class*="sm"] {
757
  background: var(--bg-elev);
758
  backdrop-filter: blur(var(--blur));
759
  -webkit-backdrop-filter: blur(var(--blur));
760
- margin-top: 28px;
761
  }
762
- .chex-footer .sep { opacity: 0.5; }
763
 
764
- /* ── Output textboxes ── */
765
- .gradio-container .gr-textbox[data-testid],
766
- .gradio-container textarea[readonly] {
767
- background: var(--bg-sunken) !important;
768
- border: 1px solid var(--hairline) !important;
769
- font-size: 13px !important;
770
- line-height: 1.65 !important;
771
- color: var(--fg) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  }
773
 
774
- /* Scrollbars */
775
- *::-webkit-scrollbar { width: 10px; height: 10px; }
776
  *::-webkit-scrollbar-thumb {
777
  background: var(--border-strong);
778
  border-radius: 999px;
@@ -780,19 +1102,38 @@ button[class*="sm"] {
780
  background-clip: padding-box;
781
  }
782
  *::-webkit-scrollbar-track { background: transparent; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
783
  """
784
 
 
 
 
 
785
  TOPBAR_HTML = """
786
  <div class="chex-topbar">
787
- <div class="chex-brand">
788
- <div class="chex-logo">CX</div>
789
- <div class="chex-name">CHEX</div>
790
- <div class="chex-tag">grounded answers from documents</div>
791
- </div>
792
- <div class="chex-status-pill">
793
- <span class="chex-status-dot"></span>
794
- <span>MI300X Β· ready</span>
795
- </div>
796
  </div>
797
  """
798
 
@@ -820,151 +1161,165 @@ BENCH_INTRO_HTML = """
820
  </div>
821
  """
822
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
  with gr.Blocks(
824
- title="CHEX - Document Intelligence",
825
  theme=gr.themes.Base(),
826
  css=CHEX_CSS,
827
  ) as demo:
 
 
828
  gr.HTML(TOPBAR_HTML)
829
 
 
830
  if WARNING_HTML:
831
  gr.HTML(WARNING_HTML)
832
 
 
833
  with gr.Tabs():
834
- # ------------------------------------------------------------------ #
835
- # Tab 1: Analyze Contract #
836
- # ------------------------------------------------------------------ #
837
- with gr.Tab("Analyze Contract"):
838
- with gr.Row():
839
- # Left column: contract input
840
- with gr.Column(scale=2):
841
- gr.Markdown("### Contract Text")
 
 
 
842
  contract_input = gr.Textbox(
843
- label="Paste contract text here",
844
- lines=22,
845
- placeholder="Paste your contract text here, or load a sample contract below...",
846
  show_label=False,
847
  )
 
848
  with gr.Row():
849
- btn_software = gr.Button("Load: Software License", size="sm")
850
- btn_nda = gr.Button("Load: NDA", size="sm")
851
- btn_service = gr.Button("Load: Service Agreement", size="sm")
852
-
853
- # Right column: question + results
854
- with gr.Column(scale=1):
855
- gr.Markdown("### Question")
856
- question_input = gr.Textbox(
857
- label="Question about the contract",
858
- placeholder="e.g., What is the limitation of liability?",
859
- lines=2,
860
- show_label=False,
861
- )
862
- analyze_btn = gr.Button(
863
- "Analyze",
864
- variant="primary",
865
- interactive=True,
866
- )
867
-
868
- gr.Markdown("### Result")
869
- label_display = gr.HTML(
870
- value=format_label_html("N/A"),
871
- label="Classification",
872
- )
873
- answer_output = gr.Textbox(
874
- label="Answer",
875
- interactive=False,
876
- lines=3,
877
- )
878
- citation_output = gr.Textbox(
879
- label="Citation (verbatim from contract)",
880
- interactive=False,
881
- lines=3,
882
- )
883
- reasoning_output = gr.Textbox(
884
- label="Reasoning",
885
- interactive=False,
886
- lines=2,
887
- )
888
-
889
- # Suggested questions shown when loading a sample
890
- suggested_q = gr.Markdown("", visible=False)
891
-
892
- # ------------------------------------------------------------------ #
893
- # Tab 2: Analyse Bank Statement #
894
- # ------------------------------------------------------------------ #
895
- with gr.Tab("Analyse Bank Statement"):
896
- with gr.Row():
897
- # Left column: statement input (3 sub-tabs)
898
- with gr.Column(scale=2):
899
- gr.Markdown("### Bank Statement Input")
900
  with gr.Tabs():
901
- with gr.Tab("Paste Text"):
902
  bank_paste_input = gr.Textbox(
903
- label="Paste bank statement text",
904
  lines=20,
905
- placeholder="Paste your bank statement here, or load the sample below...",
906
  show_label=False,
907
  )
908
- btn_load_statement = gr.Button("Load Sample Statement", size="sm")
909
  with gr.Tab("Upload PDF"):
910
- bank_pdf_input = gr.File(
911
- label="Upload PDF bank statement",
912
- file_types=[".pdf"],
913
- )
914
  with gr.Tab("Upload CSV"):
915
- bank_csv_input = gr.File(
916
- label="Upload CSV bank statement",
917
- file_types=[".csv"],
918
- )
919
-
920
- # Right column: summary + Q&A
921
- with gr.Column(scale=1):
922
- analyse_stmt_btn = gr.Button(
923
- "Analyse Statement",
924
- variant="primary",
925
- )
926
- summary_output = gr.Markdown(
927
- value="*Run 'Analyse Statement' to generate a financial summary.*"
928
- )
929
-
930
- gr.Markdown("---")
931
- gr.Markdown("### Ask a Question")
932
- bank_question_input = gr.Textbox(
933
- label="Question about the statement",
934
- placeholder="e.g., What was the largest debit this month?",
935
- lines=2,
936
- show_label=False,
937
- )
938
- bank_ask_btn = gr.Button("Ask", variant="secondary")
939
-
940
- gr.Markdown("### Q&A Result")
941
- bank_label_display = gr.HTML(
942
- value=format_label_html("N/A"),
943
- label="Classification",
944
- )
945
- bank_answer_output = gr.Textbox(
946
- label="Answer",
947
- interactive=False,
948
- lines=3,
949
- )
950
- bank_citation_output = gr.Textbox(
951
- label="Citation (verbatim from statement)",
952
- interactive=False,
953
- lines=3,
954
- )
955
- bank_reasoning_output = gr.Textbox(
956
- label="Reasoning",
957
- interactive=False,
958
- lines=2,
959
- )
960
 
961
- # Hidden state: extracted statement text shared between summary and Q&A
962
  bank_statement_state = gr.State("")
963
 
964
- # ------------------------------------------------------------------ #
965
- # Tab 2: Benchmark Demo #
966
- # ------------------------------------------------------------------ #
967
- with gr.Tab("Benchmark Demo"):
968
  gr.HTML(BENCH_INTRO_HTML)
969
  gr.Dataframe(
970
  value=BENCHMARK_DF,
@@ -974,29 +1329,51 @@ with gr.Blocks(
974
  interactive=False,
975
  )
976
 
977
- # ------------------------------------------------------------------ #
978
- # Event handlers #
979
- # ------------------------------------------------------------------ #
 
 
 
980
 
981
  def load_software():
 
 
 
 
 
 
982
  return (
983
  SOFTWARE_LICENSE,
984
  SAMPLE_QUESTIONS["software_license.txt"],
985
- gr.update(value="πŸ’‘ **Suggested question:** What is the limitation of liability in this agreement?", visible=True),
986
  )
987
 
988
  def load_nda():
 
 
 
 
 
 
989
  return (
990
  NDA,
991
  SAMPLE_QUESTIONS["nda.txt"],
992
- gr.update(value="πŸ’‘ **Suggested question:** Does this agreement include a non-compete clause?", visible=True),
993
  )
994
 
995
  def load_service():
 
 
 
 
 
 
 
996
  return (
997
  SERVICE_AGREEMENT,
998
  SAMPLE_QUESTIONS["service_agreement.txt"],
999
- gr.update(value="πŸ’‘ **Suggested question:** Does this contract include a termination for convenience clause? (expected: ABSENT)", visible=True),
1000
  )
1001
 
1002
  btn_software.click(
@@ -1021,16 +1398,14 @@ with gr.Blocks(
1021
  outputs=[label_display, answer_output, citation_output, reasoning_output],
1022
  )
1023
 
1024
- # Also trigger on Enter in question field
1025
  question_input.submit(
1026
  fn=analyze_contract,
1027
  inputs=[contract_input, question_input],
1028
  outputs=[label_display, answer_output, citation_output, reasoning_output],
1029
  )
1030
 
1031
- # ------------------------------------------------------------------ #
1032
- # Bank Statement event handlers #
1033
- # ------------------------------------------------------------------ #
1034
 
1035
  btn_load_statement.click(
1036
  fn=lambda: SAMPLE_STATEMENT,
@@ -1056,8 +1431,6 @@ with gr.Blocks(
1056
  outputs=[bank_label_display, bank_answer_output, bank_citation_output, bank_reasoning_output],
1057
  )
1058
 
1059
- gr.HTML(FOOTER_HTML)
1060
-
1061
 
1062
  if __name__ == "__main__":
1063
  demo.launch(show_error=True)
 
301
  BENCHMARK_DF = pd.DataFrame(BENCHMARK_ROWS)
302
 
303
  # ---------------------------------------------------------------------------
304
+ # Warning banner
305
  # ---------------------------------------------------------------------------
306
 
307
  WARNING_HTML = ""
308
  if model_load_error:
309
  WARNING_HTML = (
310
  '<div class="chex-banner">'
311
+ '<span class="chex-banner-icon">⚠</span>'
312
  f'<div class="chex-banner-body"><strong>Model not loaded</strong> Β· '
313
  f'{model_load_error} β€” set <code>HF_MODEL_REPO</code> in Space secrets.</div>'
314
  '</div>'
 
321
  CHEX_CSS = """
322
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
323
 
324
+ /* ── Reset ── */
325
  *, *::before, *::after { box-sizing: border-box; }
326
 
327
+ /* ── Design tokens ── */
328
  :root {
329
  --bg-base: #f3f4f7;
330
  --bg-grad: radial-gradient(ellipse 1200px 700px at 18% -10%, rgba(120,150,200,0.18), transparent 60%),
 
341
  --fg-muted: #5b6275;
342
  --fg-subtle: #8a91a3;
343
  --green: #0f9d58;
344
+ --green-bg: rgba(34,197,94,0.10);
345
+ --green-border: rgba(34,197,94,0.28);
346
+ --red: #d23131;
347
+ --red-bg: rgba(239,68,68,0.09);
348
+ --red-border: rgba(239,68,68,0.28);
349
  --amber: #b87800;
350
  --amber-bg: rgba(245,158,11,0.10);
351
  --amber-border: rgba(245,158,11,0.30);
352
  --blur: 22px;
353
  --blur-strong: 32px;
354
+ --shadow-md: 0 1px 0 rgba(255,255,255,0.6) inset,
355
+ 0 8px 24px rgba(15,18,30,0.06),
356
+ 0 1px 2px rgba(15,18,30,0.04);
357
  --radius: 10px;
358
  --radius-lg: 16px;
359
  }
360
 
361
+ /* ── Body & app shell ── */
362
+ body {
363
+ background: var(--bg-grad) !important;
364
+ background-attachment: fixed !important;
365
+ background-color: var(--bg-base) !important;
366
+ min-height: 100vh;
367
+ }
368
+
 
 
 
 
 
 
 
 
 
 
 
 
369
  .gradio-container {
370
  font-family: 'Inter', system-ui, -apple-system, sans-serif !important;
371
  font-size: 14px !important;
372
  line-height: 1.55 !important;
373
  color: var(--fg) !important;
374
+ background: transparent !important;
 
 
375
  -webkit-font-smoothing: antialiased !important;
376
+ -moz-osx-font-smoothing: grayscale !important;
377
  letter-spacing: -0.006em !important;
378
  max-width: 1480px !important;
379
+ margin: 0 auto !important;
380
+ padding: 0 !important;
381
  }
382
 
383
+ /* ── Nuke ALL Gradio chrome ── */
384
+ footer, .footer, .built-with, #footer,
385
+ footer.svelte-1ax1toq, .svelte-1ax1toq.footer,
386
+ .gradio-container > .footer,
387
+ .share-button, .copy-all-button,
388
+ .gradio-container > .top-panel { display: none !important; }
389
+
390
+ /* Strip the outer container's own bg/padding */
391
+ #root, .app, main {
392
+ background: transparent !important;
393
+ padding: 0 !important;
394
+ margin: 0 !important;
395
+ }
396
+
397
+ /* The inner .contain div Gradio wraps everything in */
398
+ .contain, .container {
399
+ padding: 0 !important;
400
+ gap: 0 !important;
401
+ max-width: 100% !important;
402
+ background: transparent !important;
403
+ }
404
+
405
+ /* Every .block Gradio creates β€” reset ALL chrome */
406
+ .block,
407
+ .gr-block,
408
+ .gr-box,
409
+ .gr-group,
410
+ .gradio-container .block {
411
+ background: transparent !important;
412
+ border: none !important;
413
+ box-shadow: none !important;
414
+ padding: 0 !important;
415
+ border-radius: 0 !important;
416
+ }
417
+
418
+ /* The padding/gap between row children */
419
+ .gap, .gr-row { gap: 20px !important; }
420
+
421
+ /* Panel wrappers */
422
+ .panel, .gr-panel, .gr-padded {
423
+ background: transparent !important;
424
+ border: none !important;
425
+ padding: 0 !important;
426
+ box-shadow: none !important;
427
+ }
428
+
429
+ /* Tabs outer wrapper */
430
+ .tabs, .gr-tabs {
431
+ background: transparent !important;
432
+ border: none !important;
433
+ }
434
+
435
+ /* Individual tab content areas */
436
+ .tabitem, .gr-tabitem {
437
+ background: transparent !important;
438
+ border: none !important;
439
+ padding: 24px !important;
440
+ }
441
+
442
+ /* Textbox wrappers β€” only reset the outer shell, let the inner textarea keep styling */
443
+ [data-testid="textbox"],
444
+ .gr-textbox {
445
+ background: transparent !important;
446
+ border: none !important;
447
+ box-shadow: none !important;
448
+ padding: 0 !important;
449
+ }
450
+
451
+ /* Label blocks */
452
+ label.block, .label-wrap {
453
+ background: transparent !important;
454
+ border: none !important;
455
+ padding: 0 !important;
456
+ gap: 6px !important;
457
+ display: flex !important;
458
+ flex-direction: column !important;
459
+ }
460
+
461
+ /* Row component */
462
+ .row, .gr-row {
463
+ background: transparent !important;
464
+ border: none !important;
465
+ padding: 0 !important;
466
+ }
467
+
468
+ /* Form groups */
469
+ .form, .gr-form {
470
+ background: transparent !important;
471
+ border: none !important;
472
+ box-shadow: none !important;
473
+ padding: 0 !important;
474
+ gap: 14px !important;
475
+ }
476
+
477
+ /* ── Topbar ── */
478
  .chex-topbar {
479
  display: flex;
480
  align-items: center;
481
  gap: 16px;
482
  padding: 0 28px;
483
  height: 60px;
484
+ position: sticky;
485
+ top: 0;
486
+ z-index: 100;
487
  background: var(--bg-elev);
488
  backdrop-filter: blur(var(--blur-strong)) saturate(160%);
489
  -webkit-backdrop-filter: blur(var(--blur-strong)) saturate(160%);
490
  border-bottom: 1px solid var(--hairline);
 
 
 
 
 
 
491
  }
492
+
493
  .chex-logo {
494
+ width: 26px;
495
+ height: 26px;
496
  border-radius: 8px;
497
+ background: linear-gradient(135deg, #0d1220, rgba(13,18,32,0.7));
498
+ color: #f3f4f7;
499
  display: grid;
500
  place-items: center;
501
  font-family: 'JetBrains Mono', monospace;
 
503
  font-size: 11px;
504
  letter-spacing: -0.05em;
505
  box-shadow: 0 4px 14px rgba(15,18,30,0.18), 0 1px 0 rgba(255,255,255,0.25) inset;
506
+ flex-shrink: 0;
507
  }
508
+
509
  .chex-name {
510
  font-size: 15px;
511
  font-weight: 600;
512
  letter-spacing: -0.01em;
513
  color: var(--fg);
514
+ font-family: 'Inter', sans-serif;
515
  }
516
+
517
  .chex-tag {
518
  font-size: 12px;
519
  color: var(--fg-muted);
520
  font-weight: 400;
521
  padding-left: 12px;
522
  border-left: 1px solid var(--hairline);
523
+ font-family: 'Inter', sans-serif;
524
  }
525
+
526
+ .chex-pill {
527
  display: inline-flex;
528
  align-items: center;
529
  gap: 8px;
 
537
  -webkit-backdrop-filter: blur(12px);
538
  font-family: 'JetBrains Mono', monospace;
539
  white-space: nowrap;
 
540
  }
541
+
542
+ .chex-dot {
543
+ width: 6px;
544
+ height: 6px;
545
  border-radius: 50%;
546
  background: var(--green);
547
  box-shadow: 0 0 0 3px rgba(15,157,88,0.22);
548
  display: inline-block;
549
+ flex-shrink: 0;
550
  }
551
 
552
+ /* ── Warning banner ── */
553
  .chex-banner {
554
  display: flex;
555
  align-items: center;
 
562
  color: var(--amber);
563
  font-size: 13px;
564
  font-family: 'Inter', sans-serif;
565
+ font-weight: 500;
566
  }
567
+ .chex-banner-icon { font-size: 14px; flex-shrink: 0; }
568
+ .chex-banner-body { color: var(--fg); font-weight: 400; line-height: 1.5; }
569
  .chex-banner-body strong { color: var(--fg); font-weight: 600; }
570
  .chex-banner code {
571
  font-family: 'JetBrains Mono', monospace;
 
575
  border-radius: 4px;
576
  }
577
 
578
+ /* ── Tab bar ── */
579
  .tab-nav {
580
  background: var(--bg-elev) !important;
581
  backdrop-filter: blur(var(--blur)) saturate(160%) !important;
582
  -webkit-backdrop-filter: blur(var(--blur)) saturate(160%) !important;
583
  border-bottom: 1px solid var(--hairline) !important;
584
+ border-top: none !important;
585
  padding: 0 20px !important;
586
+ gap: 0 !important;
587
+ position: sticky !important;
588
+ top: 60px !important;
589
+ z-index: 99 !important;
590
+ overflow: visible !important;
591
  }
592
+
593
  .tab-nav button {
594
  background: transparent !important;
595
  border: none !important;
 
599
  font-size: 13px !important;
600
  font-weight: 500 !important;
601
  font-family: 'Inter', sans-serif !important;
602
+ letter-spacing: -0.003em !important;
603
  position: relative !important;
604
  white-space: nowrap !important;
605
+ transition: color 0.15s ease !important;
606
+ cursor: pointer !important;
607
+ box-shadow: none !important;
608
+ outline: none !important;
609
  }
610
+
611
+ .tab-nav button:hover {
612
  color: var(--fg) !important;
613
  background: transparent !important;
614
  }
615
+
616
+ .tab-nav button.selected,
617
+ .tab-nav button[aria-selected="true"] {
618
+ color: var(--fg) !important;
619
+ background: transparent !important;
620
+ font-weight: 500 !important;
621
+ box-shadow: none !important;
622
+ }
623
+
624
+ .tab-nav button.selected::after,
625
+ .tab-nav button[aria-selected="true"]::after {
626
  content: "";
627
  position: absolute;
628
+ left: 12px;
629
+ right: 12px;
630
+ bottom: -1px;
631
  height: 1.5px;
632
  background: var(--fg);
633
  border-radius: 2px 2px 0 0;
634
  }
635
 
636
+ /* Tab content panels */
637
+ .tabitem {
638
+ border: none !important;
639
+ background: transparent !important;
640
+ padding: 24px 24px !important;
 
 
 
 
 
641
  }
642
+
643
+ /* ── Card components ── */
644
+ .chex-card,
645
+ .gradio-container .gr-group.chex-card-group,
646
+ .gradio-container [data-testid="group"].chex-card-group {
647
+ background: var(--bg-elev) !important;
648
+ backdrop-filter: blur(var(--blur)) saturate(180%) !important;
649
+ -webkit-backdrop-filter: blur(var(--blur)) saturate(180%) !important;
650
+ border: 1px solid var(--border) !important;
651
+ border-radius: var(--radius-lg) !important;
652
+ box-shadow: var(--shadow-md) !important;
653
+ overflow: hidden !important;
654
+ padding: 0 !important;
655
+ }
656
+
657
+ /* Groups used as cards */
658
+ .gradio-container .gr-group {
659
+ background: var(--bg-elev) !important;
660
+ backdrop-filter: blur(var(--blur)) saturate(180%) !important;
661
+ -webkit-backdrop-filter: blur(var(--blur)) saturate(180%) !important;
662
+ border: 1px solid var(--border) !important;
663
+ border-radius: var(--radius-lg) !important;
664
+ box-shadow: var(--shadow-md) !important;
665
+ overflow: hidden !important;
666
+ padding: 0 !important;
667
+ }
668
+
669
+ /* Inner content of groups gets consistent padding */
670
+ .gradio-container .gr-group > *:not(.chex-card-header):not(.chex-chip-row) {
671
+ padding-left: 20px !important;
672
+ padding-right: 20px !important;
673
+ }
674
+ .gradio-container .gr-group > *:last-child {
675
+ padding-bottom: 18px !important;
676
+ }
677
+
678
  .chex-card-header {
679
  padding: 16px 20px;
680
  display: flex;
 
683
  gap: 12px;
684
  border-bottom: 1px solid var(--hairline);
685
  }
686
+
687
  .chex-card-title {
688
  font-size: 13.5px;
689
  font-weight: 600;
 
693
  gap: 10px;
694
  color: var(--fg);
695
  white-space: nowrap;
696
+ font-family: 'Inter', sans-serif;
697
  }
698
+
699
  .chex-card-kicker {
700
  font-family: 'JetBrains Mono', monospace;
701
  font-size: 11px;
702
  color: var(--fg-subtle);
703
  font-weight: 400;
704
+ letter-spacing: 0.04em;
705
  }
706
 
707
+ /* ── Chip row (load samples) ── */
708
+ .chex-chip-row {
709
+ display: flex;
710
+ align-items: center;
711
+ gap: 8px;
712
+ padding: 12px 20px;
713
+ border-top: 1px solid var(--hairline);
714
+ background: var(--bg-sunken);
715
+ flex-wrap: wrap;
716
+ }
717
+
718
+ .chex-chip-label {
719
+ font-family: 'JetBrains Mono', monospace;
720
+ font-size: 10.5px;
721
+ text-transform: uppercase;
722
+ letter-spacing: 0.08em;
723
+ color: var(--fg-subtle);
724
+ white-space: nowrap;
725
+ margin-right: 4px;
726
+ }
727
+
728
+ /* ── Suggested question bar ── */
729
+ .chex-suggested {
730
+ display: flex;
731
+ align-items: center;
732
+ gap: 10px;
733
+ padding: 10px 14px;
734
+ background: rgba(13,18,32,0.04);
735
+ border: 1px solid var(--border);
736
+ border-radius: var(--radius);
737
+ font-size: 12.5px;
738
+ color: var(--fg-muted);
739
+ font-family: 'Inter', sans-serif;
740
+ line-height: 1.4;
741
+ margin-top: 2px;
742
+ }
743
+
744
+ .chex-suggested-icon {
745
+ font-size: 13px;
746
+ flex-shrink: 0;
747
+ opacity: 0.7;
748
+ }
749
+
750
+
751
+ /* ── Labels on inputs ── */
752
+ label > span:first-child,
753
+ .label-wrap span,
754
+ .gradio-container label span.text-gray-500,
755
+ span.svelte-1b6s6s {
756
+ font-family: 'JetBrains Mono', monospace !important;
757
+ font-size: 10.5px !important;
758
+ font-weight: 500 !important;
759
+ text-transform: uppercase !important;
760
+ letter-spacing: 0.08em !important;
761
+ color: var(--fg-subtle) !important;
762
+ margin-bottom: 6px !important;
763
+ display: block !important;
764
+ }
765
+
766
+ /* ── Textareas & inputs ── */
767
+ textarea,
768
+ input[type="text"],
769
+ input[type="search"],
770
  .gradio-container .gr-input,
771
+ .gradio-container .gr-textarea,
772
+ .gradio-container [data-testid="textbox"] textarea,
773
+ .gradio-container [data-testid="textbox"] input {
774
  background: var(--bg-input) !important;
775
  backdrop-filter: blur(10px) !important;
776
  -webkit-backdrop-filter: blur(10px) !important;
 
779
  color: var(--fg) !important;
780
  font-family: 'Inter', sans-serif !important;
781
  font-size: 13px !important;
782
+ line-height: 1.6 !important;
783
+ padding: 11px 14px !important;
784
+ transition: border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease !important;
785
+ resize: vertical !important;
786
  }
787
+
788
+ textarea:focus,
789
+ input[type="text"]:focus,
790
+ .gradio-container [data-testid="textbox"] textarea:focus,
791
+ .gradio-container [data-testid="textbox"] input:focus {
792
  border-color: var(--border-strong) !important;
793
  background: var(--bg-elev-strong) !important;
794
  box-shadow: 0 0 0 4px rgba(13,18,32,0.08) !important;
795
  outline: none !important;
796
  }
797
 
798
+ textarea::placeholder,
799
+ input::placeholder {
 
 
 
 
 
 
800
  color: var(--fg-subtle) !important;
801
  }
802
 
803
+ /* Read-only / output textboxes */
804
+ textarea[readonly],
805
+ .gradio-container [data-testid="textbox"][data-interactive="false"] textarea {
806
+ background: var(--bg-sunken) !important;
807
+ border: 1px solid var(--hairline) !important;
808
+ color: var(--fg) !important;
809
+ cursor: default !important;
810
+ }
811
+
812
  /* ── Buttons ── */
813
+ .gradio-container button {
814
+ font-family: 'Inter', sans-serif !important;
815
+ font-size: 13px !important;
816
+ font-weight: 500 !important;
817
+ border-radius: var(--radius) !important;
818
+ padding: 10px 16px !important;
819
+ transition: opacity 0.15s ease, background 0.15s ease, box-shadow 0.15s ease !important;
820
+ cursor: pointer !important;
821
+ letter-spacing: -0.003em !important;
822
+ }
823
+
824
  .gradio-container button.primary,
825
+ .gradio-container [data-testid="button"][variant="primary"],
826
+ button.primary {
827
  background: var(--fg) !important;
828
  color: var(--bg-base) !important;
829
  border: 1px solid var(--fg) !important;
830
+ box-shadow: 0 6px 18px rgba(13,18,32,0.28), 0 1px 0 rgba(255,255,255,0.1) inset !important;
 
 
 
 
 
 
831
  }
832
+
833
  .gradio-container button.primary:hover,
834
+ button.primary:hover {
835
+ opacity: 0.88 !important;
836
+ box-shadow: 0 4px 12px rgba(13,18,32,0.22) !important;
837
+ }
838
 
839
  .gradio-container button.secondary,
840
+ button.secondary {
841
  background: var(--bg-elev) !important;
842
  backdrop-filter: blur(10px) !important;
843
+ -webkit-backdrop-filter: blur(10px) !important;
844
  color: var(--fg) !important;
845
  border: 1px solid var(--border) !important;
846
+ box-shadow: var(--shadow-md) !important;
 
 
 
 
 
847
  }
848
+
849
  .gradio-container button.secondary:hover,
850
+ button.secondary:hover {
851
  background: var(--bg-elev-strong) !important;
852
  border-color: var(--border-strong) !important;
853
  }
854
 
855
+ /* Small / sm-size buttons */
856
+ button.sm,
857
+ .gradio-container button[size="sm"],
858
+ button.small {
859
  font-size: 12px !important;
860
  padding: 7px 11px !important;
861
  }
862
 
863
+ /* ── File upload ── */
864
+ .gradio-container .upload-container,
865
+ .gradio-container [data-testid="file"] {
866
+ background: var(--bg-input) !important;
867
+ border: 1px dashed var(--border-strong) !important;
868
+ border-radius: var(--radius) !important;
869
+ }
870
+
871
  /* ── Dataframe / benchmark table ── */
872
+ .gradio-container .wrap.svelte-a4gbbr,
873
+ .gradio-container .table-wrap,
874
+ .gradio-container [data-testid="dataframe"] {
875
  background: var(--bg-elev) !important;
876
  backdrop-filter: blur(var(--blur)) saturate(180%) !important;
877
+ -webkit-backdrop-filter: blur(var(--blur)) saturate(180%) !important;
878
  border: 1px solid var(--border) !important;
879
  border-radius: var(--radius-lg) !important;
880
  box-shadow: var(--shadow-md) !important;
881
+ overflow: hidden !important;
882
+ }
883
+
884
+ .gradio-container table {
885
+ background: transparent !important;
886
  font-size: 13px !important;
887
  font-family: 'Inter', sans-serif !important;
888
  border-collapse: separate !important;
889
  border-spacing: 0 !important;
 
890
  width: 100% !important;
891
+ border: none !important;
892
+ box-shadow: none !important;
893
+ border-radius: 0 !important;
894
  }
895
+
896
  .gradio-container th {
897
  background: var(--bg-sunken) !important;
898
  border-bottom: 1px solid var(--hairline) !important;
899
+ border-top: none !important;
900
  padding: 14px 18px !important;
901
  font-family: 'JetBrains Mono', monospace !important;
902
  font-size: 10.5px !important;
 
906
  font-weight: 500 !important;
907
  text-align: left !important;
908
  }
909
+
910
  .gradio-container td {
911
  padding: 16px 18px !important;
912
  border-top: 1px solid var(--hairline) !important;
913
+ border-bottom: none !important;
914
  vertical-align: top !important;
915
  line-height: 1.6 !important;
916
  color: var(--fg) !important;
917
+ background: transparent !important;
918
  }
919
+
920
  .gradio-container tr:first-child td { border-top: none !important; }
921
 
922
+ /* Hallucinated rows β€” rows where 'Hallucinated?' is YES */
923
+ .gradio-container tr:has(td:last-child:contains("YES")) td,
924
+ .chex-hallucinated-row td {
925
+ background: color-mix(in srgb, var(--red-bg) 4%, transparent) !important;
926
+ box-shadow: inset 2px 0 0 var(--red) !important;
927
+ }
928
+
929
+ /* ── Markdown output ── */
930
  .gradio-container .prose,
931
+ .gradio-container .md,
932
+ .gradio-container [data-testid="markdown"] {
933
  color: var(--fg) !important;
934
  font-family: 'Inter', sans-serif !important;
935
+ font-size: 13px !important;
936
+ line-height: 1.65 !important;
937
  }
938
+
939
+ .gradio-container .prose h2,
940
+ .gradio-container .md h2 {
941
+ font-size: 18px !important;
942
  font-weight: 600 !important;
943
  letter-spacing: -0.02em !important;
944
  color: var(--fg) !important;
945
  margin-bottom: 10px !important;
946
+ margin-top: 0 !important;
947
  }
948
+
949
+ .gradio-container .prose h3,
950
+ .gradio-container .md h3 {
951
+ font-size: 13.5px !important;
952
  font-weight: 600 !important;
953
  letter-spacing: -0.01em !important;
954
  color: var(--fg) !important;
955
  margin-bottom: 8px !important;
956
+ margin-top: 16px !important;
957
  }
958
+
959
+ .gradio-container .prose p,
960
+ .gradio-container .md p {
961
  color: var(--fg-muted) !important;
962
  font-size: 13px !important;
963
  line-height: 1.65 !important;
964
+ margin-bottom: 8px !important;
965
+ }
966
+
967
+ .gradio-container .prose strong,
968
+ .gradio-container .md strong {
969
+ color: var(--fg) !important;
970
+ font-weight: 600 !important;
971
+ }
972
+
973
+ .gradio-container .prose code,
974
+ .gradio-container .md code {
975
+ font-family: 'JetBrains Mono', monospace !important;
976
+ font-size: 12px !important;
977
+ background: rgba(13,18,32,0.06) !important;
978
+ padding: 1px 5px !important;
979
+ border-radius: 4px !important;
980
+ color: var(--fg) !important;
981
  }
982
 
983
  /* ── Bench intro card ── */
 
989
  border-radius: var(--radius-lg);
990
  box-shadow: var(--shadow-md);
991
  padding: 24px 28px;
992
+ margin-bottom: 20px;
993
  }
994
+
995
  .chex-bench-intro h2 {
996
  margin: 0 0 10px;
997
  font-size: 19px;
998
  font-weight: 600;
999
  letter-spacing: -0.02em;
1000
  color: var(--fg);
1001
+ font-family: 'Inter', sans-serif;
1002
  }
1003
+
1004
  .chex-bench-intro p {
1005
  margin: 0;
1006
  color: var(--fg-muted);
 
1008
  line-height: 1.65;
1009
  font-family: 'Inter', sans-serif;
1010
  }
1011
+
1012
  .chex-bench-stats {
1013
  display: grid;
1014
  grid-template-columns: repeat(3, 1fr);
1015
  gap: 8px;
1016
  margin-top: 18px;
1017
  }
1018
+
1019
  .chex-bench-stat {
1020
  background: var(--bg-sunken);
1021
  border: 1px solid var(--hairline);
1022
  border-radius: var(--radius);
1023
  padding: 12px 14px;
1024
  }
1025
+
1026
  .chex-bench-stat .v {
1027
  font-family: 'Inter', sans-serif;
1028
  font-size: 20px;
1029
  font-weight: 600;
1030
  letter-spacing: -0.025em;
1031
  color: var(--fg);
1032
+ line-height: 1.2;
1033
+ margin-bottom: 4px;
1034
  }
1035
+
1036
+ .chex-bench-stat .v.red { color: var(--red); }
1037
+ .chex-bench-stat .v.green { color: var(--green); }
1038
+
1039
  .chex-bench-stat .k {
1040
  font-size: 10px;
1041
  text-transform: uppercase;
 
1057
  background: var(--bg-elev);
1058
  backdrop-filter: blur(var(--blur));
1059
  -webkit-backdrop-filter: blur(var(--blur));
1060
+ margin-top: 32px;
1061
  }
 
1062
 
1063
+ .chex-footer .sep { opacity: 0.4; }
1064
+
1065
+ /* ── Result label container ── */
1066
+ .chex-label-wrap {
1067
+ padding: 4px 0 8px;
1068
+ }
1069
+
1070
+ /* ── Divider ── */
1071
+ .chex-divider {
1072
+ height: 1px;
1073
+ background: var(--hairline);
1074
+ margin: 18px 0;
1075
+ }
1076
+
1077
+ /* ── Section kicker ── */
1078
+ .chex-section-kicker {
1079
+ font-family: 'JetBrains Mono', monospace;
1080
+ font-size: 10.5px;
1081
+ text-transform: uppercase;
1082
+ letter-spacing: 0.08em;
1083
+ color: var(--fg-subtle);
1084
+ margin-bottom: 10px;
1085
+ display: block;
1086
+ }
1087
+
1088
+ /* ── Card body padding ── */
1089
+ .chex-card-body {
1090
+ padding: 18px 20px;
1091
+ display: flex;
1092
+ flex-direction: column;
1093
+ gap: 14px;
1094
  }
1095
 
1096
+ /* ── Scrollbars ── */
1097
+ *::-webkit-scrollbar { width: 8px; height: 8px; }
1098
  *::-webkit-scrollbar-thumb {
1099
  background: var(--border-strong);
1100
  border-radius: 999px;
 
1102
  background-clip: padding-box;
1103
  }
1104
  *::-webkit-scrollbar-track { background: transparent; }
1105
+
1106
+ /* ── Gradio utility gaps ── */
1107
+ .gradio-container .gap-4 { gap: 14px !important; }
1108
+ .gradio-container .gap-2 { gap: 8px !important; }
1109
+
1110
+ /* Nested sub-tabs (bank statement) */
1111
+ .tabitem .tab-nav {
1112
+ position: static !important;
1113
+ top: auto !important;
1114
+ }
1115
+
1116
+ /* Responsive spacing */
1117
+ @media (max-width: 900px) {
1118
+ .chex-topbar { padding: 0 16px; }
1119
+ .chex-tag { display: none; }
1120
+ .tabitem { padding: 16px !important; }
1121
+ .chex-bench-stats { grid-template-columns: 1fr; }
1122
+ .chex-footer { padding: 12px 16px; gap: 12px; flex-wrap: wrap; }
1123
+ }
1124
  """
1125
 
1126
+ # ---------------------------------------------------------------------------
1127
+ # Static HTML strings
1128
+ # ---------------------------------------------------------------------------
1129
+
1130
  TOPBAR_HTML = """
1131
  <div class="chex-topbar">
1132
+ <div class="chex-logo">CX</div>
1133
+ <span class="chex-name">CHEX</span>
1134
+ <span class="chex-tag">grounded answers from documents</span>
1135
+ <div style="flex:1"></div>
1136
+ <div class="chex-pill"><span class="chex-dot"></span>MI300X Β· ready</div>
 
 
 
 
1137
  </div>
1138
  """
1139
 
 
1161
  </div>
1162
  """
1163
 
1164
+ CONTRACT_SOURCE_HEADER_HTML = """
1165
+ <div class="chex-card-header">
1166
+ <span class="chex-card-title">
1167
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:0.55"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
1168
+ Source Document
1169
+ </span>
1170
+ <span class="chex-card-kicker">paste Β· load sample</span>
1171
+ </div>
1172
+ """
1173
+
1174
+ CONTRACT_RESULTS_HEADER_HTML = """
1175
+ <div class="chex-card-header">
1176
+ <span class="chex-card-title">
1177
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:0.55"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
1178
+ Analysis
1179
+ </span>
1180
+ <span class="chex-card-kicker">grounded Β· cited Β· structured</span>
1181
+ </div>
1182
+ """
1183
+
1184
+ CHIP_ROW_HTML = """
1185
+ <div class="chex-chip-row">
1186
+ <span class="chex-chip-label">Load sample</span>
1187
+ </div>
1188
+ """
1189
+
1190
+ STATEMENT_SOURCE_HEADER_HTML = """
1191
+ <div class="chex-card-header">
1192
+ <span class="chex-card-title">
1193
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:0.55"><rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/></svg>
1194
+ Bank Statement
1195
+ </span>
1196
+ <span class="chex-card-kicker">paste Β· pdf Β· csv</span>
1197
+ </div>
1198
+ """
1199
+
1200
+ STATEMENT_RESULTS_HEADER_HTML = """
1201
+ <div class="chex-card-header">
1202
+ <span class="chex-card-title">
1203
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:0.55"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
1204
+ Summary &amp; Q&amp;A
1205
+ </span>
1206
+ <span class="chex-card-kicker">summarise Β· ask Β· verify</span>
1207
+ </div>
1208
+ """
1209
+
1210
+ # ---------------------------------------------------------------------------
1211
+ # Gradio UI
1212
+ # ---------------------------------------------------------------------------
1213
+
1214
  with gr.Blocks(
1215
+ title="CHEX β€” Document Intelligence",
1216
  theme=gr.themes.Base(),
1217
  css=CHEX_CSS,
1218
  ) as demo:
1219
+
1220
+ # ── Topbar ──────────────────────────────────────────────────────────── #
1221
  gr.HTML(TOPBAR_HTML)
1222
 
1223
+ # ── Warning banner (only if model failed) ───────────────────────────── #
1224
  if WARNING_HTML:
1225
  gr.HTML(WARNING_HTML)
1226
 
1227
+ # ── Tabs ────────────────────────────────────────────────────────────── #
1228
  with gr.Tabs():
1229
+
1230
+ # ================================================================== #
1231
+ # Tab 01 β€” Contract Analysis #
1232
+ # ================================================================== #
1233
+ with gr.Tab("01 Contract analysis"):
1234
+ with gr.Row(equal_height=False):
1235
+
1236
+ # ── Left panel: source document ──────────────────────────── #
1237
+ with gr.Column(scale=9):
1238
+ with gr.Group():
1239
+ gr.HTML(CONTRACT_SOURCE_HEADER_HTML)
1240
  contract_input = gr.Textbox(
1241
+ label="Contract text",
1242
+ lines=20,
1243
+ placeholder="Paste your contract text here, or load a sample below…",
1244
  show_label=False,
1245
  )
1246
+ gr.HTML(CHIP_ROW_HTML)
1247
  with gr.Row():
1248
+ btn_software = gr.Button("Software License", variant="secondary", size="sm")
1249
+ btn_nda = gr.Button("NDA", variant="secondary", size="sm")
1250
+ btn_service = gr.Button("Service Agreement", variant="secondary", size="sm")
1251
+ suggested_q = gr.HTML(value="", visible=False)
1252
+
1253
+ # ── Right panel: question + results ──────────────────────── #
1254
+ with gr.Column(scale=11):
1255
+ with gr.Group():
1256
+ gr.HTML(CONTRACT_RESULTS_HEADER_HTML)
1257
+ with gr.Row():
1258
+ question_input = gr.Textbox(
1259
+ label="Question",
1260
+ placeholder="e.g., What is the limitation of liability?",
1261
+ lines=1,
1262
+ show_label=False,
1263
+ scale=8,
1264
+ )
1265
+ analyze_btn = gr.Button("Analyze ↡", variant="primary", scale=2)
1266
+ label_display = gr.HTML(value=format_label_html("N/A"))
1267
+ answer_output = gr.Textbox(label="Answer", interactive=False, lines=3)
1268
+ citation_output = gr.Textbox(label="Citation", interactive=False, lines=2)
1269
+ reasoning_output = gr.Textbox(label="Reasoning", interactive=False, lines=3)
1270
+
1271
+ # ================================================================== #
1272
+ # Tab 02 β€” Bank Statements #
1273
+ # ================================================================== #
1274
+ with gr.Tab("02 Bank statements"):
1275
+ with gr.Row(equal_height=False):
1276
+
1277
+ # ── Left panel: statement input ───────────────────────────── #
1278
+ with gr.Column(scale=9):
1279
+ with gr.Group():
1280
+ gr.HTML(STATEMENT_SOURCE_HEADER_HTML)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1281
  with gr.Tabs():
1282
+ with gr.Tab("Paste text"):
1283
  bank_paste_input = gr.Textbox(
1284
+ label="Bank statement text",
1285
  lines=20,
1286
+ placeholder="Paste your bank statement here, or load the sample below…",
1287
  show_label=False,
1288
  )
1289
+ btn_load_statement = gr.Button("Load sample statement", variant="secondary", size="sm")
1290
  with gr.Tab("Upload PDF"):
1291
+ bank_pdf_input = gr.File(label="PDF bank statement", file_types=[".pdf"])
 
 
 
1292
  with gr.Tab("Upload CSV"):
1293
+ bank_csv_input = gr.File(label="CSV bank statement", file_types=[".csv"])
1294
+
1295
+ # ── Right panel: summary + Q&A ───────────────────────────── #
1296
+ with gr.Column(scale=11):
1297
+ with gr.Group():
1298
+ gr.HTML(STATEMENT_RESULTS_HEADER_HTML)
1299
+ analyse_stmt_btn = gr.Button("Analyse statement", variant="primary")
1300
+ summary_output = gr.Markdown(value="*Run 'Analyse statement' to generate a financial summary.*")
1301
+ gr.HTML('<div class="chex-divider"></div>')
1302
+ gr.HTML('<span class="chex-section-kicker">Ask a question</span>')
1303
+ with gr.Row():
1304
+ bank_question_input = gr.Textbox(
1305
+ label="Question",
1306
+ placeholder="e.g., What was the largest debit this month?",
1307
+ lines=1,
1308
+ show_label=False,
1309
+ scale=8,
1310
+ )
1311
+ bank_ask_btn = gr.Button("Ask ↡", variant="secondary", scale=2)
1312
+ bank_label_display = gr.HTML(value=format_label_html("N/A"))
1313
+ bank_answer_output = gr.Textbox(label="Answer", interactive=False, lines=3)
1314
+ bank_citation_output = gr.Textbox(label="Citation", interactive=False, lines=2)
1315
+ bank_reasoning_output = gr.Textbox(label="Reasoning", interactive=False, lines=3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1316
 
 
1317
  bank_statement_state = gr.State("")
1318
 
1319
+ # ================================================================== #
1320
+ # Tab 03 β€” Benchmark #
1321
+ # ================================================================== #
1322
+ with gr.Tab("03 Benchmark"):
1323
  gr.HTML(BENCH_INTRO_HTML)
1324
  gr.Dataframe(
1325
  value=BENCHMARK_DF,
 
1329
  interactive=False,
1330
  )
1331
 
1332
+ # ── Footer ──────────────────────────────────────────────────────────── #
1333
+ gr.HTML(FOOTER_HTML)
1334
+
1335
+ # ====================================================================== #
1336
+ # Event handlers #
1337
+ # ====================================================================== #
1338
 
1339
  def load_software():
1340
+ hint = (
1341
+ '<div class="chex-suggested">'
1342
+ '<span class="chex-suggested-icon">πŸ’‘</span>'
1343
+ '<span><strong>Suggested:</strong> What is the limitation of liability in this agreement?</span>'
1344
+ '</div>'
1345
+ )
1346
  return (
1347
  SOFTWARE_LICENSE,
1348
  SAMPLE_QUESTIONS["software_license.txt"],
1349
+ gr.update(value=hint, visible=True),
1350
  )
1351
 
1352
  def load_nda():
1353
+ hint = (
1354
+ '<div class="chex-suggested">'
1355
+ '<span class="chex-suggested-icon">πŸ’‘</span>'
1356
+ '<span><strong>Suggested:</strong> Does this agreement include a non-compete clause?</span>'
1357
+ '</div>'
1358
+ )
1359
  return (
1360
  NDA,
1361
  SAMPLE_QUESTIONS["nda.txt"],
1362
+ gr.update(value=hint, visible=True),
1363
  )
1364
 
1365
  def load_service():
1366
+ hint = (
1367
+ '<div class="chex-suggested">'
1368
+ '<span class="chex-suggested-icon">πŸ’‘</span>'
1369
+ '<span><strong>Suggested:</strong> Does this contract include a termination for convenience clause? '
1370
+ '<em>(expected: ABSENT)</em></span>'
1371
+ '</div>'
1372
+ )
1373
  return (
1374
  SERVICE_AGREEMENT,
1375
  SAMPLE_QUESTIONS["service_agreement.txt"],
1376
+ gr.update(value=hint, visible=True),
1377
  )
1378
 
1379
  btn_software.click(
 
1398
  outputs=[label_display, answer_output, citation_output, reasoning_output],
1399
  )
1400
 
1401
+ # Trigger on Enter in question field
1402
  question_input.submit(
1403
  fn=analyze_contract,
1404
  inputs=[contract_input, question_input],
1405
  outputs=[label_display, answer_output, citation_output, reasoning_output],
1406
  )
1407
 
1408
+ # ── Bank Statement handlers ──────────────────────────────────────────── #
 
 
1409
 
1410
  btn_load_statement.click(
1411
  fn=lambda: SAMPLE_STATEMENT,
 
1431
  outputs=[bank_label_display, bank_answer_output, bank_citation_output, bank_reasoning_output],
1432
  )
1433
 
 
 
1434
 
1435
  if __name__ == "__main__":
1436
  demo.launch(show_error=True)