Lavender825 commited on
Commit
1f017cb
·
1 Parent(s): 029de51

Refresh catalog and harden consumer inference

Browse files
Files changed (3) hide show
  1. app.py +11 -6
  2. data/product_catalog.json +0 -0
  3. 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"], 190))}</p>'
490
- f'{_recommendation_html(result, product["name"])}{_overall_html(result)}</div></div>'
 
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:104px 1fr; gap:14px; align-items:start; }
710
- .product-img { width:104px; height:132px; object-fit:cover; border-radius:8px; border:1px solid var(--line); background:#f8fafc; }
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