Spaces:
Sleeping
Sleeping
Commit ·
1f017cb
1
Parent(s): 029de51
Refresh catalog and harden consumer inference
Browse files- app.py +11 -6
- data/product_catalog.json +0 -0
- src/inference.py +15 -3
app.py
CHANGED
|
@@ -477,7 +477,7 @@ def consumer_product_view(product_name: str, selected_aspect: str) -> Tuple[str,
|
|
| 477 |
store = product.get("store") or "Amazon"
|
| 478 |
asin = product.get("parent_asin") or "-"
|
| 479 |
detail = (
|
| 480 |
-
f'<div class="product-card product-layout">{image_html}<div>'
|
| 481 |
f'<h3>{_esc(product["name"])}</h3>'
|
| 482 |
f'<p class="muted">{_esc(product["categories"])}</p>'
|
| 483 |
f'<div class="meta-pills"><span>Store: {_esc(store)}</span>'
|
|
@@ -486,8 +486,9 @@ def consumer_product_view(product_name: str, selected_aspect: str) -> Tuple[str,
|
|
| 486 |
f'<span>Price: ${_safe_float(product["price"]):.2f}</span>'
|
| 487 |
f'<span>Rating: {_safe_float(product["average_rating"]):.1f}</span>'
|
| 488 |
f'<span>Reviews: {int(_safe_float(product["rating_number"]))}</span></div>'
|
| 489 |
-
f'<p class="metadata-summary"><b>Metadata:</b> {_esc(_short_text(product["features"],
|
| 490 |
-
f'
|
|
|
|
| 491 |
)
|
| 492 |
evidence = f'<h4>Key review evidence</h4>{_highlight_review(product["review"], selected_aspect)}'
|
| 493 |
return detail, _aspect_cards(result, product["review"], selected_aspect), evidence, _aspect_rows(result, product["review"])
|
|
@@ -706,11 +707,15 @@ button.primary, .gradio-button.primary { background:var(--accent) !important; bo
|
|
| 706 |
.status.ok { background:#ecfdf5; border-color:#86efac; color:#065f46; }
|
| 707 |
.status.bad { background:#fff1f2; border-color:#fda4af; color:#991b1b; }
|
| 708 |
.product-card, .overall-card, .note-card { border:1px solid var(--line); border-radius:8px; padding:16px; background:white; }
|
| 709 |
-
.product-layout { display:grid; grid-template-columns:
|
| 710 |
-
.product-img { width:
|
| 711 |
.product-card h3 { margin:0 0 6px; font-size:18px; line-height:1.25; }
|
| 712 |
.product-card p { margin:7px 0; }
|
| 713 |
.metadata-summary { color:#334155; font-size:13px; line-height:1.45; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 714 |
.recommendation-card { border:1px solid #fed7aa; border-left:5px solid var(--accent); background:#fff7ed; border-radius:8px; padding:13px 14px; margin:12px 0; }
|
| 715 |
.recommendation-card p { margin:7px 0 0; }
|
| 716 |
.muted { color:var(--muted); }
|
|
@@ -743,7 +748,7 @@ mark { background:#fde68a; color:#111827; border-radius:4px; padding:1px 3px; }
|
|
| 743 |
.kv-table td { border-bottom:1px solid #e2e8f0; padding:9px 12px; }
|
| 744 |
.kv-table td:first-child { width:220px; color:#334155; font-weight:700; background:#f8fafc; }
|
| 745 |
.compact-note { color:#475569; font-size:13px; margin:4px 0 10px; }
|
| 746 |
-
@media (max-width:860px) { .aspect-grid, .metric-grid, .product-layout { grid-template-columns:1fr; } .product-img { width:100%; height:180px; } }
|
| 747 |
"""
|
| 748 |
|
| 749 |
|
|
|
|
| 477 |
store = product.get("store") or "Amazon"
|
| 478 |
asin = product.get("parent_asin") or "-"
|
| 479 |
detail = (
|
| 480 |
+
f'<div class="product-card"><div class="product-layout">{image_html}<div>'
|
| 481 |
f'<h3>{_esc(product["name"])}</h3>'
|
| 482 |
f'<p class="muted">{_esc(product["categories"])}</p>'
|
| 483 |
f'<div class="meta-pills"><span>Store: {_esc(store)}</span>'
|
|
|
|
| 486 |
f'<span>Price: ${_safe_float(product["price"]):.2f}</span>'
|
| 487 |
f'<span>Rating: {_safe_float(product["average_rating"]):.1f}</span>'
|
| 488 |
f'<span>Reviews: {int(_safe_float(product["rating_number"]))}</span></div>'
|
| 489 |
+
f'<p class="metadata-summary"><b>Metadata:</b> {_esc(_short_text(product["features"], 130))}</p>'
|
| 490 |
+
f'</div></div><div class="decision-layout">{_overall_html(result)}'
|
| 491 |
+
f'{_recommendation_html(result, product["name"])}</div></div>'
|
| 492 |
)
|
| 493 |
evidence = f'<h4>Key review evidence</h4>{_highlight_review(product["review"], selected_aspect)}'
|
| 494 |
return detail, _aspect_cards(result, product["review"], selected_aspect), evidence, _aspect_rows(result, product["review"])
|
|
|
|
| 707 |
.status.ok { background:#ecfdf5; border-color:#86efac; color:#065f46; }
|
| 708 |
.status.bad { background:#fff1f2; border-color:#fda4af; color:#991b1b; }
|
| 709 |
.product-card, .overall-card, .note-card { border:1px solid var(--line); border-radius:8px; padding:16px; background:white; }
|
| 710 |
+
.product-layout { display:grid; grid-template-columns:96px 1fr; gap:12px; align-items:start; }
|
| 711 |
+
.product-img { width:96px; height:118px; object-fit:cover; border-radius:8px; border:1px solid var(--line); background:#f8fafc; }
|
| 712 |
.product-card h3 { margin:0 0 6px; font-size:18px; line-height:1.25; }
|
| 713 |
.product-card p { margin:7px 0; }
|
| 714 |
.metadata-summary { color:#334155; font-size:13px; line-height:1.45; }
|
| 715 |
+
.decision-layout { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:10px; align-items:stretch; }
|
| 716 |
+
.decision-layout .overall-card, .decision-layout .recommendation-card { margin:0; padding:12px; }
|
| 717 |
+
.decision-layout .recommendation-card .small-label { display:none; }
|
| 718 |
+
.decision-layout .recommendation-card p { display:none; }
|
| 719 |
.recommendation-card { border:1px solid #fed7aa; border-left:5px solid var(--accent); background:#fff7ed; border-radius:8px; padding:13px 14px; margin:12px 0; }
|
| 720 |
.recommendation-card p { margin:7px 0 0; }
|
| 721 |
.muted { color:var(--muted); }
|
|
|
|
| 748 |
.kv-table td { border-bottom:1px solid #e2e8f0; padding:9px 12px; }
|
| 749 |
.kv-table td:first-child { width:220px; color:#334155; font-weight:700; background:#f8fafc; }
|
| 750 |
.compact-note { color:#475569; font-size:13px; margin:4px 0 10px; }
|
| 751 |
+
@media (max-width:860px) { .aspect-grid, .metric-grid, .product-layout, .decision-layout { grid-template-columns:1fr; } .product-img { width:100%; height:180px; } }
|
| 752 |
"""
|
| 753 |
|
| 754 |
|
data/product_catalog.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
src/inference.py
CHANGED
|
@@ -174,6 +174,18 @@ class AspectPredictor:
|
|
| 174 |
checkpoint_dir=checkpoint_dir, device=device,
|
| 175 |
)
|
| 176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
def predict(self, review_text: str, product_meta: Dict,
|
| 178 |
return_attention: bool = True) -> Dict:
|
| 179 |
enc = self.tokenizer(
|
|
@@ -183,7 +195,7 @@ class AspectPredictor:
|
|
| 183 |
padding="max_length",
|
| 184 |
return_tensors="pt",
|
| 185 |
)
|
| 186 |
-
input_ids = enc["input_ids"].to(self.device)
|
| 187 |
attn_mask = enc["attention_mask"].to(self.device)
|
| 188 |
|
| 189 |
meta_df = _meta_dict_to_df(product_meta)
|
|
@@ -227,7 +239,7 @@ class AspectPredictor:
|
|
| 227 |
padding="max_length",
|
| 228 |
return_tensors="pt",
|
| 229 |
)
|
| 230 |
-
input_ids = enc["input_ids"].to(self.device)
|
| 231 |
attn_mask = enc["attention_mask"].to(self.device)
|
| 232 |
|
| 233 |
meta_df = pd.concat(
|
|
@@ -261,4 +273,4 @@ class AspectPredictor:
|
|
| 261 |
item.update(attn_payload)
|
| 262 |
item.update(_attention_insights(attn_payload))
|
| 263 |
results.append(item)
|
| 264 |
-
return results
|
|
|
|
| 174 |
checkpoint_dir=checkpoint_dir, device=device,
|
| 175 |
)
|
| 176 |
|
| 177 |
+
def _prepare_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
|
| 178 |
+
bert = getattr(self.model, "bert", None)
|
| 179 |
+
embeddings = getattr(bert, "embeddings", None)
|
| 180 |
+
word_embeddings = getattr(embeddings, "word_embeddings", None)
|
| 181 |
+
if word_embeddings is None:
|
| 182 |
+
return input_ids
|
| 183 |
+
vocab_size = int(word_embeddings.num_embeddings)
|
| 184 |
+
if vocab_size <= 0:
|
| 185 |
+
return input_ids
|
| 186 |
+
unk_id = int(getattr(self.tokenizer, "unk_token_id", 0) or 0)
|
| 187 |
+
return torch.where(input_ids >= vocab_size, torch.full_like(input_ids, unk_id), input_ids)
|
| 188 |
+
|
| 189 |
def predict(self, review_text: str, product_meta: Dict,
|
| 190 |
return_attention: bool = True) -> Dict:
|
| 191 |
enc = self.tokenizer(
|
|
|
|
| 195 |
padding="max_length",
|
| 196 |
return_tensors="pt",
|
| 197 |
)
|
| 198 |
+
input_ids = self._prepare_input_ids(enc["input_ids"]).to(self.device)
|
| 199 |
attn_mask = enc["attention_mask"].to(self.device)
|
| 200 |
|
| 201 |
meta_df = _meta_dict_to_df(product_meta)
|
|
|
|
| 239 |
padding="max_length",
|
| 240 |
return_tensors="pt",
|
| 241 |
)
|
| 242 |
+
input_ids = self._prepare_input_ids(enc["input_ids"]).to(self.device)
|
| 243 |
attn_mask = enc["attention_mask"].to(self.device)
|
| 244 |
|
| 245 |
meta_df = pd.concat(
|
|
|
|
| 273 |
item.update(attn_payload)
|
| 274 |
item.update(_attention_insights(attn_payload))
|
| 275 |
results.append(item)
|
| 276 |
+
return results
|