ElMETRICO commited on
Commit
a158874
·
verified ·
1 Parent(s): 5004307

Update CrossTalk AI with premium UI

Browse files
Files changed (2) hide show
  1. README.md +3 -1
  2. app.py +246 -63
README.md CHANGED
@@ -14,7 +14,7 @@ models:
14
 
15
  This Space runs the full CrossTalk AI trained hybrid retrieval system.
16
 
17
- It downloads the full trained artifacts from:
18
 
19
  `ElMETRICO/crosstalk-ai-full-artifacts`
20
 
@@ -25,3 +25,5 @@ It downloads the full trained artifacts from:
25
  - fine-tuned multilingual E5 semantic fallback
26
  - FAISS vector search
27
  - confidence-aware safe output handling
 
 
 
14
 
15
  This Space runs the full CrossTalk AI trained hybrid retrieval system.
16
 
17
+ It downloads trained artifacts from:
18
 
19
  `ElMETRICO/crosstalk-ai-full-artifacts`
20
 
 
25
  - fine-tuned multilingual E5 semantic fallback
26
  - FAISS vector search
27
  - confidence-aware safe output handling
28
+
29
+ Semantic fallback outputs are candidate suggestions only, not verified translations.
app.py CHANGED
@@ -10,21 +10,13 @@ from sentence_transformers import SentenceTransformer
10
 
11
  MODEL_REPO_ID = "ElMETRICO/crosstalk-ai-full-artifacts"
12
 
13
- print("Downloading CrossTalk AI full artifacts from:", MODEL_REPO_ID)
14
-
15
- artifact_dir = Path(snapshot_download(
16
- repo_id=MODEL_REPO_ID,
17
- repo_type="model"
18
- ))
19
 
20
  MODEL_DIR = artifact_dir / "model" / "e5_lexical_contrastive_finetuned"
21
  INDEX_PATH = artifact_dir / "artifacts" / "trained_e5_source_to_meaning.index"
22
  DF_PATH = artifact_dir / "artifacts" / "trained_e5_source_to_meaning_df.csv"
23
 
24
- print("Model path:", MODEL_DIR)
25
- print("Index path:", INDEX_PATH)
26
- print("Data path:", DF_PATH)
27
-
28
  print("Loading fine-tuned E5 model...")
29
  model = SentenceTransformer(str(MODEL_DIR))
30
  model.max_seq_length = 128
@@ -32,7 +24,7 @@ model.max_seq_length = 128
32
  print("Loading FAISS index...")
33
  source_index = faiss.read_index(str(INDEX_PATH))
34
 
35
- print("Loading source dataframe...")
36
  source_df = pd.read_csv(DF_PATH, encoding="utf-8-sig")
37
  print("Rows loaded:", len(source_df))
38
 
@@ -45,9 +37,7 @@ def clean_text(x):
45
 
46
 
47
  def normalize_lookup_text(x):
48
- x = clean_text(x).lower()
49
- x = re.sub(r"\s+", " ", x).strip()
50
- return x
51
 
52
 
53
  def remove_parentheses_text(x):
@@ -59,9 +49,7 @@ def remove_parentheses_text(x):
59
 
60
  def count_source_files(x):
61
  x = "" if pd.isna(x) else str(x)
62
- if not x.strip():
63
- return 0
64
- return len([p for p in x.split("||") if p.strip()])
65
 
66
 
67
  def add_quality_score(df):
@@ -92,7 +80,7 @@ def add_quality_score(df):
92
 
93
  source_df = add_quality_score(source_df)
94
 
95
- for col in ["language", "source_text", "english_meaning", "bangla_meaning"]:
96
  if col not in source_df.columns:
97
  source_df[col] = ""
98
  source_df[col] = source_df[col].apply(clean_text)
@@ -103,7 +91,6 @@ source_df["base_source"] = source_df["source_text"].apply(remove_parentheses_tex
103
 
104
  def format_verified_result(df, query, method, top_k=10):
105
  result = df.copy()
106
-
107
  result = result.sort_values(
108
  by=["quality_score", "duplicate_count"],
109
  ascending=[False, False]
@@ -134,7 +121,6 @@ def format_verified_result(df, query, method, top_k=10):
134
  result["confidence"] = "high"
135
 
136
  result["note"] = "Verified dictionary match."
137
-
138
  return result
139
 
140
 
@@ -167,9 +153,7 @@ def trained_semantic_fallback(query, top_k=5, search_k_per_language=20):
167
  continue
168
 
169
  if idx not in best_by_index or score > best_by_index[idx]["score"]:
170
- best_by_index[idx] = {
171
- "score": score
172
- }
173
 
174
  ranked = sorted(
175
  best_by_index.items(),
@@ -196,7 +180,7 @@ def trained_semantic_fallback(query, top_k=5, search_k_per_language=20):
196
  return "low_trained_semantic_candidate"
197
 
198
  result["confidence"] = result["score"].apply(label_score)
199
- result["note"] = "No verified dictionary match. Semantic candidate only; not a confirmed translation."
200
 
201
  keep_cols = [
202
  "query",
@@ -224,7 +208,7 @@ def safe_search(query):
224
  query = clean_text(query)
225
 
226
  if not query:
227
- return "Please enter a word.", pd.DataFrame()
228
 
229
  q_norm = normalize_lookup_text(query)
230
  q_base = remove_parentheses_text(query)
@@ -233,62 +217,261 @@ def safe_search(query):
233
 
234
  if len(exact) > 0:
235
  result = format_verified_result(exact, query, "hybrid_exact_source_match", top_k=10)
236
- return "Verified dictionary match found.", result
 
 
 
 
237
 
238
  base = source_df[source_df["base_source"] == q_base].copy()
239
 
240
  if len(base) > 0:
241
  result = format_verified_result(base, query, "hybrid_base_form_match", top_k=10)
242
- return "Verified base-form dictionary match found.", result
 
 
 
 
243
 
244
  semantic = trained_semantic_fallback(query, top_k=5, search_k_per_language=20)
245
 
246
  if len(semantic) == 0:
247
- return "No verified dictionary match or semantic candidate found.", pd.DataFrame()
 
 
 
 
248
 
249
  strong = semantic[semantic["score"] >= 0.88].copy()
250
 
251
  if len(strong) > 0:
252
- return "No verified dictionary match found. Showing trained semantic candidates only.", strong
253
-
254
- return "No verified dictionary match found. Semantic scores are below safe threshold; no translation is claimed.", semantic
 
 
 
 
 
 
 
 
255
 
256
 
257
- description = """
258
- CrossTalk AI Full Deployment
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
- This is the full trained hybrid lexical retrieval system:
261
- 1. exact dictionary matching
262
- 2. base-form matching
263
- 3. fine-tuned multilingual E5 semantic fallback
264
- 4. FAISS vector search
265
- 5. confidence-aware safe output handling
266
 
267
- Semantic fallback results are candidate suggestions only, not verified translations.
268
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
- demo = gr.Interface(
271
- fn=safe_search,
272
- inputs=gr.Textbox(
273
- label="Enter source / ethnic word",
274
- placeholder="Example: kəkhyáŋ, Hula, Aina"
275
- ),
276
- outputs=[
277
- gr.Textbox(label="System Message"),
278
- gr.Dataframe(label="Results")
279
- ],
280
- title="CrossTalk AI Full",
281
- description=description,
282
- examples=[
283
- ["kəkhyáŋ"],
284
- ["Hula"],
285
- ["Aina"],
286
- ["aam"],
287
- ["bajaoo"],
288
- ["unknown tribal word"]
289
- ],
290
- flagging_mode="never"
291
- )
292
 
293
  if __name__ == "__main__":
294
  demo.launch()
 
10
 
11
  MODEL_REPO_ID = "ElMETRICO/crosstalk-ai-full-artifacts"
12
 
13
+ print("Downloading artifacts from:", MODEL_REPO_ID)
14
+ artifact_dir = Path(snapshot_download(repo_id=MODEL_REPO_ID, repo_type="model"))
 
 
 
 
15
 
16
  MODEL_DIR = artifact_dir / "model" / "e5_lexical_contrastive_finetuned"
17
  INDEX_PATH = artifact_dir / "artifacts" / "trained_e5_source_to_meaning.index"
18
  DF_PATH = artifact_dir / "artifacts" / "trained_e5_source_to_meaning_df.csv"
19
 
 
 
 
 
20
  print("Loading fine-tuned E5 model...")
21
  model = SentenceTransformer(str(MODEL_DIR))
22
  model.max_seq_length = 128
 
24
  print("Loading FAISS index...")
25
  source_index = faiss.read_index(str(INDEX_PATH))
26
 
27
+ print("Loading dataframe...")
28
  source_df = pd.read_csv(DF_PATH, encoding="utf-8-sig")
29
  print("Rows loaded:", len(source_df))
30
 
 
37
 
38
 
39
  def normalize_lookup_text(x):
40
+ return clean_text(x).lower()
 
 
41
 
42
 
43
  def remove_parentheses_text(x):
 
49
 
50
  def count_source_files(x):
51
  x = "" if pd.isna(x) else str(x)
52
+ return len([p for p in x.split("||") if p.strip()]) if x.strip() else 0
 
 
53
 
54
 
55
  def add_quality_score(df):
 
80
 
81
  source_df = add_quality_score(source_df)
82
 
83
+ for col in ["language", "source_text", "english_meaning", "bangla_meaning", "part_of_speech"]:
84
  if col not in source_df.columns:
85
  source_df[col] = ""
86
  source_df[col] = source_df[col].apply(clean_text)
 
91
 
92
  def format_verified_result(df, query, method, top_k=10):
93
  result = df.copy()
 
94
  result = result.sort_values(
95
  by=["quality_score", "duplicate_count"],
96
  ascending=[False, False]
 
121
  result["confidence"] = "high"
122
 
123
  result["note"] = "Verified dictionary match."
 
124
  return result
125
 
126
 
 
153
  continue
154
 
155
  if idx not in best_by_index or score > best_by_index[idx]["score"]:
156
+ best_by_index[idx] = {"score": score}
 
 
157
 
158
  ranked = sorted(
159
  best_by_index.items(),
 
180
  return "low_trained_semantic_candidate"
181
 
182
  result["confidence"] = result["score"].apply(label_score)
183
+ result["note"] = "Semantic candidate only; not a confirmed translation."
184
 
185
  keep_cols = [
186
  "query",
 
208
  query = clean_text(query)
209
 
210
  if not query:
211
+ return "⚠️ Please enter a word.", "No input provided.", pd.DataFrame()
212
 
213
  q_norm = normalize_lookup_text(query)
214
  q_base = remove_parentheses_text(query)
 
217
 
218
  if len(exact) > 0:
219
  result = format_verified_result(exact, query, "hybrid_exact_source_match", top_k=10)
220
+ return (
221
+ "✅ Verified dictionary match found.",
222
+ f"High-confidence verified dictionary output for **{query}**.",
223
+ result
224
+ )
225
 
226
  base = source_df[source_df["base_source"] == q_base].copy()
227
 
228
  if len(base) > 0:
229
  result = format_verified_result(base, query, "hybrid_base_form_match", top_k=10)
230
+ return (
231
+ "✅ Verified base-form match found.",
232
+ f"High-confidence base-form dictionary output for **{query}**.",
233
+ result
234
+ )
235
 
236
  semantic = trained_semantic_fallback(query, top_k=5, search_k_per_language=20)
237
 
238
  if len(semantic) == 0:
239
+ return (
240
+ "❌ No match found.",
241
+ "No verified dictionary match or semantic candidate was found.",
242
+ pd.DataFrame()
243
+ )
244
 
245
  strong = semantic[semantic["score"] >= 0.88].copy()
246
 
247
  if len(strong) > 0:
248
+ return (
249
+ "🧠 Semantic candidates found.",
250
+ "No verified dictionary match was found. Showing fine-tuned E5 semantic candidates only. These are suggestions, not confirmed translations.",
251
+ strong
252
+ )
253
+
254
+ return (
255
+ "⚠️ Low-confidence semantic candidates.",
256
+ "No verified dictionary match was found. Semantic scores are below the safe verification threshold, so no translation is claimed.",
257
+ semantic
258
+ )
259
 
260
 
261
+ custom_css = """
262
+ .gradio-container {
263
+ background:
264
+ radial-gradient(circle at 15% 5%, rgba(139,92,246,0.32), transparent 28%),
265
+ radial-gradient(circle at 90% 10%, rgba(168,85,247,0.22), transparent 26%),
266
+ linear-gradient(180deg, #07020F 0%, #090716 50%, #07020F 100%) !important;
267
+ color: #F8F5FF !important;
268
+ font-family: Inter, ui-sans-serif, system-ui, sans-serif !important;
269
+ }
270
+
271
+ #shell {
272
+ max-width: 1280px;
273
+ margin: auto;
274
+ }
275
+
276
+ .hero {
277
+ background: linear-gradient(135deg, rgba(18,16,38,0.96), rgba(37,22,78,0.82));
278
+ border: 1px solid rgba(255,255,255,0.10);
279
+ border-radius: 30px;
280
+ padding: 34px;
281
+ margin-bottom: 22px;
282
+ box-shadow: 0 26px 80px rgba(0,0,0,0.42);
283
+ }
284
+
285
+ .badge {
286
+ display: inline-block;
287
+ padding: 8px 13px;
288
+ border-radius: 999px;
289
+ background: rgba(139,92,246,0.16);
290
+ border: 1px solid rgba(139,92,246,0.36);
291
+ color: #EDE7FF;
292
+ font-weight: 800;
293
+ font-size: 13px;
294
+ margin-bottom: 14px;
295
+ }
296
+
297
+ .title {
298
+ font-size: 48px;
299
+ line-height: 1.02;
300
+ font-weight: 950;
301
+ letter-spacing: -0.05em;
302
+ margin: 0;
303
+ background: linear-gradient(135deg, #FFFFFF, #D9CCFF, #A78BFA);
304
+ -webkit-background-clip: text;
305
+ -webkit-text-fill-color: transparent;
306
+ }
307
+
308
+ .subtitle {
309
+ max-width: 980px;
310
+ margin-top: 15px;
311
+ color: #BDB2DE;
312
+ font-size: 16px;
313
+ line-height: 1.7;
314
+ }
315
+
316
+ .metrics {
317
+ display: grid;
318
+ grid-template-columns: repeat(4, 1fr);
319
+ gap: 14px;
320
+ margin-top: 24px;
321
+ }
322
+
323
+ .metric {
324
+ padding: 17px;
325
+ border-radius: 20px;
326
+ background: rgba(255,255,255,0.055);
327
+ border: 1px solid rgba(255,255,255,0.09);
328
+ }
329
+
330
+ .metric strong {
331
+ display: block;
332
+ color: #FFFFFF;
333
+ font-size: 25px;
334
+ font-weight: 950;
335
+ }
336
+
337
+ .metric span {
338
+ color: #B8ADD8;
339
+ font-size: 12px;
340
+ }
341
+
342
+ .panel {
343
+ background: rgba(18,16,38,0.78) !important;
344
+ border: 1px solid rgba(255,255,255,0.10) !important;
345
+ border-radius: 24px !important;
346
+ padding: 18px !important;
347
+ box-shadow: 0 20px 55px rgba(0,0,0,0.30);
348
+ }
349
+
350
+ button.primary-btn {
351
+ background: linear-gradient(135deg, #8B5CF6, #A855F7) !important;
352
+ border: none !important;
353
+ border-radius: 16px !important;
354
+ color: white !important;
355
+ font-weight: 900 !important;
356
+ min-height: 48px !important;
357
+ }
358
+
359
+ button.secondary-btn {
360
+ background: rgba(255,255,255,0.08) !important;
361
+ border: 1px solid rgba(255,255,255,0.12) !important;
362
+ border-radius: 16px !important;
363
+ color: white !important;
364
+ font-weight: 800 !important;
365
+ min-height: 48px !important;
366
+ }
367
+
368
+ textarea, input {
369
+ background: rgba(5,4,14,0.78) !important;
370
+ color: white !important;
371
+ border: 1px solid rgba(255,255,255,0.14) !important;
372
+ border-radius: 16px !important;
373
+ }
374
+
375
+ .footer {
376
+ text-align: center;
377
+ color: #A99FD0;
378
+ font-size: 13px;
379
+ line-height: 1.6;
380
+ margin-top: 18px;
381
+ }
382
+
383
+ @media (max-width: 900px) {
384
+ .title { font-size: 34px; }
385
+ .metrics { grid-template-columns: repeat(2, 1fr); }
386
+ }
387
+ """
388
 
 
 
 
 
 
 
389
 
390
+ with gr.Blocks(
391
+ css=custom_css,
392
+ theme=gr.themes.Soft(
393
+ primary_hue="violet",
394
+ secondary_hue="purple",
395
+ neutral_hue="slate"
396
+ )
397
+ ) as demo:
398
+
399
+ with gr.Column(elem_id="shell"):
400
+ gr.HTML(
401
+ """
402
+ <div class="hero">
403
+ <div class="badge">🌐 Full trained hybrid retrieval system</div>
404
+ <h1 class="title">CrossTalk AI</h1>
405
+ <div class="subtitle">
406
+ A confidence-aware lexical retrieval and translation support system for low-resource ethnic languages of Bangladesh.
407
+ It combines exact dictionary matching, base-form matching, fine-tuned multilingual E5 semantic fallback,
408
+ FAISS vector search, and safe output handling.
409
+ </div>
410
+ <div class="metrics">
411
+ <div class="metric"><strong>12</strong><span>Ethnic language groups</span></div>
412
+ <div class="metric"><strong>70K+</strong><span>Indexed source entries</span></div>
413
+ <div class="metric"><strong>E5</strong><span>Fine-tuned semantic retriever</span></div>
414
+ <div class="metric"><strong>FAISS</strong><span>Vector search backend</span></div>
415
+ </div>
416
+ </div>
417
+ """
418
+ )
419
+
420
+ with gr.Row():
421
+ with gr.Column(scale=5, elem_classes=["panel"]):
422
+ gr.Markdown("### Search source word")
423
+ query = gr.Textbox(
424
+ label="Input word",
425
+ placeholder="Try: kəkhyáŋ, Hula, Aina, aam, bajaoo",
426
+ lines=1
427
+ )
428
+
429
+ with gr.Row():
430
+ clear_btn = gr.Button("Clear", elem_classes=["secondary-btn"])
431
+ submit_btn = gr.Button("Search", elem_classes=["primary-btn"])
432
+
433
+ gr.Examples(
434
+ examples=[
435
+ ["kəkhyáŋ"],
436
+ ["Hula"],
437
+ ["Aina"],
438
+ ["aam"],
439
+ ["bajaoo"],
440
+ ["unknown tribal word"]
441
+ ],
442
+ inputs=query,
443
+ label="Quick examples"
444
+ )
445
+
446
+ with gr.Column(scale=7, elem_classes=["panel"]):
447
+ gr.Markdown("### Retrieval output")
448
+ status = gr.Textbox(label="System status", lines=1)
449
+ summary = gr.Markdown(
450
+ value="Enter a source word to retrieve verified dictionary matches or semantic candidates."
451
+ )
452
+ results = gr.Dataframe(
453
+ label="Results",
454
+ interactive=False,
455
+ wrap=True
456
+ )
457
+
458
+ gr.HTML(
459
+ """
460
+ <div class="footer">
461
+ Exact and base-form matches are verified dictionary outputs.
462
+ Fine-tuned semantic fallback results are candidate suggestions only and should not be treated as confirmed translations.
463
+ </div>
464
+ """
465
+ )
466
+
467
+ submit_btn.click(fn=safe_search, inputs=query, outputs=[status, summary, results])
468
+ query.submit(fn=safe_search, inputs=query, outputs=[status, summary, results])
469
+ clear_btn.click(
470
+ fn=lambda: ("", "Ready.", "Enter a source word to retrieve verified dictionary matches or semantic candidates.", pd.DataFrame()),
471
+ inputs=None,
472
+ outputs=[query, status, summary, results]
473
+ )
474
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
 
476
  if __name__ == "__main__":
477
  demo.launch()