Abrar55 commited on
Commit
65a4729
Β·
verified Β·
1 Parent(s): 08663d7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +135 -831
app.py CHANGED
@@ -1042,713 +1042,34 @@ if model_load_error:
1042
  # ---------------------------------------------------------------------------
1043
 
1044
  CHEX_CSS = """
1045
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
1046
-
1047
  *, *::before, *::after { box-sizing: border-box; }
1048
 
1049
  :root {
1050
- --bg-base: #0B0E14;
1051
- --bg-grad: linear-gradient(180deg, #0B0E14 0%, #06080C 100%);
1052
- --bg-elev: #131720;
1053
- --bg-elev-strong: #191E2B;
1054
- --bg-sunken: #0E121A;
1055
- --bg-input: rgba(0,0,0,0.2);
1056
- --border: rgba(255,255,255,0.06);
1057
- --border-strong: rgba(255,255,255,0.12);
1058
- --hairline: rgba(255,255,255,0.03);
1059
- --fg: #E2E8F0;
1060
- --fg-muted: #94A3B8;
1061
- --fg-subtle: #475569;
1062
- --green: #10B981;
1063
- --green-bg: rgba(16,185,129,0.10);
1064
- --green-border: rgba(16,185,129,0.25);
1065
- --red: #F43F5E;
1066
- --red-bg: rgba(244,63,94,0.10);
1067
- --red-border: rgba(244,63,94,0.25);
1068
- --amber: #F59E0B;
1069
- --amber-bg: rgba(245,158,11,0.10);
1070
- --amber-border: rgba(245,158,11,0.25);
1071
- --blur: 24px;
1072
- --blur-strong: 32px;
1073
- --shadow-md: 0 1px 0 rgba(255,255,255,0.03) inset,
1074
- 0 8px 24px rgba(0,0,0,0.4),
1075
- 0 1px 2px rgba(0,0,0,0.2);
1076
- --radius: 10px;
1077
- --radius-lg: 14px;
1078
- }
1079
-
1080
- body {
1081
- background: var(--bg-grad) !important;
1082
- background-attachment: fixed !important;
1083
- background-color: var(--bg-base) !important;
1084
- min-height: 100vh;
1085
- }
1086
-
1087
- .gradio-container {
1088
- font-family: 'Inter', system-ui, -apple-system, sans-serif !important;
1089
- font-size: 14px !important;
1090
- line-height: 1.55 !important;
1091
- color: var(--fg) !important;
1092
- background: transparent !important;
1093
- -webkit-font-smoothing: antialiased !important;
1094
- -moz-osx-font-smoothing: grayscale !important;
1095
- letter-spacing: -0.006em !important;
1096
- max-width: 1480px !important;
1097
- margin: 0 auto !important;
1098
- padding: 0 !important;
1099
- }
1100
-
1101
- footer, .footer, .built-with, #footer,
1102
- footer.svelte-1ax1toq, .svelte-1ax1toq.footer,
1103
- .gradio-container > .footer,
1104
- .share-button, .copy-all-button,
1105
- .gradio-container > .top-panel { display: none !important; }
1106
-
1107
- #root, .app, main {
1108
- background: transparent !important;
1109
- padding: 0 !important;
1110
- margin: 0 !important;
1111
- }
1112
-
1113
- .contain, .container {
1114
- padding: 0 !important;
1115
- gap: 0 !important;
1116
- max-width: 100% !important;
1117
- background: transparent !important;
1118
- }
1119
-
1120
- .block, .gr-block, .gr-box, .gr-group, .gradio-container .block {
1121
- background: transparent !important;
1122
- border: none !important;
1123
- box-shadow: none !important;
1124
- padding: 0 !important;
1125
- border-radius: 0 !important;
1126
- }
1127
-
1128
- .gap, .gr-row { gap: 20px !important; }
1129
-
1130
- .panel, .gr-panel, .gr-padded {
1131
- background: transparent !important;
1132
- border: none !important;
1133
- padding: 0 !important;
1134
- box-shadow: none !important;
1135
- }
1136
-
1137
- .tabs, .gr-tabs { background: transparent !important; border: none !important; }
1138
-
1139
- .tabitem, .gr-tabitem {
1140
- background: transparent !important;
1141
- border: none !important;
1142
- padding: 24px !important;
1143
- }
1144
-
1145
- [data-testid="textbox"], .gr-textbox {
1146
- background: transparent !important;
1147
- border: none !important;
1148
- box-shadow: none !important;
1149
- padding: 0 !important;
1150
- }
1151
-
1152
- label.block, .label-wrap {
1153
- background: transparent !important;
1154
- border: none !important;
1155
- padding: 0 !important;
1156
- gap: 6px !important;
1157
- display: flex !important;
1158
- flex-direction: column !important;
1159
- }
1160
-
1161
- .row, .gr-row { background: transparent !important; border: none !important; padding: 0 !important; }
1162
-
1163
- .form, .gr-form {
1164
- background: transparent !important;
1165
- border: none !important;
1166
- box-shadow: none !important;
1167
- padding: 0 !important;
1168
- gap: 14px !important;
1169
- }
1170
-
1171
- /* ── Topbar ─────────────────────────────────────────────────────────────── */
1172
- .chex-topbar {
1173
- display: flex;
1174
- align-items: center;
1175
- gap: 16px;
1176
- padding: 0 28px;
1177
- height: 52px;
1178
- position: sticky;
1179
- top: 0;
1180
- z-index: 100;
1181
- background: rgba(11, 14, 20, 0.85);
1182
- backdrop-filter: blur(var(--blur-strong)) saturate(160%);
1183
- -webkit-backdrop-filter: blur(var(--blur-strong)) saturate(160%);
1184
- border-bottom: 1px solid var(--hairline);
1185
- }
1186
-
1187
- .chex-logo {
1188
- width: 22px; height: 22px; border-radius: 5px;
1189
- background: #E2E8F0;
1190
- color: #0B0E14; display: grid; place-items: center;
1191
- font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 10px;
1192
- letter-spacing: -0.05em;
1193
- box-shadow: 0 2px 10px rgba(0,0,0,0.5);
1194
- flex-shrink: 0;
1195
- }
1196
-
1197
- .chex-name { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; color: var(--fg); font-family: 'Inter', sans-serif; }
1198
- .chex-tag { font-size: 12px; color: var(--fg-muted); font-weight: 400; padding-left: 12px; border-left: 1px solid rgba(255,255,255,0.08); font-family: 'Inter', sans-serif; }
1199
-
1200
- .chex-pill {
1201
- display: inline-flex; align-items: center; gap: 7px;
1202
- padding: 4px 11px 4px 9px; border: 1px solid var(--border); border-radius: 999px;
1203
- font-size: 11.5px; color: var(--fg-muted); background: var(--bg-elev);
1204
- backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
1205
- font-family: 'JetBrains Mono', monospace; white-space: nowrap;
1206
- }
1207
-
1208
- .chex-dot {
1209
- width: 6px; height: 6px; border-radius: 50%; background: var(--green);
1210
- box-shadow: 0 0 0 3px rgba(16,185,129,0.22); display: inline-block; flex-shrink: 0;
1211
- }
1212
-
1213
- /* ── Warning banner ─────────────────────────────────────────────────────── */
1214
- .chex-banner {
1215
- display: flex; align-items: center; gap: 12px; padding: 11px 20px;
1216
- border-bottom: 1px solid var(--amber-border); background: var(--amber-bg);
1217
- backdrop-filter: blur(var(--blur)) saturate(160%); -webkit-backdrop-filter: blur(var(--blur)) saturate(160%);
1218
- color: var(--amber); font-size: 13px; font-family: 'Inter', sans-serif; font-weight: 500;
1219
- }
1220
- .chex-banner-icon { font-size: 14px; flex-shrink: 0; }
1221
- .chex-banner-body { color: var(--fg); font-weight: 400; line-height: 1.5; }
1222
- .chex-banner-body strong { color: var(--fg); font-weight: 600; }
1223
- .chex-banner code { font-family: 'JetBrains Mono', monospace; font-size: 12px; background: rgba(0,0,0,0.06); padding: 1px 5px; border-radius: 4px; }
1224
-
1225
- /* ── Tab nav ────────────────────────────────────────────────────────────── */
1226
- .tab-nav {
1227
- background: rgba(11,14,20,0.85) !important;
1228
- backdrop-filter: blur(var(--blur)) saturate(160%) !important;
1229
- -webkit-backdrop-filter: blur(var(--blur)) saturate(160%) !important;
1230
- border-bottom: 1px solid var(--hairline) !important;
1231
- border-top: none !important; padding: 0 20px !important; gap: 0 !important;
1232
- position: sticky !important; top: 52px !important; z-index: 99 !important; overflow: visible !important;
1233
- }
1234
-
1235
- .tab-nav button {
1236
- background: transparent !important; border: none !important; border-radius: 0 !important;
1237
- padding: 13px 16px !important; color: var(--fg-muted) !important;
1238
- font-size: 13px !important; font-weight: 500 !important; font-family: 'Inter', sans-serif !important;
1239
- letter-spacing: -0.003em !important; position: relative !important; white-space: nowrap !important;
1240
- transition: color 0.15s ease !important; cursor: pointer !important; box-shadow: none !important; outline: none !important;
1241
- }
1242
-
1243
- .tab-nav button:hover { color: var(--fg) !important; background: transparent !important; }
1244
-
1245
- .tab-nav button.selected, .tab-nav button[aria-selected="true"] {
1246
- color: var(--fg) !important; background: transparent !important; font-weight: 500 !important; box-shadow: none !important;
1247
- }
1248
-
1249
- .tab-nav button.selected::after, .tab-nav button[aria-selected="true"]::after {
1250
- content: ""; position: absolute; left: 12px; right: 12px; bottom: -1px;
1251
- height: 1.5px; background: var(--fg); border-radius: 2px 2px 0 0;
1252
- }
1253
-
1254
- .tabitem { border: none !important; background: transparent !important; padding: 20px 20px !important; }
1255
-
1256
- /* ── Cards (gr-group) ───────────────────────────────────────────────────── */
1257
- .gradio-container .gr-group {
1258
- background: var(--bg-elev) !important;
1259
- backdrop-filter: blur(var(--blur)) saturate(180%) !important;
1260
- -webkit-backdrop-filter: blur(var(--blur)) saturate(180%) !important;
1261
- border: 1px solid var(--border) !important;
1262
- border-radius: var(--radius-lg) !important;
1263
- box-shadow: var(--shadow-md) !important;
1264
- overflow: hidden !important; padding: 0 !important;
1265
- }
1266
-
1267
- .gradio-container .gr-group > *:not(.chex-card-header):not(.chex-load-bar) {
1268
- padding-left: 20px !important; padding-right: 20px !important;
1269
- }
1270
- .gradio-container .gr-group > *:last-child { padding-bottom: 18px !important; }
1271
-
1272
- /* ── Card header (new design) ───────────────────────────────────────────── */
1273
- .chex-card-header {
1274
- padding: 14px 20px; display: flex; align-items: center;
1275
- gap: 10px; border-bottom: 1px solid var(--hairline);
1276
- }
1277
-
1278
- .chex-card-icon {
1279
- opacity: 0.45; flex-shrink: 0; display: flex; align-items: center;
1280
- }
1281
-
1282
- .chex-card-title {
1283
- font-size: 13px; font-weight: 600; letter-spacing: -0.01em;
1284
- color: var(--fg); white-space: nowrap; font-family: 'Inter', sans-serif;
1285
- display: inline-flex; align-items: center; gap: 0;
1286
- }
1287
-
1288
- .chex-card-sep {
1289
- font-size: 13px; color: var(--fg-subtle); font-weight: 400;
1290
- padding: 0 6px; font-family: 'Inter', sans-serif;
1291
- }
1292
-
1293
- .chex-card-sub {
1294
- font-size: 12px; color: var(--fg-subtle); font-weight: 400;
1295
- font-family: 'Inter', sans-serif; letter-spacing: 0;
1296
- }
1297
-
1298
- /* ── Load bar (LOAD chip + sample buttons row) ─────────────────────────── */
1299
- .chex-load-bar {
1300
- display: flex; align-items: center; gap: 10px; padding: 10px 16px;
1301
- border-bottom: 1px solid var(--hairline); background: var(--bg-sunken); flex-wrap: wrap;
1302
- }
1303
-
1304
- .chex-load-chip {
1305
- font-family: 'JetBrains Mono', monospace; font-size: 10px;
1306
- text-transform: uppercase; letter-spacing: 0.1em;
1307
- color: var(--fg-subtle); background: var(--bg-elev-strong);
1308
- border: 1px solid var(--border); border-radius: 4px;
1309
- padding: 3px 7px; white-space: nowrap; flex-shrink: 0;
1310
- }
1311
-
1312
- /* Question label chip */
1313
- .chex-question-label {
1314
- font-family: 'JetBrains Mono', monospace; font-size: 10px;
1315
- text-transform: uppercase; letter-spacing: 0.1em;
1316
- color: var(--fg-subtle); padding: 14px 20px 6px; display: block;
1317
- }
1318
-
1319
- /* ── Suggested hint ─────────────────────────────────────────────────────── */
1320
- .chex-suggested {
1321
- display: flex; align-items: center; gap: 10px; padding: 10px 14px;
1322
- background: rgba(13,18,32,0.04); border: 1px solid var(--border); border-radius: var(--radius);
1323
- font-size: 12.5px; color: var(--fg-muted); font-family: 'Inter', sans-serif; line-height: 1.4; margin-top: 2px;
1324
- }
1325
- .chex-suggested-icon { font-size: 13px; flex-shrink: 0; opacity: 0.7; }
1326
-
1327
- /* ── Labels ─────────────────────────────────────────────────────────────── */
1328
- label > span:first-child, .label-wrap span,
1329
- .gradio-container label span.text-gray-500, span.svelte-1b6s6s {
1330
- font-family: 'JetBrains Mono', monospace !important; font-size: 10px !important;
1331
- font-weight: 500 !important; text-transform: uppercase !important; letter-spacing: 0.08em !important;
1332
- color: var(--fg-subtle) !important; margin-bottom: 6px !important; display: block !important;
1333
- }
1334
-
1335
- /* ── Text inputs ────────────────────────────────────────────────────────── */
1336
- textarea, input[type="text"], input[type="search"],
1337
- .gradio-container .gr-input, .gradio-container .gr-textarea,
1338
- .gradio-container [data-testid="textbox"] textarea,
1339
- .gradio-container [data-testid="textbox"] input {
1340
- background: var(--bg-input) !important; backdrop-filter: blur(10px) !important;
1341
- -webkit-backdrop-filter: blur(10px) !important; border: 1px solid var(--border) !important;
1342
- border-radius: var(--radius) !important; color: var(--fg) !important;
1343
- font-family: 'Inter', sans-serif !important; font-size: 13px !important;
1344
- line-height: 1.6 !important; padding: 11px 14px !important;
1345
- transition: border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease !important;
1346
- resize: vertical !important;
1347
- }
1348
-
1349
- textarea:focus, input[type="text"]:focus,
1350
- .gradio-container [data-testid="textbox"] textarea:focus,
1351
- .gradio-container [data-testid="textbox"] input:focus {
1352
- border-color: var(--border-strong) !important; background: var(--bg-elev) !important;
1353
- box-shadow: 0 0 0 2px rgba(255,255,255,0.05) !important; outline: none !important;
1354
- }
1355
-
1356
- textarea::placeholder, input::placeholder { color: var(--fg-subtle) !important; }
1357
-
1358
- textarea[readonly],
1359
- .gradio-container [data-testid="textbox"][data-interactive="false"] textarea {
1360
- background: var(--bg-sunken) !important; border: 1px solid var(--hairline) !important;
1361
- color: var(--fg) !important; cursor: default !important;
1362
- }
1363
-
1364
- /* ── Buttons ────────────────────────────────────────────────────────────── */
1365
- .gradio-container button {
1366
- font-family: 'Inter', sans-serif !important; font-size: 13px !important;
1367
- font-weight: 500 !important; border-radius: var(--radius) !important;
1368
- padding: 9px 16px !important;
1369
- transition: opacity 0.15s ease, background 0.15s ease, box-shadow 0.15s ease !important;
1370
- cursor: pointer !important; letter-spacing: -0.003em !important;
1371
- }
1372
-
1373
- .gradio-container button.primary, button.primary {
1374
- background: var(--fg) !important; color: var(--bg-base) !important; border: 1px solid var(--fg) !important;
1375
- box-shadow: 0 4px 14px rgba(0,0,0,0.35), 0 1px 0 rgba(255,255,255,0.08) inset !important;
1376
- }
1377
- .gradio-container button.primary:hover, button.primary:hover { opacity: 0.9 !important; box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important; }
1378
-
1379
- .gradio-container button.secondary, button.secondary {
1380
- background: transparent !important; color: var(--fg-muted) !important;
1381
- border: 1px solid var(--border-strong) !important; box-shadow: none !important;
1382
- }
1383
- .gradio-container button.secondary:hover, button.secondary:hover { background: var(--bg-elev) !important; color: var(--fg) !important; border-color: var(--border-strong) !important; }
1384
-
1385
- button.sm, .gradio-container button[size="sm"], button.small { font-size: 12px !important; padding: 6px 12px !important; }
1386
-
1387
- /* ── File upload ────────────────────────────────────────────────────────── */
1388
- .gradio-container .upload-container, .gradio-container [data-testid="file"] {
1389
- background: var(--bg-input) !important; border: 1px dashed var(--border-strong) !important; border-radius: var(--radius) !important;
1390
- }
1391
-
1392
- /* ── Dataframe / table ──────────────────────────────────────────────────── */
1393
- .gradio-container .wrap.svelte-a4gbbr, .gradio-container .table-wrap,
1394
- .gradio-container [data-testid="dataframe"] {
1395
- background: var(--bg-elev) !important;
1396
- backdrop-filter: blur(var(--blur)) saturate(180%) !important;
1397
- -webkit-backdrop-filter: blur(var(--blur)) saturate(180%) !important;
1398
- border: 1px solid var(--border) !important; border-radius: var(--radius-lg) !important;
1399
- box-shadow: var(--shadow-md) !important; overflow: hidden !important;
1400
  }
1401
 
1402
- .gradio-container table {
1403
- background: transparent !important; font-size: 13px !important;
1404
- font-family: 'Inter', sans-serif !important; border-collapse: separate !important;
1405
- border-spacing: 0 !important; width: 100% !important; border: none !important;
1406
- box-shadow: none !important; border-radius: 0 !important;
1407
- }
1408
-
1409
- .gradio-container th {
1410
- background: var(--bg-sunken) !important; border-bottom: 1px solid var(--hairline) !important;
1411
- border-top: none !important; padding: 14px 18px !important;
1412
- font-family: 'JetBrains Mono', monospace !important; font-size: 10px !important;
1413
- text-transform: uppercase !important; letter-spacing: 0.08em !important;
1414
- color: var(--fg-muted) !important; font-weight: 500 !important; text-align: left !important;
1415
- }
1416
-
1417
- .gradio-container td {
1418
- padding: 16px 18px !important; border-top: 1px solid var(--hairline) !important;
1419
- border-bottom: none !important; vertical-align: top !important; line-height: 1.6 !important;
1420
- color: var(--fg) !important; background: transparent !important;
1421
- }
1422
-
1423
- .gradio-container tr:first-child td { border-top: none !important; }
1424
-
1425
- /* ── Markdown prose ─────────────────────────────────────────────────────── */
1426
- .gradio-container .prose, .gradio-container .md, .gradio-container [data-testid="markdown"] {
1427
- color: var(--fg) !important; font-family: 'Inter', sans-serif !important;
1428
- font-size: 13px !important; line-height: 1.65 !important;
1429
- }
1430
-
1431
- .gradio-container .prose h2, .gradio-container .md h2 {
1432
- font-size: 18px !important; font-weight: 600 !important; letter-spacing: -0.02em !important;
1433
- color: var(--fg) !important; margin-bottom: 10px !important; margin-top: 0 !important;
1434
- }
1435
-
1436
- .gradio-container .prose p, .gradio-container .md p {
1437
- color: var(--fg-muted) !important; font-size: 13px !important; line-height: 1.65 !important; margin-bottom: 8px !important;
1438
- }
1439
-
1440
- .gradio-container .prose strong, .gradio-container .md strong { color: var(--fg) !important; font-weight: 600 !important; }
1441
-
1442
- .gradio-container .prose code, .gradio-container .md code {
1443
- font-family: 'JetBrains Mono', monospace !important; font-size: 12px !important;
1444
- background: rgba(13,18,32,0.06) !important; padding: 1px 5px !important;
1445
- border-radius: 4px !important; color: var(--fg) !important;
1446
- }
1447
-
1448
- /* ── Benchmark intro card ───────────────────────────────────────────────── */
1449
- .chex-bench-wrap {
1450
- background: var(--bg-elev);
1451
- backdrop-filter: blur(var(--blur)) saturate(180%);
1452
- -webkit-backdrop-filter: blur(var(--blur)) saturate(180%);
1453
- border: 1px solid var(--border); border-radius: var(--radius-lg);
1454
- box-shadow: var(--shadow-md); margin-bottom: 20px; overflow: hidden;
1455
- }
1456
-
1457
- .chex-bench-top {
1458
- padding: 24px 28px 20px; display: flex; align-items: flex-start; gap: 28px;
1459
- }
1460
-
1461
- .chex-bench-copy { flex: 1; min-width: 0; }
1462
- .chex-bench-copy h2 { margin: 0 0 8px; font-size: 18px; font-weight: 600; letter-spacing: -0.02em; color: var(--fg); font-family: 'Inter', sans-serif; }
1463
- .chex-bench-copy p { margin: 0; color: var(--fg-muted); font-size: 13px; line-height: 1.65; font-family: 'Inter', sans-serif; }
1464
-
1465
- .chex-bench-stats {
1466
- display: flex; gap: 0; flex-shrink: 0;
1467
- border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden;
1468
- }
1469
- .chex-bench-stat {
1470
- padding: 14px 20px; display: flex; flex-direction: column; gap: 3px;
1471
- border-right: 1px solid var(--border);
1472
- }
1473
- .chex-bench-stat:last-child { border-right: none; }
1474
- .chex-bench-stat .v { font-family: 'Inter', sans-serif; font-size: 22px; font-weight: 600; letter-spacing: -0.03em; line-height: 1.1; }
1475
- .chex-bench-stat .v.red { color: var(--red); }
1476
- .chex-bench-stat .v.green { color: var(--green); }
1477
- .chex-bench-stat .v.white { color: var(--fg); }
1478
- .chex-bench-stat .k { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--fg-subtle); font-family: 'JetBrains Mono', monospace; }
1479
-
1480
- /* ── Benchmark HTML table ──────────────────────────���────────────────────── */
1481
- .chex-bench-table-wrap {
1482
- background: var(--bg-elev);
1483
- backdrop-filter: blur(var(--blur)) saturate(180%);
1484
- -webkit-backdrop-filter: blur(var(--blur)) saturate(180%);
1485
- border: 1px solid var(--border); border-radius: var(--radius-lg);
1486
- box-shadow: var(--shadow-md); overflow: hidden;
1487
- }
1488
-
1489
- .chex-bench-table {
1490
- width: 100%; border-collapse: separate; border-spacing: 0;
1491
- font-family: 'Inter', sans-serif; font-size: 13px;
1492
- }
1493
-
1494
- .chex-bench-table th {
1495
- background: var(--bg-sunken) !important; border-bottom: 1px solid var(--hairline) !important;
1496
- padding: 13px 18px !important; font-family: 'JetBrains Mono', monospace !important;
1497
- font-size: 10px !important; text-transform: uppercase !important; letter-spacing: 0.08em !important;
1498
- color: var(--fg-muted) !important; font-weight: 500 !important; text-align: left !important;
1499
- white-space: nowrap;
1500
- }
1501
-
1502
- .chex-bench-table td {
1503
- padding: 16px 18px; border-top: 1px solid var(--hairline);
1504
- vertical-align: top; line-height: 1.55; color: var(--fg);
1505
- }
1506
-
1507
- .chex-bench-table tr:first-child td { border-top: none; }
1508
-
1509
- .chex-bench-table .col-question { color: var(--fg); font-weight: 400; }
1510
- .chex-bench-table .col-truth { color: var(--fg-muted); font-style: italic; }
1511
- .chex-bench-table .col-base { color: #F87171; }
1512
- .chex-bench-table .col-halluc { text-align: center; }
1513
-
1514
- /* Inline label badges for the benchmark table */
1515
- .cbadge {
1516
- display: inline-flex; align-items: center; gap: 6px;
1517
- padding: 5px 10px; border-radius: 8px; font-size: 11px;
1518
- font-weight: 600; letter-spacing: 0.02em; font-family: 'Inter', sans-serif;
1519
- white-space: nowrap;
1520
- }
1521
- .cbadge-green { background: rgba(16,185,129,0.12); color: #10B981; border: 1px solid rgba(16,185,129,0.28); }
1522
- .cbadge-red { background: rgba(244,63,94,0.10); color: #F43F5E; border: 1px solid rgba(244,63,94,0.28); }
1523
- .cbadge-amber { background: rgba(245,158,11,0.12); color: #F59E0B; border: 1px solid rgba(245,158,11,0.30); }
1524
- .cbadge-icon { font-size: 12px; }
1525
-
1526
- .chex-halluc-yes { color: var(--red); font-size: 15px; }
1527
- .chex-halluc-no { color: var(--green); font-size: 15px; }
1528
-
1529
- /* ── Footer ─────────────────────────────────────────────────────────────── */
1530
- .chex-footer {
1531
- border-top: 1px solid var(--hairline); padding: 13px 28px;
1532
- display: flex; align-items: center; gap: 18px; color: var(--fg-subtle);
1533
- font-size: 11px; font-family: 'JetBrains Mono', monospace;
1534
- background: var(--bg-elev); backdrop-filter: blur(var(--blur));
1535
- -webkit-backdrop-filter: blur(var(--blur)); margin-top: 24px;
1536
- justify-content: space-between;
1537
- }
1538
- .chex-footer-left { display: flex; align-items: center; gap: 18px; }
1539
- .chex-footer .sep { opacity: 0.35; }
1540
- .chex-footer-right { font-size: 11px; color: var(--fg-subtle); opacity: 0.6; }
1541
-
1542
- /* ── Misc helpers ───────────────────────────────────────────────────────── */
1543
- .chex-label-wrap { padding: 4px 0 8px; }
1544
- .chex-divider { height: 1px; background: var(--hairline); margin: 18px 0; }
1545
- .chex-section-kicker { font-family: 'JetBrains Mono', monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--fg-subtle); margin-bottom: 10px; display: block; }
1546
- .chex-card-body { padding: 18px 20px; display: flex; flex-direction: column; gap: 14px; }
1547
-
1548
- /* ── Empty state ────────────────────────────────────────────────────────── */
1549
- .chex-empty {
1550
- display: flex; flex-direction: column; align-items: center; justify-content: center;
1551
- gap: 10px; padding: 48px 24px; color: var(--fg-subtle); text-align: center;
1552
- }
1553
- .chex-empty-icon { font-size: 20px; opacity: 0.4; }
1554
- .chex-empty-title { font-size: 13.5px; font-weight: 500; color: var(--fg-muted); font-family: 'Inter', sans-serif; }
1555
- .chex-empty-body { font-size: 12.5px; color: var(--fg-subtle); line-height: 1.6; font-family: 'Inter', sans-serif; max-width: 280px; }
1556
-
1557
- *::-webkit-scrollbar { width: 8px; height: 8px; }
1558
- *::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
1559
- *::-webkit-scrollbar-track { background: transparent; }
1560
-
1561
- .gradio-container .gap-4 { gap: 14px !important; }
1562
- .gradio-container .gap-2 { gap: 8px !important; }
1563
-
1564
- .tabitem .tab-nav { position: static !important; top: auto !important; }
1565
-
1566
- @media (max-width: 900px) {
1567
- .chex-topbar { padding: 0 16px; }
1568
- .chex-tag { display: none; }
1569
- .tabitem { padding: 16px !important; }
1570
- .chex-bench-top { flex-direction: column; gap: 16px; }
1571
- .chex-bench-stats { flex-direction: row; }
1572
- .chex-footer { padding: 12px 16px; gap: 12px; flex-wrap: wrap; }
1573
  }
 
 
 
 
1574
  """
1575
 
1576
  # ---------------------------------------------------------------------------
1577
  # Static HTML
1578
  # ---------------------------------------------------------------------------
1579
 
1580
- TOPBAR_HTML = """
1581
- <div class="chex-topbar">
1582
- <div class="chex-logo">CX</div>
1583
- <span class="chex-name">CHEX</span>
1584
- <span class="chex-tag">grounded answers from documents</span>
1585
- <div style="flex:1"></div>
1586
- <div class="chex-pill"><span class="chex-dot"></span>MI300X &middot; ready</div>
1587
- </div>
1588
- """
1589
-
1590
- FOOTER_HTML = """
1591
- <div class="chex-footer">
1592
- <div class="chex-footer-left">
1593
- <span>chex/v0.4.1</span>
1594
- <span class="sep">&middot;</span>
1595
- <span>endpoint: mi300x-east-2</span>
1596
- <span class="sep">&middot;</span>
1597
- <span>tokens/s 142.7</span>
1598
- </div>
1599
- <div class="chex-footer-right">&#8617; to analyse &nbsp;&nbsp; &#8984;K shortcuts</div>
1600
- </div>
1601
- """
1602
-
1603
- BENCH_INTRO_HTML = """
1604
- <div class="chex-bench-wrap">
1605
- <div class="chex-bench-top">
1606
- <div class="chex-bench-copy">
1607
- <h2>Why grounding matters</h2>
1608
- <p>We ran the same five questions through a base instruction-tuned model and through CHEX. The base model invented or extrapolated answers in 4 of 5 cases &mdash; confident, plausible, wrong. CHEX returned a verifiable label, a verbatim citation, and refused to answer when the source was silent.</p>
1609
- </div>
1610
- <div class="chex-bench-stats">
1611
- <div class="chex-bench-stat"><div class="v red">4/5</div><div class="k">Base hallucinations</div></div>
1612
- <div class="chex-bench-stat"><div class="v green">5/5</div><div class="k">CHEX correct</div></div>
1613
- <div class="chex-bench-stat"><div class="v white">100%</div><div class="k">Cited verbatim</div></div>
1614
- </div>
1615
- </div>
1616
- </div>
1617
- """
1618
-
1619
- BENCH_TABLE_HTML = """
1620
- <div class="chex-bench-table-wrap">
1621
- <table class="chex-bench-table">
1622
- <thead>
1623
- <tr>
1624
- <th>Question</th>
1625
- <th>Ground truth</th>
1626
- <th>Base model</th>
1627
- <th>CHEX</th>
1628
- <th>Halluc.</th>
1629
- </tr>
1630
- </thead>
1631
- <tbody>
1632
- <tr>
1633
- <td class="col-question">What is the annual license fee in the Helix MSA?</td>
1634
- <td class="col-truth">$144,000</td>
1635
- <td class="col-base">$120,000 per year (industry-standard rate).</td>
1636
- <td>
1637
- <span class="cbadge cbadge-green"><span class="cbadge-icon">&#10003;</span> GROUNDED</span>
1638
- <div style="margin-top:8px;font-size:12.5px;color:var(--fg-muted)">$144,000 per year.</div>
1639
- </td>
1640
- <td class="col-halluc"><span class="chex-halluc-yes">!</span></td>
1641
- </tr>
1642
- <tr>
1643
- <td class="col-question">What is the SLA uptime in the Northwind MSA?</td>
1644
- <td class="col-truth">99.9% per calendar month</td>
1645
- <td class="col-base">99.95% with four nines on premium tier.</td>
1646
- <td>
1647
- <span class="cbadge cbadge-green"><span class="cbadge-icon">&#10003;</span> GROUNDED</span>
1648
- <div style="margin-top:8px;font-size:12.5px;color:var(--fg-muted)">99.9% per calendar month, excluding maintenance.</div>
1649
- </td>
1650
- <td class="col-halluc"><span class="chex-halluc-yes">!</span></td>
1651
- </tr>
1652
- <tr>
1653
- <td class="col-question">Does the Aperture NDA include an indemnification clause?</td>
1654
- <td class="col-truth" style="font-style:italic">Not present in the document.</td>
1655
- <td class="col-base">Yes &mdash; Section 7 of the NDA contains standard mutual indemnification.</td>
1656
- <td>
1657
- <span class="cbadge cbadge-red"><span class="cbadge-icon">&#10007;</span> ABSENT</span>
1658
- <div style="margin-top:8px;font-size:12.5px;color:var(--fg-muted)">No indemnification clause is present.</div>
1659
- </td>
1660
- <td class="col-halluc"><span class="chex-halluc-yes">!</span></td>
1661
- </tr>
1662
- <tr>
1663
- <td class="col-question">Within how many days must security incidents be reported?</td>
1664
- <td class="col-truth">72 hours</td>
1665
- <td class="col-base">72 hours.</td>
1666
- <td>
1667
- <span class="cbadge cbadge-green"><span class="cbadge-icon">&#10003;</span> GROUNDED</span>
1668
- <div style="margin-top:8px;font-size:12.5px;color:var(--fg-muted)">Within 72 hours of becoming aware.</div>
1669
- </td>
1670
- <td class="col-halluc"><span class="chex-halluc-no">&#10003;</span></td>
1671
- </tr>
1672
- <tr>
1673
- <td class="col-question">Is the NDA perpetual?</td>
1674
- <td class="col-truth" style="font-style:italic">No &mdash; 2 year term.</td>
1675
- <td class="col-base">The NDA appears to be perpetual based on standard NDA practice.</td>
1676
- <td>
1677
- <span class="cbadge cbadge-amber"><span class="cbadge-icon">&#9888;</span> CONTRADICTS PRIOR</span>
1678
- <div style="margin-top:8px;font-size:12.5px;color:var(--fg-muted)">No &mdash; 2 year term, contradicts the premise.</div>
1679
- </td>
1680
- <td class="col-halluc"><span class="chex-halluc-yes">!</span></td>
1681
- </tr>
1682
- </tbody>
1683
- </table>
1684
- </div>
1685
- """
1686
-
1687
- CONTRACT_SOURCE_HEADER_HTML = """
1688
- <div class="chex-card-header">
1689
- <div class="chex-card-icon">
1690
- <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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>
1691
- </div>
1692
- <span class="chex-card-title">Contract source</span>
1693
- <span class="chex-card-sep">/</span>
1694
- <span class="chex-card-sub">paste or load sample</span>
1695
- </div>
1696
- """
1697
-
1698
- CONTRACT_RESULTS_HEADER_HTML = """
1699
- <div class="chex-card-header">
1700
- <div class="chex-card-icon">
1701
- <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
1702
- </div>
1703
- <span class="chex-card-title">Classification</span>
1704
- <span class="chex-card-sep">/</span>
1705
- <span class="chex-card-sub">ask a question of the source</span>
1706
- </div>
1707
- """
1708
-
1709
- LOAD_BAR_HTML = """
1710
- <div class="chex-load-bar">
1711
- <span class="chex-load-chip">LOAD</span>
1712
- </div>
1713
- """
1714
-
1715
- STATEMENT_SOURCE_HEADER_HTML = """
1716
- <div class="chex-card-header">
1717
- <div class="chex-card-icon">
1718
- <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/></svg>
1719
- </div>
1720
- <span class="chex-card-title">Statement source</span>
1721
- <span class="chex-card-sep">/</span>
1722
- <span class="chex-card-sub">paste &middot; upload pdf &middot; upload csv</span>
1723
- </div>
1724
- """
1725
-
1726
- STATEMENT_RESULTS_HEADER_HTML = """
1727
- <div class="chex-card-header">
1728
- <div class="chex-card-icon">
1729
- <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
1730
- </div>
1731
- <span class="chex-card-title">Statement analysis</span>
1732
- <span class="chex-card-sep">/</span>
1733
- <span class="chex-card-sub">summary + Q&amp;A</span>
1734
- </div>
1735
- """
1736
-
1737
- CONTRACT_EMPTY_HTML = """
1738
- <div class="chex-empty">
1739
- <div class="chex-empty-icon">&#9889;</div>
1740
- <div class="chex-empty-title">No analysis yet</div>
1741
- <div class="chex-empty-body">Load or paste a contract, type a question, then press Analyze. CHEX will return a label, an exact citation, and its reasoning.</div>
1742
- </div>
1743
- """
1744
-
1745
- STATEMENT_EMPTY_HTML = """
1746
- <div class="chex-empty">
1747
- <div class="chex-empty-icon">&#9141;</div>
1748
- <div class="chex-empty-title">Awaiting statement</div>
1749
- <div class="chex-empty-body">Paste, upload, or load the sample. CHEX will summarise totals, surface recurring payments, and flag anomalies you can drill into.</div>
1750
- </div>
1751
- """
1752
 
1753
  # ---------------------------------------------------------------------------
1754
  # Gradio UI
@@ -1756,146 +1077,146 @@ STATEMENT_EMPTY_HTML = """
1756
 
1757
  with gr.Blocks(title="CHEX β€” Document Intelligence") as demo:
1758
 
1759
- gr.HTML(TOPBAR_HTML)
1760
-
1761
- if WARNING_HTML:
1762
- gr.HTML(WARNING_HTML)
1763
 
1764
  with gr.Tabs():
1765
 
1766
  # ── Tab 01: Contract Analysis ──────────────────────────────────── #
1767
- with gr.Tab("01 Contract analysis"):
1768
  with gr.Row(equal_height=False):
1769
 
1770
  with gr.Column(scale=9):
1771
- with gr.Group():
1772
- gr.HTML(CONTRACT_SOURCE_HEADER_HTML)
1773
- contract_input = gr.Textbox(
1774
- label="Contract text",
1775
- lines=20,
1776
- placeholder="Paste contract text here, or load a sample above…",
1777
- show_label=False,
1778
- )
1779
- gr.HTML(LOAD_BAR_HTML)
1780
- with gr.Row():
1781
- btn_software = gr.Button("Software License", variant="secondary", size="sm")
1782
- btn_nda = gr.Button("NDA", variant="secondary", size="sm")
1783
- btn_service = gr.Button("Service Agreement", variant="secondary", size="sm")
1784
- suggested_q = gr.HTML(value="", visible=False)
1785
 
1786
  with gr.Column(scale=11):
1787
- with gr.Group():
1788
- gr.HTML(CONTRACT_RESULTS_HEADER_HTML)
1789
- gr.HTML('<span class="chex-question-label">Question</span>')
1790
- with gr.Row():
1791
- question_input = gr.Textbox(
1792
- label="Question",
1793
- placeholder="e.g., What is the annual license fee?",
1794
- lines=1,
1795
- show_label=False,
1796
- scale=8,
1797
- )
1798
- analyze_btn = gr.Button("Analyze ↡", variant="primary", scale=2)
1799
- label_display = gr.HTML(value=CONTRACT_EMPTY_HTML)
1800
- answer_output = gr.Textbox(label="Answer", interactive=False, lines=3, visible=False)
1801
- citation_output = gr.Textbox(label="Citation", interactive=False, lines=2, visible=False)
1802
- reasoning_output = gr.Textbox(label="Reasoning", interactive=False, lines=3, visible=False)
1803
 
1804
  # ── Tab 02: Bank Statements ────────────────────────────────────── #
1805
- with gr.Tab("02 Bank statements"):
1806
  with gr.Row(equal_height=False):
1807
 
1808
  with gr.Column(scale=9):
1809
- with gr.Group():
1810
- gr.HTML(STATEMENT_SOURCE_HEADER_HTML)
1811
- with gr.Tabs():
1812
- with gr.Tab("Paste text"):
1813
- bank_paste_input = gr.Textbox(
1814
- label="Bank statement text (supports multiple)",
1815
- lines=20,
1816
- placeholder=(
1817
- "Paste your statement here, e.g. lines like: 2025-03-15 Direct deposit +5,420.00…"
1818
- ),
1819
- show_label=False,
1820
- )
1821
- btn_load_statement = gr.Button("Load sample statement", variant="secondary", size="sm")
1822
- with gr.Tab("Upload PDF"):
1823
- bank_pdf_input = gr.File(
1824
- label="PDF bank statement (multiple allowed)",
1825
- file_types=[".pdf"],
1826
- file_count="multiple",
1827
- )
1828
- bank_pdf_password_input = gr.Textbox(
1829
- label="PDF password (optional)",
1830
- type="password",
1831
- placeholder="Leave blank if PDF is not encrypted",
1832
- show_label=False,
1833
- )
1834
- with gr.Tab("Upload CSV"):
1835
- bank_csv_input = gr.File(
1836
- label="CSV bank statement (multiple allowed)",
1837
- file_types=[".csv"],
1838
- file_count="multiple",
1839
- )
1840
- with gr.Tab("Upload TXT"):
1841
- bank_txt_input = gr.File(
1842
- label="TXT bank statement (multiple allowed)",
1843
- file_types=[".txt", ".text"],
1844
- file_count="multiple",
1845
- )
1846
- with gr.Tab("Upload Excel"):
1847
- bank_xlsx_input = gr.File(
1848
- label="Excel bank statement (.xlsx, multiple allowed)",
1849
- file_types=[".xlsx"],
1850
- file_count="multiple",
1851
- )
1852
- with gr.Tab("Upload OFX / QFX"):
1853
- bank_ofx_input = gr.File(
1854
- label="OFX / QFX bank statement (multiple allowed)",
1855
- file_types=[".ofx", ".qfx"],
1856
- file_count="multiple",
1857
- )
1858
-
1859
- with gr.Column(scale=11):
1860
- with gr.Group():
1861
- gr.HTML(STATEMENT_RESULTS_HEADER_HTML)
1862
- analyse_stmt_btn = gr.Button("Analyse statement", variant="primary")
1863
- summary_output = gr.HTML(value=STATEMENT_EMPTY_HTML)
1864
- summary_md_output = gr.Markdown(value="", visible=False)
1865
- with gr.Row():
1866
- export_csv_btn = gr.Button("Export CSV", variant="secondary", size="sm")
1867
- export_pdf_btn = gr.Button("Export PDF", variant="secondary", size="sm")
1868
- export_status = gr.Markdown(value="")
1869
- export_file = gr.File(label="Download", interactive=False)
1870
- gr.HTML('<div class="chex-divider"></div>')
1871
- gr.HTML('<span class="chex-section-kicker">Ask a question</span>')
1872
- with gr.Row():
1873
- bank_question_input = gr.Textbox(
1874
- label="Question",
1875
- placeholder="e.g., What was the largest debit this month?",
1876
- lines=1,
1877
  show_label=False,
1878
- scale=8,
1879
  )
1880
- bank_ask_btn = gr.Button("Ask ↡", variant="secondary", scale=2)
1881
- bank_label_display = gr.HTML(value=format_label_html("N/A"))
1882
- bank_answer_output = gr.Textbox(label="Answer", interactive=False, lines=3)
1883
- bank_citation_output = gr.Textbox(label="Citation", interactive=False, lines=2)
1884
- bank_reasoning_output = gr.Textbox(label="Reasoning", interactive=False, lines=3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1885
 
1886
  bank_statement_state = gr.State("")
1887
  bank_summary_state = gr.State("")
1888
- # Hidden JSON output for `gradio_client` API usage.
1889
  bank_api_output = gr.JSON(visible=False)
1890
  bank_api_question = gr.Textbox(visible=False)
1891
  bank_api_btn = gr.Button(visible=False)
1892
 
1893
  # ── Tab 03: Benchmark ──────────────────────────────────────────── #
1894
- with gr.Tab("03 Benchmark"):
1895
- gr.HTML(BENCH_INTRO_HTML)
1896
- gr.HTML(BENCH_TABLE_HTML)
1897
-
1898
- gr.HTML(FOOTER_HTML)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1899
 
1900
  # ── Event handlers ─────────────────────────────────────────────────── #
1901
 
@@ -1916,13 +1237,7 @@ with gr.Blocks(title="CHEX β€” Document Intelligence") as demo:
1916
  btn_service.click(fn=load_service, inputs=[], outputs=[contract_input, question_input, suggested_q])
1917
 
1918
  def analyze_contract_ui(contract_text: str, question: str):
1919
- label_html, answer, citation, reasoning = analyze_contract(contract_text, question)
1920
- return (
1921
- label_html,
1922
- gr.update(value=answer, visible=True),
1923
- gr.update(value=citation, visible=True),
1924
- gr.update(value=reasoning, visible=True),
1925
- )
1926
 
1927
  analyze_btn.click(
1928
  fn=analyze_contract_ui,
@@ -1939,19 +1254,8 @@ with gr.Blocks(title="CHEX β€” Document Intelligence") as demo:
1939
 
1940
  btn_load_statement.click(fn=lambda: SAMPLE_STATEMENT, inputs=[], outputs=[bank_paste_input])
1941
 
1942
- def analyse_bank_ui(paste_text, pdf_file, pdf_password, csv_file, txt_file, xlsx_file, ofx_file):
1943
- summary_md, combined_text, summary_json = analyse_bank_statement(
1944
- paste_text, pdf_file, pdf_password, csv_file, txt_file, xlsx_file, ofx_file
1945
- )
1946
- return (
1947
- gr.update(value="", visible=False),
1948
- gr.update(value=summary_md, visible=True),
1949
- combined_text,
1950
- summary_json,
1951
- )
1952
-
1953
  analyse_stmt_btn.click(
1954
- fn=analyse_bank_ui,
1955
  inputs=[
1956
  bank_paste_input,
1957
  bank_pdf_input,
@@ -1961,7 +1265,7 @@ with gr.Blocks(title="CHEX β€” Document Intelligence") as demo:
1961
  bank_xlsx_input,
1962
  bank_ofx_input,
1963
  ],
1964
- outputs=[summary_output, summary_md_output, bank_statement_state, bank_summary_state],
1965
  )
1966
 
1967
  export_csv_btn.click(
 
1042
  # ---------------------------------------------------------------------------
1043
 
1044
  CHEX_CSS = """
 
 
1045
  *, *::before, *::after { box-sizing: border-box; }
1046
 
1047
  :root {
1048
+ --green: #16a34a;
1049
+ --red: #dc2626;
1050
+ --amber: #d97706;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1051
  }
1052
 
1053
+ /* label badges */
1054
+ .badge {
1055
+ display: inline-block; padding: 3px 10px; border-radius: 4px;
1056
+ font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1057
  }
1058
+ .badge-green { background: #dcfce7; color: var(--green); }
1059
+ .badge-red { background: #fee2e2; color: var(--red); }
1060
+ .badge-amber { background: #fef3c7; color: var(--amber); }
1061
+ .badge-gray { background: #f1f5f9; color: #64748b; }
1062
  """
1063
 
1064
  # ---------------------------------------------------------------------------
1065
  # Static HTML
1066
  # ---------------------------------------------------------------------------
1067
 
1068
+ WARNING_BANNER_HTML = (
1069
+ '<div style="background:#fef3c7;border:1px solid #fde68a;border-radius:6px;'
1070
+ 'padding:10px 14px;margin-bottom:12px;color:#92400e;font-size:13px;">'
1071
+ '<strong>⚠ Model not loaded</strong> &mdash; {msg}</div>'
1072
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
 
1074
  # ---------------------------------------------------------------------------
1075
  # Gradio UI
 
1077
 
1078
  with gr.Blocks(title="CHEX β€” Document Intelligence") as demo:
1079
 
1080
+ if model_load_error:
1081
+ gr.HTML(WARNING_BANNER_HTML.format(msg=model_load_error))
 
 
1082
 
1083
  with gr.Tabs():
1084
 
1085
  # ── Tab 01: Contract Analysis ──────────────────────────────────── #
1086
+ with gr.Tab("Contract analysis"):
1087
  with gr.Row(equal_height=False):
1088
 
1089
  with gr.Column(scale=9):
1090
+ gr.Markdown("### Contract source\nPaste text or load a sample.")
1091
+ contract_input = gr.Textbox(
1092
+ label="Contract text",
1093
+ lines=20,
1094
+ placeholder="Paste contract text here…",
1095
+ show_label=False,
1096
+ )
1097
+ gr.Markdown("**Load sample:**")
1098
+ with gr.Row():
1099
+ btn_software = gr.Button("Software License", variant="secondary", size="sm")
1100
+ btn_nda = gr.Button("NDA", variant="secondary", size="sm")
1101
+ btn_service = gr.Button("Service Agreement", variant="secondary", size="sm")
1102
+ suggested_q = gr.Markdown(value="", visible=False)
 
1103
 
1104
  with gr.Column(scale=11):
1105
+ gr.Markdown("### Classification\nAsk a yes/no or factual question about the contract.")
1106
+ with gr.Row():
1107
+ question_input = gr.Textbox(
1108
+ label="Question",
1109
+ placeholder="e.g., What is the annual license fee?",
1110
+ lines=1,
1111
+ scale=8,
1112
+ )
1113
+ analyze_btn = gr.Button("Analyze", variant="primary", scale=2)
1114
+ label_display = gr.HTML(value=format_label_html("N/A"))
1115
+ answer_output = gr.Textbox(label="Answer", interactive=False, lines=3)
1116
+ citation_output = gr.Textbox(label="Citation", interactive=False, lines=2)
1117
+ reasoning_output = gr.Textbox(label="Reasoning", interactive=False, lines=3)
 
 
 
1118
 
1119
  # ── Tab 02: Bank Statements ────────────────────────────────────── #
1120
+ with gr.Tab("Bank statements"):
1121
  with gr.Row(equal_height=False):
1122
 
1123
  with gr.Column(scale=9):
1124
+ gr.Markdown("### Statement source\nPaste, upload, or load the sample.")
1125
+ with gr.Tabs():
1126
+ with gr.Tab("Paste text"):
1127
+ bank_paste_input = gr.Textbox(
1128
+ label="Statement text",
1129
+ lines=20,
1130
+ placeholder="Paste statement here, e.g. lines like: 2025-03-15 Direct deposit +5,420.00…\n\nSeparate multiple statements with a line containing only ---",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1131
  show_label=False,
 
1132
  )
1133
+ btn_load_statement = gr.Button("Load sample", variant="secondary", size="sm")
1134
+ with gr.Tab("Upload PDF"):
1135
+ bank_pdf_input = gr.File(
1136
+ label="PDF (multiple allowed)",
1137
+ file_types=[".pdf"],
1138
+ file_count="multiple",
1139
+ )
1140
+ bank_pdf_password_input = gr.Textbox(
1141
+ label="PDF password (leave blank if unencrypted)",
1142
+ type="password",
1143
+ )
1144
+ with gr.Tab("Upload CSV"):
1145
+ bank_csv_input = gr.File(
1146
+ label="CSV (multiple allowed)",
1147
+ file_types=[".csv"],
1148
+ file_count="multiple",
1149
+ )
1150
+ with gr.Tab("Upload TXT"):
1151
+ bank_txt_input = gr.File(
1152
+ label="TXT (multiple allowed)",
1153
+ file_types=[".txt", ".text"],
1154
+ file_count="multiple",
1155
+ )
1156
+ with gr.Tab("Upload Excel"):
1157
+ bank_xlsx_input = gr.File(
1158
+ label="XLSX (multiple allowed)",
1159
+ file_types=[".xlsx"],
1160
+ file_count="multiple",
1161
+ )
1162
+ with gr.Tab("Upload OFX/QFX"):
1163
+ bank_ofx_input = gr.File(
1164
+ label="OFX/QFX (multiple allowed)",
1165
+ file_types=[".ofx", ".qfx"],
1166
+ file_count="multiple",
1167
+ )
1168
+
1169
+ with gr.Column(scale=11):
1170
+ gr.Markdown("### Statement analysis")
1171
+ analyse_stmt_btn = gr.Button("Analyse statement", variant="primary")
1172
+ summary_md_output = gr.Markdown(value="*Run Analyse statement to see results.*")
1173
+ with gr.Row():
1174
+ export_csv_btn = gr.Button("Export CSV", variant="secondary", size="sm")
1175
+ export_pdf_btn = gr.Button("Export PDF", variant="secondary", size="sm")
1176
+ export_status = gr.Markdown(value="")
1177
+ export_file = gr.File(label="Download", interactive=False)
1178
+ gr.Markdown("---\n**Ask a question about the statement:**")
1179
+ with gr.Row():
1180
+ bank_question_input = gr.Textbox(
1181
+ label="Question",
1182
+ placeholder="e.g., What was the largest debit this month?",
1183
+ lines=1,
1184
+ scale=8,
1185
+ )
1186
+ bank_ask_btn = gr.Button("Ask", variant="secondary", scale=2)
1187
+ bank_label_display = gr.HTML(value=format_label_html("N/A"))
1188
+ bank_answer_output = gr.Textbox(label="Answer", interactive=False, lines=3)
1189
+ bank_citation_output = gr.Textbox(label="Citation", interactive=False, lines=2)
1190
+ bank_reasoning_output = gr.Textbox(label="Reasoning", interactive=False, lines=3)
1191
 
1192
  bank_statement_state = gr.State("")
1193
  bank_summary_state = gr.State("")
 
1194
  bank_api_output = gr.JSON(visible=False)
1195
  bank_api_question = gr.Textbox(visible=False)
1196
  bank_api_btn = gr.Button(visible=False)
1197
 
1198
  # ── Tab 03: Benchmark ──────────────────────────────────────────── #
1199
+ with gr.Tab("Benchmark"):
1200
+ gr.Markdown("""
1201
+ ### Why grounding matters
1202
+
1203
+ We ran the same five questions through a base instruction-tuned model and through CHEX.
1204
+ The base model invented or extrapolated answers in **4 of 5 cases** β€” confident, plausible, wrong.
1205
+ CHEX returned a verifiable label, a verbatim citation, and refused to answer when the source was silent.
1206
+
1207
+ | Metric | Result |
1208
+ |---|---|
1209
+ | Base hallucinations | **4 / 5** |
1210
+ | CHEX correct | **5 / 5** |
1211
+ | Cited verbatim | **100%** |
1212
+ """)
1213
+ gr.Dataframe(
1214
+ value=BENCHMARK_DF,
1215
+ headers=list(BENCHMARK_DF.columns),
1216
+ datatype=["str"] * len(BENCHMARK_DF.columns),
1217
+ wrap=True,
1218
+ interactive=False,
1219
+ )
1220
 
1221
  # ── Event handlers ─────────────────────────────────────────────────── #
1222
 
 
1237
  btn_service.click(fn=load_service, inputs=[], outputs=[contract_input, question_input, suggested_q])
1238
 
1239
  def analyze_contract_ui(contract_text: str, question: str):
1240
+ return analyze_contract(contract_text, question)
 
 
 
 
 
 
1241
 
1242
  analyze_btn.click(
1243
  fn=analyze_contract_ui,
 
1254
 
1255
  btn_load_statement.click(fn=lambda: SAMPLE_STATEMENT, inputs=[], outputs=[bank_paste_input])
1256
 
 
 
 
 
 
 
 
 
 
 
 
1257
  analyse_stmt_btn.click(
1258
+ fn=analyse_bank_statement,
1259
  inputs=[
1260
  bank_paste_input,
1261
  bank_pdf_input,
 
1265
  bank_xlsx_input,
1266
  bank_ofx_input,
1267
  ],
1268
+ outputs=[summary_md_output, bank_statement_state, bank_summary_state],
1269
  )
1270
 
1271
  export_csv_btn.click(