Lavender825 commited on
Commit
4ad0c8f
·
1 Parent(s): 0ace5b0

Create app-style Hugging Face interface

Browse files
Files changed (35) hide show
  1. __pycache__/app.cpython-314.pyc +0 -0
  2. app.py +124 -74
  3. src/__pycache__/__init__.cpython-310.pyc +0 -0
  4. src/__pycache__/__init__.cpython-312.pyc +0 -0
  5. src/__pycache__/ablation.cpython-312.pyc +0 -0
  6. src/__pycache__/ablation.cpython-314.pyc +0 -0
  7. src/__pycache__/aspect_dict.cpython-312.pyc +0 -0
  8. src/__pycache__/aspect_dict.cpython-314.pyc +0 -0
  9. src/__pycache__/baselines.cpython-312.pyc +0 -0
  10. src/__pycache__/config.cpython-310.pyc +0 -0
  11. src/__pycache__/config.cpython-312.pyc +0 -0
  12. src/__pycache__/config.cpython-314.pyc +0 -0
  13. src/__pycache__/data_download.cpython-312.pyc +0 -0
  14. src/__pycache__/data_download.cpython-314.pyc +0 -0
  15. src/__pycache__/dataset.cpython-310.pyc +0 -0
  16. src/__pycache__/dataset.cpython-312.pyc +0 -0
  17. src/__pycache__/evaluator.cpython-310.pyc +0 -0
  18. src/__pycache__/evaluator.cpython-312.pyc +0 -0
  19. src/__pycache__/explainer.cpython-310.pyc +0 -0
  20. src/__pycache__/explainer.cpython-312.pyc +0 -0
  21. src/__pycache__/inference.cpython-312.pyc +0 -0
  22. src/__pycache__/meta_encoder.cpython-310.pyc +0 -0
  23. src/__pycache__/meta_encoder.cpython-312.pyc +0 -0
  24. src/__pycache__/models.cpython-310.pyc +0 -0
  25. src/__pycache__/models.cpython-312.pyc +0 -0
  26. src/__pycache__/models.cpython-314.pyc +0 -0
  27. src/__pycache__/preprocess.cpython-312.pyc +0 -0
  28. src/__pycache__/trainer.cpython-310.pyc +0 -0
  29. src/__pycache__/trainer.cpython-312.pyc +0 -0
  30. src/__pycache__/trainer.cpython-314.pyc +0 -0
  31. src/__pycache__/utils.cpython-310.pyc +0 -0
  32. src/__pycache__/utils.cpython-312.pyc +0 -0
  33. src/__pycache__/utils.cpython-314.pyc +0 -0
  34. src/__pycache__/weak_labeling.cpython-312.pyc +0 -0
  35. src/__pycache__/weak_labeling.cpython-314.pyc +0 -0
__pycache__/app.cpython-314.pyc DELETED
Binary file (54.6 kB)
 
app.py CHANGED
@@ -370,131 +370,181 @@ def _status_html() -> str:
370
 
371
 
372
  CSS = """
373
- :root { --accent:#f97316; --ink:#0f172a; --muted:#475569; --line:#dbe3ef; }
374
- .gradio-container { max-width:1240px !important; margin:auto !important; color:var(--ink); }
375
- #hero { border:1px solid var(--line); background:#f8fbff; border-radius:8px; padding:22px 28px; margin:10px 0 22px; }
376
- #hero h1 { margin:0 0 8px; font-size:30px; line-height:1.15; }
377
- #hero p { margin:0; color:#334155; }
378
- button.primary, .gradio-button.primary { background:var(--accent) !important; border-color:var(--accent) !important; color:white !important; font-weight:700 !important; }
379
- .status { border-radius:8px; padding:12px 14px; margin:8px 0 18px; border:1px solid var(--line); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  .status.ok { background:#ecfdf5; border-color:#86efac; color:#065f46; }
381
  .status.bad { background:#fff1f2; border-color:#fda4af; color:#991b1b; }
382
- .product-card, .overall-card, .note-card { border:1px solid var(--line); border-radius:8px; padding:16px; background:white; }
 
 
 
 
 
 
383
  .muted { color:var(--muted); }
384
  .meta-pills { display:flex; flex-wrap:wrap; gap:8px; margin:10px 0; }
385
- .meta-pills span { background:#f1f5f9; border:1px solid #e2e8f0; border-radius:999px; padding:5px 9px; font-size:13px; }
386
- .chip { display:inline-block; border-radius:999px; padding:5px 10px; font-weight:700; font-size:13px; }
387
  .conf { color:#334155; font-size:13px; margin-left:8px; }
388
  .small-label { color:#475569; font-size:13px; margin-bottom:8px; }
389
- .prob-row { display:grid; grid-template-columns:82px 1fr 44px; gap:8px; align-items:center; font-size:13px; margin:6px 0; }
390
- .bar { height:8px; background:#e2e8f0; border-radius:999px; overflow:hidden; }
391
- .bar i { display:block; height:100%; background:var(--accent); }
392
- .aspect-grid { display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:12px; }
393
- .aspect-card { border:1px solid var(--line); border-top:4px solid #64748b; border-radius:8px; padding:13px; background:white; min-height:165px; }
394
- .aspect-card.focus { border-top-color:var(--accent); box-shadow:0 2px 10px rgba(15,23,42,.08); }
395
- .aspect-card.dim { opacity:.66; }
 
 
396
  .aspect-head { display:flex; justify-content:space-between; align-items:center; margin-bottom:8px; }
397
  .aspect-head span, .source-line { color:#475569; font-size:12px; }
398
  .aspect-card p { margin:8px 0; color:#334155; font-size:13px; }
399
  .evidence-row { display:flex; flex-wrap:wrap; gap:5px; margin-top:8px; }
400
  .evidence-token { color:#1d4ed8; background:#eef2ff; border:1px solid #bfdbfe; border-radius:999px; padding:3px 8px; font-size:12px; }
401
- .review-box { border:1px dashed #cbd5e1; background:#f8fafc; border-radius:8px; padding:14px; line-height:1.6; }
402
- mark { background:#fde68a; color:#111827; border-radius:4px; padding:1px 3px; }
403
  .metric-grid { display:grid; grid-template-columns:repeat(3, 1fr); gap:14px; margin:8px 0 18px; }
404
- .metric { border:1px solid var(--line); border-radius:8px; padding:16px; background:white; }
405
  .metric span { display:block; color:#334155; font-size:13px; }
406
- .metric b { display:block; font-size:28px; margin:6px 0; }
407
  .metric small { color:#475569; }
408
- .table-wrap { border:1px solid var(--line); border-radius:8px; overflow:hidden; background:white; }
409
  .kv-table { width:100%; border-collapse:collapse; }
410
- .kv-table td { border-bottom:1px solid #e2e8f0; padding:9px 12px; }
411
- .kv-table td:first-child { width:220px; color:#334155; font-weight:700; background:#f8fafc; }
412
- @media (max-width:860px) { .aspect-grid, .metric-grid { grid-template-columns:1fr; } }
 
 
 
 
 
413
  """
414
 
415
-
416
  def build_app() -> gr.Blocks:
417
  categories = ["All"] + sorted({p["category"] for p in PRODUCTS})
418
  tags = sorted({tag for p in PRODUCTS for tag in p.get("tags", [])})
419
- with gr.Blocks(css=CSS, title="Clothing Sentiment Analysis") as demo:
420
- gr.HTML('<div id="hero"><h1>Aspect-Level Sentiment Analysis for Clothing Reviews</h1><p>BERT + metadata cross-attention fusion for consumer decision support and merchant diagnostics.</p></div>')
 
 
 
 
 
 
 
421
  gr.HTML(_status_html())
422
  with gr.Tabs():
423
- with gr.Tab("Consumer Interface"):
 
424
  with gr.Row():
425
- with gr.Column(scale=4):
 
426
  product_select = gr.Dropdown(_product_names(), value=_product_names()[0], label="Choose a product")
427
  consumer_aspect = gr.Dropdown(["All"] + ASPECTS, value="All", label="Highlight aspect evidence")
428
  product_detail = gr.HTML()
429
  evidence_html = gr.HTML()
430
- with gr.Column(scale=6):
 
431
  aspect_html = gr.HTML()
432
- consumer_table = gr.Dataframe(headers=["Aspect", "Prediction", "Confidence", "Key review evidence"], datatype=["str", "str", "number", "str"], label="Aspect-level result table", interactive=False)
433
- gr.Markdown("### Product Finder")
 
434
  with gr.Row():
435
- filter_aspect = gr.Dropdown(["Overall"] + ASPECTS, value="Overall", label="Target metric")
436
- filter_sentiment = gr.Radio(["Any", "Positive", "Negative", "Not_Mentioned", "Neutral"], value="Any", label="Preferred prediction")
437
- filter_category = gr.Dropdown(categories, value="All", label="Category")
438
- with gr.Row():
439
- filter_tags = gr.CheckboxGroup(tags, label="Required metadata tags")
440
- min_rating = gr.Slider(3.0, 5.0, value=4.0, step=0.1, label="Minimum product rating")
441
- filter_btn = gr.Button("Filter Products", variant="primary")
 
 
442
  filter_summary = gr.HTML()
443
  filter_table = gr.Dataframe(headers=["Product", "Category", "Prediction", "Confidence", "Rating", "Price", "Metadata"], datatype=["str", "str", "str", "number", "number", "number", "str"], interactive=False, label="Filtered product candidates")
444
  product_select.change(consumer_product_view, [product_select, consumer_aspect], [product_detail, aspect_html, evidence_html, consumer_table])
445
  consumer_aspect.change(consumer_product_view, [product_select, consumer_aspect], [product_detail, aspect_html, evidence_html, consumer_table])
446
  filter_btn.click(filter_products, [filter_aspect, filter_sentiment, filter_category, filter_tags, min_rating], [filter_table, filter_summary])
447
 
448
- with gr.Tab("Merchant Interface"):
 
449
  with gr.Row():
450
  with gr.Column(scale=4):
451
- gr.Markdown("### Model Basic Information")
452
  gr.HTML(model_info_html())
453
- with gr.Column(scale=6):
454
- gr.Markdown("### Product Score Monitor")
455
  merchant_metric = gr.Dropdown(["Overall"] + ASPECTS, value="Overall", label="Choose overall or aspect")
456
- merchant_scores = gr.Dataframe(headers=["Product", "Category", "Metric", "Prediction", "Confidence", "Price", "Rating"], value=[["Click Refresh Product Scores", "", "", "", 0.0, 0.0, 0.0]], datatype=["str", "str", "str", "str", "number", "number", "number"], interactive=False)
457
  refresh_scores = gr.Button("Refresh Product Scores", variant="primary")
458
- gr.Markdown("### New Product Metadata Risk Screening")
459
- with gr.Row():
460
- new_features = gr.Textbox("Cotton Polyester Blend, Slim Fit, Graphic Print, Machine Wash", label="New product features")
461
- new_categories = gr.Textbox("Clothing > Women > Tops > T-Shirts", label="New product categories")
462
  with gr.Row():
463
- new_price = gr.Number(29.99, label="Price")
464
- new_rating = gr.Number(4.1, label="Expected or early average rating")
465
- new_count = gr.Number(35, label="Expected or early rating count")
466
- new_focus = gr.Dropdown(["All"] + ASPECTS, value="All", label="Focus aspect")
467
- screen_btn = gr.Button("Predict Metadata Risk", variant="primary")
 
 
 
 
468
  risk_summary = gr.HTML()
469
  risk_table = gr.Dataframe(headers=["Aspect", "Risk Level", "Model Signal", "Confidence", "Reason"], value=[["Click Predict Metadata Risk", "", "", 0.0, ""]], datatype=["str", "str", "str", "number", "str"], interactive=False)
470
- gr.Markdown("### External Review Prediction")
471
- external_review = gr.Textbox("The fabric is soft and the color looks good, but it runs small and the zipper feels weak.", label="External customer review", lines=4)
472
  with gr.Row():
473
- ext_features = gr.Textbox("Cotton Blend, Slim Fit, Zipper Closure", label="Product features")
474
- ext_categories = gr.Textbox("Clothing > Women > Jackets", label="Product categories")
475
- with gr.Row():
476
- ext_price = gr.Number(39.99, label="Price")
477
- ext_rating = gr.Number(4.2, label="Average rating")
478
- ext_count = gr.Number(312, label="Rating count")
479
- ext_aspect = gr.Dropdown(["All"] + ASPECTS, value="All", label="Highlight aspect")
480
- external_btn = gr.Button("Analyze External Review", variant="primary")
481
- ext_overall = gr.HTML()
482
- ext_aspects = gr.HTML()
483
- ext_table = gr.Dataframe(headers=["Aspect", "Prediction", "Confidence", "Key review evidence"], value=[["Click Analyze External Review", "", 0.0, ""]], datatype=["str", "str", "number", "str"], interactive=False)
 
 
 
 
484
  refresh_scores.click(merchant_product_scores, merchant_metric, merchant_scores)
485
  merchant_metric.change(merchant_product_scores, merchant_metric, merchant_scores)
486
  screen_btn.click(screen_new_product, [new_features, new_categories, new_price, new_rating, new_count, new_focus], [risk_summary, risk_table])
487
  external_btn.click(external_review_predict, [external_review, ext_features, ext_categories, ext_price, ext_rating, ext_count, ext_aspect], [ext_overall, ext_aspects, ext_table])
488
 
489
  with gr.Tab("Research Metrics"):
490
- gr.Markdown("Metrics are loaded from the bundled 10W0715 experiment reports.")
491
  research_cards = gr.HTML(research_cards_html())
492
- gr.Markdown("### 1. Overall Sentiment Metrics")
493
- overall_table = gr.Dataframe(headers=["Model", "Macro-F1", "Accuracy"], value=overall_metric_rows(), interactive=False)
494
- gr.Markdown("### 2. Aspect-Level Overall Comparison")
 
 
 
 
 
495
  aspect_table = gr.Dataframe(headers=["Aspect", "No-meta F1", "Proposed F1", "F1 Delta", "No-meta Acc", "Proposed Acc", "Acc Delta"], value=aspect_metric_rows(), interactive=False)
496
- gr.Markdown("### 3. Ablation Comparison")
497
- ablation_table = gr.Dataframe(headers=["Variant", "Mean Macro-F1", "Mean Accuracy"], value=ablation_rows(), interactive=False)
498
  refresh_research = gr.Button("Refresh Research Metrics", variant="primary")
499
  refresh_research.click(lambda: (research_cards_html(), overall_metric_rows(), aspect_metric_rows(), ablation_rows()), outputs=[research_cards, overall_table, aspect_table, ablation_table])
500
  demo.load(consumer_product_view, [product_select, consumer_aspect], [product_detail, aspect_html, evidence_html, consumer_table])
@@ -502,9 +552,9 @@ def build_app() -> gr.Blocks:
502
  demo.load(filter_products, [filter_aspect, filter_sentiment, filter_category, filter_tags, min_rating], [filter_table, filter_summary])
503
  return demo
504
 
505
-
506
  demo = build_app()
507
 
508
  if __name__ == "__main__":
509
  demo.launch()
510
 
 
 
370
 
371
 
372
  CSS = """
373
+ :root {
374
+ --bg:#f5f7fb;
375
+ --surface:#ffffff;
376
+ --surface-2:#f8fafc;
377
+ --ink:#0f172a;
378
+ --muted:#64748b;
379
+ --line:#d9e2ef;
380
+ --accent:#f97316;
381
+ --accent-2:#fb923c;
382
+ --green:#16a34a;
383
+ --red:#dc2626;
384
+ --blue:#2563eb;
385
+ --shadow:0 18px 45px rgba(15,23,42,.08);
386
+ }
387
+ body, .gradio-container { background:var(--bg) !important; color:var(--ink); }
388
+ .gradio-container { max-width:1280px !important; margin:auto !important; font-family:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important; }
389
+ footer { display:none !important; }
390
+ #app-hero { margin:12px 0 18px; padding:24px 28px; border-radius:22px; background:linear-gradient(135deg,#111827 0%,#1e293b 55%,#f97316 160%); color:white; box-shadow:var(--shadow); }
391
+ #app-hero .eyebrow { text-transform:uppercase; letter-spacing:.12em; font-size:12px; color:#fed7aa; font-weight:800; margin-bottom:8px; }
392
+ #app-hero h1 { margin:0; font-size:32px; line-height:1.12; letter-spacing:-.02em; }
393
+ #app-hero p { margin:10px 0 0; color:#e2e8f0; max-width:840px; }
394
+ #app-hero .hero-pills { display:flex; gap:8px; flex-wrap:wrap; margin-top:16px; }
395
+ #app-hero .hero-pills span { background:rgba(255,255,255,.12); border:1px solid rgba(255,255,255,.18); border-radius:999px; padding:7px 11px; font-size:13px; }
396
+ .status { border-radius:14px; padding:12px 14px; margin:8px 0 18px; border:1px solid var(--line); }
397
  .status.ok { background:#ecfdf5; border-color:#86efac; color:#065f46; }
398
  .status.bad { background:#fff1f2; border-color:#fda4af; color:#991b1b; }
399
+ .app-section { display:flex; align-items:flex-end; justify-content:space-between; gap:16px; margin:8px 0 14px; }
400
+ .app-section h2 { margin:0; font-size:24px; letter-spacing:-.02em; }
401
+ .app-section p { margin:5px 0 0; color:var(--muted); }
402
+ .app-badge { background:#fff7ed; color:#c2410c; border:1px solid #fed7aa; border-radius:999px; padding:7px 12px; font-weight:800; font-size:13px; }
403
+ .app-card, .product-card, .overall-card, .note-card { border:1px solid var(--line); border-radius:18px; padding:18px; background:var(--surface); box-shadow:0 10px 24px rgba(15,23,42,.05); }
404
+ .input-card { border:1px solid var(--line); border-radius:18px; padding:16px; background:var(--surface); box-shadow:0 10px 24px rgba(15,23,42,.05); }
405
+ .panel-title { margin:0 0 12px; font-size:17px; font-weight:850; }
406
  .muted { color:var(--muted); }
407
  .meta-pills { display:flex; flex-wrap:wrap; gap:8px; margin:10px 0; }
408
+ .meta-pills span { background:#f1f5f9; border:1px solid #e2e8f0; border-radius:999px; padding:6px 10px; font-size:13px; }
409
+ .chip { display:inline-block; border-radius:999px; padding:5px 10px; font-weight:800; font-size:13px; }
410
  .conf { color:#334155; font-size:13px; margin-left:8px; }
411
  .small-label { color:#475569; font-size:13px; margin-bottom:8px; }
412
+ .overall-card { margin-top:14px; background:linear-gradient(180deg,#ffffff 0%,#f8fafc 100%); }
413
+ .overall-main { display:flex; align-items:center; gap:8px; margin-bottom:10px; }
414
+ .prob-row { display:grid; grid-template-columns:82px 1fr 44px; gap:8px; align-items:center; font-size:13px; margin:7px 0; }
415
+ .bar { height:9px; background:#e2e8f0; border-radius:999px; overflow:hidden; }
416
+ .bar i { display:block; height:100%; background:linear-gradient(90deg,var(--accent),var(--accent-2)); }
417
+ .aspect-grid { display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:14px; }
418
+ .aspect-card { border:1px solid var(--line); border-top:5px solid #64748b; border-radius:18px; padding:15px; background:white; min-height:172px; box-shadow:0 10px 22px rgba(15,23,42,.05); }
419
+ .aspect-card.focus { border-top-color:var(--accent); box-shadow:0 18px 34px rgba(249,115,22,.13); }
420
+ .aspect-card.dim { opacity:.58; }
421
  .aspect-head { display:flex; justify-content:space-between; align-items:center; margin-bottom:8px; }
422
  .aspect-head span, .source-line { color:#475569; font-size:12px; }
423
  .aspect-card p { margin:8px 0; color:#334155; font-size:13px; }
424
  .evidence-row { display:flex; flex-wrap:wrap; gap:5px; margin-top:8px; }
425
  .evidence-token { color:#1d4ed8; background:#eef2ff; border:1px solid #bfdbfe; border-radius:999px; padding:3px 8px; font-size:12px; }
426
+ .review-box { border:1px dashed #cbd5e1; background:#f8fafc; border-radius:14px; padding:15px; line-height:1.65; }
427
+ mark { background:#fde68a; color:#111827; border-radius:5px; padding:1px 4px; }
428
  .metric-grid { display:grid; grid-template-columns:repeat(3, 1fr); gap:14px; margin:8px 0 18px; }
429
+ .metric { border:1px solid var(--line); border-radius:18px; padding:18px; background:white; box-shadow:0 10px 24px rgba(15,23,42,.05); }
430
  .metric span { display:block; color:#334155; font-size:13px; }
431
+ .metric b { display:block; font-size:30px; margin:6px 0; letter-spacing:-.03em; }
432
  .metric small { color:#475569; }
433
+ .table-wrap { border:1px solid var(--line); border-radius:16px; overflow:hidden; background:white; box-shadow:0 10px 24px rgba(15,23,42,.05); }
434
  .kv-table { width:100%; border-collapse:collapse; }
435
+ .kv-table td { border-bottom:1px solid #e2e8f0; padding:11px 13px; }
436
+ .kv-table td:first-child { width:220px; color:#334155; font-weight:800; background:#f8fafc; }
437
+ button.primary, .gradio-button.primary { background:linear-gradient(135deg,var(--accent),var(--accent-2)) !important; border:none !important; color:white !important; font-weight:850 !important; border-radius:13px !important; min-height:44px !important; box-shadow:0 12px 24px rgba(249,115,22,.22) !important; }
438
+ .gradio-tabs { border-radius:18px !important; }
439
+ .tab-nav button { font-weight:750 !important; }
440
+ .gradio-dataframe, .wrap.svelte-1lcyrx4, .dataframe-container { border-radius:16px !important; overflow:hidden !important; }
441
+ textarea, input, select { border-radius:12px !important; }
442
+ @media (max-width:920px) { .aspect-grid, .metric-grid { grid-template-columns:1fr; } #app-hero h1 { font-size:26px; } }
443
  """
444
 
 
445
  def build_app() -> gr.Blocks:
446
  categories = ["All"] + sorted({p["category"] for p in PRODUCTS})
447
  tags = sorted({tag for p in PRODUCTS for tag in p.get("tags", [])})
448
+ with gr.Blocks(css=CSS, title="Clothing Sentiment Intelligence") as demo:
449
+ gr.HTML(
450
+ '<div id="app-hero">'
451
+ '<div class="eyebrow">BERT + Metadata Cross-Attention</div>'
452
+ '<h1>Clothing Review Sentiment Intelligence App</h1>'
453
+ '<p>Explore overall sentiment, six aspect-level opinions, metadata-driven risks, and research metrics from the 10W0715 experiment.</p>'
454
+ '<div class="hero-pills"><span>Consumer decision support</span><span>Merchant diagnostics</span><span>Research dashboard</span></div>'
455
+ '</div>'
456
+ )
457
  gr.HTML(_status_html())
458
  with gr.Tabs():
459
+ with gr.Tab("Consumer App"):
460
+ gr.HTML('<div class="app-section"><div><h2>Shopping Review Analyzer</h2><p>Select a product, inspect overall sentiment, and compare aspect-level strengths and weaknesses.</p></div><span class="app-badge">Consumer view</span></div>')
461
  with gr.Row():
462
+ with gr.Column(scale=4, elem_classes=["input-card"]):
463
+ gr.HTML('<div class="panel-title">Product context</div>')
464
  product_select = gr.Dropdown(_product_names(), value=_product_names()[0], label="Choose a product")
465
  consumer_aspect = gr.Dropdown(["All"] + ASPECTS, value="All", label="Highlight aspect evidence")
466
  product_detail = gr.HTML()
467
  evidence_html = gr.HTML()
468
+ with gr.Column(scale=7):
469
+ gr.HTML('<div class="panel-title">Aspect sentiment cards</div>')
470
  aspect_html = gr.HTML()
471
+ with gr.Accordion("Technical aspect result table", open=False):
472
+ consumer_table = gr.Dataframe(headers=["Aspect", "Prediction", "Confidence", "Key review evidence"], datatype=["str", "str", "number", "str"], interactive=False)
473
+ gr.HTML('<div class="app-section"><div><h2>Product Finder</h2><p>Filter products by metadata and target sentiment signal.</p></div></div>')
474
  with gr.Row():
475
+ with gr.Column(scale=3, elem_classes=["input-card"]):
476
+ filter_aspect = gr.Dropdown(["Overall"] + ASPECTS, value="Overall", label="Target metric")
477
+ filter_sentiment = gr.Radio(["Any", "Positive", "Negative", "Not_Mentioned", "Neutral"], value="Any", label="Preferred prediction")
478
+ with gr.Column(scale=4, elem_classes=["input-card"]):
479
+ filter_category = gr.Dropdown(categories, value="All", label="Category")
480
+ filter_tags = gr.CheckboxGroup(tags, label="Required metadata tags")
481
+ with gr.Column(scale=3, elem_classes=["input-card"]):
482
+ min_rating = gr.Slider(3.0, 5.0, value=4.0, step=0.1, label="Minimum product rating")
483
+ filter_btn = gr.Button("Find Matching Products", variant="primary")
484
  filter_summary = gr.HTML()
485
  filter_table = gr.Dataframe(headers=["Product", "Category", "Prediction", "Confidence", "Rating", "Price", "Metadata"], datatype=["str", "str", "str", "number", "number", "number", "str"], interactive=False, label="Filtered product candidates")
486
  product_select.change(consumer_product_view, [product_select, consumer_aspect], [product_detail, aspect_html, evidence_html, consumer_table])
487
  consumer_aspect.change(consumer_product_view, [product_select, consumer_aspect], [product_detail, aspect_html, evidence_html, consumer_table])
488
  filter_btn.click(filter_products, [filter_aspect, filter_sentiment, filter_category, filter_tags, min_rating], [filter_table, filter_summary])
489
 
490
+ with gr.Tab("Merchant App"):
491
+ gr.HTML('<div class="app-section"><div><h2>Merchant Sentiment Operations</h2><p>Monitor products, screen new metadata, and test external customer reviews.</p></div><span class="app-badge">Business view</span></div>')
492
  with gr.Row():
493
  with gr.Column(scale=4):
494
+ gr.HTML('<div class="panel-title">Model basic information</div>')
495
  gr.HTML(model_info_html())
496
+ with gr.Column(scale=6, elem_classes=["input-card"]):
497
+ gr.HTML('<div class="panel-title">Product score monitor</div>')
498
  merchant_metric = gr.Dropdown(["Overall"] + ASPECTS, value="Overall", label="Choose overall or aspect")
 
499
  refresh_scores = gr.Button("Refresh Product Scores", variant="primary")
500
+ merchant_scores = gr.Dataframe(headers=["Product", "Category", "Metric", "Prediction", "Confidence", "Price", "Rating"], value=[["Click Refresh Product Scores", "", "", "", 0.0, 0.0, 0.0]], datatype=["str", "str", "str", "str", "number", "number", "number"], interactive=False)
501
+ gr.HTML('<div class="app-section"><div><h2>New Product Risk Screening</h2><p>Use metadata to preview which aspect may need QA or product-page clarification.</p></div></div>')
 
 
502
  with gr.Row():
503
+ with gr.Column(scale=6, elem_classes=["input-card"]):
504
+ new_features = gr.Textbox("Cotton Polyester Blend, Slim Fit, Graphic Print, Machine Wash", label="New product features")
505
+ new_categories = gr.Textbox("Clothing > Women > Tops > T-Shirts", label="New product categories")
506
+ with gr.Column(scale=4, elem_classes=["input-card"]):
507
+ new_price = gr.Number(29.99, label="Price")
508
+ new_rating = gr.Number(4.1, label="Expected or early average rating")
509
+ new_count = gr.Number(35, label="Expected or early rating count")
510
+ new_focus = gr.Dropdown(["All"] + ASPECTS, value="All", label="Focus aspect")
511
+ screen_btn = gr.Button("Predict Metadata Risk", variant="primary")
512
  risk_summary = gr.HTML()
513
  risk_table = gr.Dataframe(headers=["Aspect", "Risk Level", "Model Signal", "Confidence", "Reason"], value=[["Click Predict Metadata Risk", "", "", 0.0, ""]], datatype=["str", "str", "str", "number", "str"], interactive=False)
514
+ gr.HTML('<div class="app-section"><div><h2>External Review Prediction</h2><p>Paste any review and metadata to get overall and aspect-level predictions.</p></div></div>')
 
515
  with gr.Row():
516
+ with gr.Column(scale=4, elem_classes=["input-card"]):
517
+ external_review = gr.Textbox("The fabric is soft and the color looks good, but it runs small and the zipper feels weak.", label="External customer review", lines=5)
518
+ ext_features = gr.Textbox("Cotton Blend, Slim Fit, Zipper Closure", label="Product features")
519
+ ext_categories = gr.Textbox("Clothing > Women > Jackets", label="Product categories")
520
+ with gr.Row():
521
+ ext_price = gr.Number(39.99, label="Price")
522
+ ext_rating = gr.Number(4.2, label="Average rating")
523
+ ext_count = gr.Number(312, label="Rating count")
524
+ ext_aspect = gr.Dropdown(["All"] + ASPECTS, value="All", label="Highlight aspect")
525
+ external_btn = gr.Button("Analyze External Review", variant="primary")
526
+ with gr.Column(scale=6):
527
+ ext_overall = gr.HTML()
528
+ ext_aspects = gr.HTML()
529
+ with gr.Accordion("External review raw table", open=False):
530
+ ext_table = gr.Dataframe(headers=["Aspect", "Prediction", "Confidence", "Key review evidence"], value=[["Click Analyze External Review", "", 0.0, ""]], datatype=["str", "str", "number", "str"], interactive=False)
531
  refresh_scores.click(merchant_product_scores, merchant_metric, merchant_scores)
532
  merchant_metric.change(merchant_product_scores, merchant_metric, merchant_scores)
533
  screen_btn.click(screen_new_product, [new_features, new_categories, new_price, new_rating, new_count, new_focus], [risk_summary, risk_table])
534
  external_btn.click(external_review_predict, [external_review, ext_features, ext_categories, ext_price, ext_rating, ext_count, ext_aspect], [ext_overall, ext_aspects, ext_table])
535
 
536
  with gr.Tab("Research Metrics"):
537
+ gr.HTML('<div class="app-section"><div><h2>Experiment Dashboard</h2><p>Metrics are loaded from the bundled 10W0715 reports on the same held-out test split.</p></div><span class="app-badge">Research view</span></div>')
538
  research_cards = gr.HTML(research_cards_html())
539
+ with gr.Row():
540
+ with gr.Column(scale=1):
541
+ gr.Markdown("### Overall Sentiment Metrics")
542
+ overall_table = gr.Dataframe(headers=["Model", "Macro-F1", "Accuracy"], value=overall_metric_rows(), interactive=False)
543
+ with gr.Column(scale=1):
544
+ gr.Markdown("### Ablation Comparison")
545
+ ablation_table = gr.Dataframe(headers=["Variant", "Mean Macro-F1", "Mean Accuracy"], value=ablation_rows(), interactive=False)
546
+ gr.Markdown("### Six-Aspect Comparison")
547
  aspect_table = gr.Dataframe(headers=["Aspect", "No-meta F1", "Proposed F1", "F1 Delta", "No-meta Acc", "Proposed Acc", "Acc Delta"], value=aspect_metric_rows(), interactive=False)
 
 
548
  refresh_research = gr.Button("Refresh Research Metrics", variant="primary")
549
  refresh_research.click(lambda: (research_cards_html(), overall_metric_rows(), aspect_metric_rows(), ablation_rows()), outputs=[research_cards, overall_table, aspect_table, ablation_table])
550
  demo.load(consumer_product_view, [product_select, consumer_aspect], [product_detail, aspect_html, evidence_html, consumer_table])
 
552
  demo.load(filter_products, [filter_aspect, filter_sentiment, filter_category, filter_tags, min_rating], [filter_table, filter_summary])
553
  return demo
554
 
 
555
  demo = build_app()
556
 
557
  if __name__ == "__main__":
558
  demo.launch()
559
 
560
+
src/__pycache__/__init__.cpython-310.pyc DELETED
Binary file (145 Bytes)
 
src/__pycache__/__init__.cpython-312.pyc DELETED
Binary file (197 Bytes)
 
src/__pycache__/ablation.cpython-312.pyc DELETED
Binary file (21.9 kB)
 
src/__pycache__/ablation.cpython-314.pyc DELETED
Binary file (24.6 kB)
 
src/__pycache__/aspect_dict.cpython-312.pyc DELETED
Binary file (8.9 kB)
 
src/__pycache__/aspect_dict.cpython-314.pyc DELETED
Binary file (13.4 kB)
 
src/__pycache__/baselines.cpython-312.pyc DELETED
Binary file (4.43 kB)
 
src/__pycache__/config.cpython-310.pyc DELETED
Binary file (2.11 kB)
 
src/__pycache__/config.cpython-312.pyc DELETED
Binary file (2.89 kB)
 
src/__pycache__/config.cpython-314.pyc DELETED
Binary file (3.02 kB)
 
src/__pycache__/data_download.cpython-312.pyc DELETED
Binary file (13.7 kB)
 
src/__pycache__/data_download.cpython-314.pyc DELETED
Binary file (14.8 kB)
 
src/__pycache__/dataset.cpython-310.pyc DELETED
Binary file (5.66 kB)
 
src/__pycache__/dataset.cpython-312.pyc DELETED
Binary file (7.9 kB)
 
src/__pycache__/evaluator.cpython-310.pyc DELETED
Binary file (10.1 kB)
 
src/__pycache__/evaluator.cpython-312.pyc DELETED
Binary file (17.5 kB)
 
src/__pycache__/explainer.cpython-310.pyc DELETED
Binary file (13.4 kB)
 
src/__pycache__/explainer.cpython-312.pyc DELETED
Binary file (45.8 kB)
 
src/__pycache__/inference.cpython-312.pyc DELETED
Binary file (16 kB)
 
src/__pycache__/meta_encoder.cpython-310.pyc DELETED
Binary file (5.62 kB)
 
src/__pycache__/meta_encoder.cpython-312.pyc DELETED
Binary file (13.7 kB)
 
src/__pycache__/models.cpython-310.pyc DELETED
Binary file (12.1 kB)
 
src/__pycache__/models.cpython-312.pyc DELETED
Binary file (34.9 kB)
 
src/__pycache__/models.cpython-314.pyc DELETED
Binary file (38.6 kB)
 
src/__pycache__/preprocess.cpython-312.pyc DELETED
Binary file (8.43 kB)
 
src/__pycache__/trainer.cpython-310.pyc DELETED
Binary file (12.3 kB)
 
src/__pycache__/trainer.cpython-312.pyc DELETED
Binary file (22.8 kB)
 
src/__pycache__/trainer.cpython-314.pyc DELETED
Binary file (25.6 kB)
 
src/__pycache__/utils.cpython-310.pyc DELETED
Binary file (654 Bytes)
 
src/__pycache__/utils.cpython-312.pyc DELETED
Binary file (2.8 kB)
 
src/__pycache__/utils.cpython-314.pyc DELETED
Binary file (3.19 kB)
 
src/__pycache__/weak_labeling.cpython-312.pyc DELETED
Binary file (24.7 kB)
 
src/__pycache__/weak_labeling.cpython-314.pyc DELETED
Binary file (27.7 kB)