pixel3user commited on
Commit
103cddb
·
1 Parent(s): 7a99397

some changes

Browse files
Files changed (1) hide show
  1. app.py +97 -12
app.py CHANGED
@@ -119,6 +119,94 @@ def _parse_recommendation_json(raw: str):
119
  except Exception:
120
  return None
121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  # ---- Inference on GPU (ZeroGPU pattern) ----
123
  @spaces.GPU(duration=120)
124
  def generate_answer(image, question, temperature=0.7, top_p=0.95, max_tokens=256):
@@ -183,7 +271,10 @@ def pet_answer_with_recs(image, question, temperature=0.7, top_p=0.95, max_token
183
 
184
  # Step 2: retrieve product candidates (humans/skincare; model will decide relevance)
185
  cands = product_search(question, k=8)
186
- cand_block = format_candidates_for_llm(cands, budget_twd=budget_twd)
 
 
 
187
 
188
  # Step 3: build a small, text-only prompt for suggestions
189
  # IMPORTANT: we use the same Qwen2.5-VL model in text mode
@@ -192,22 +283,16 @@ def pet_answer_with_recs(image, question, temperature=0.7, top_p=0.95, max_token
192
  "content": [
193
  {"type": "text", "text":
194
  "You are DermalCare's assistant.\n"
195
- "You will receive (1) a user question and (2) a JSON array named candidate_products containing skincare products.\n"
196
- "If one or more products genuinely help the user, respond with: \n"
197
- "### Suggested Products\n"
198
- "1. **Product name**\n- **Why it helps:** ...\n- **How to use:** ...\n"
199
- "(repeat up to 3 items)\n"
200
- "\n<DERMACARE_PRODUCTS_JSON>{\"version\": 1, \"products\": [...]}</DERMACARE_PRODUCTS_JSON>\n"
201
- "The JSON must list exactly the recommended products with keys: id, brand, name, category, price_value, price_currency, why, how, url, image_url.\n"
202
- "Use values from candidate_products: use brand_en/brand_zh to compose 'brand', prefer English names but fall back to Chinese when absent.\n"
203
- "If a field is missing, set it to null. Do NOT invent ids or products outside the provided list.\n"
204
- "If no products are relevant, output exactly 'No relevant products.' with nothing else."}
205
  ]
206
  },{
207
  "role": "user",
208
  "content": [
209
  {"type": "text", "text": f"User message:\n{question}"},
210
- {"type": "text", "text": f"candidate_products = {cand_block}"}
211
  ]
212
  }]
213
 
 
119
  except Exception:
120
  return None
121
 
122
+
123
+ def _build_recommendation_sections(rec_data, candidate_lookup):
124
+ if not rec_data:
125
+ return None, None
126
+
127
+ recommend_flag = rec_data.get("recommend")
128
+ if isinstance(recommend_flag, str):
129
+ recommend_flag = recommend_flag.strip().lower() in {"yes", "true", "1"}
130
+ elif isinstance(recommend_flag, (int, float)):
131
+ recommend_flag = bool(recommend_flag)
132
+
133
+ if not recommend_flag:
134
+ return None, None
135
+
136
+ recommendations = rec_data.get("recommendations", [])
137
+ if not isinstance(recommendations, list):
138
+ return None, None
139
+
140
+ lines = ["### Suggested Products", ""]
141
+ products_payload = []
142
+
143
+ for idx, item in enumerate(recommendations[:3], start=1):
144
+ if not isinstance(item, dict):
145
+ continue
146
+ raw_id = item.get("id")
147
+ if raw_id is None:
148
+ continue
149
+ pid = str(raw_id).strip()
150
+ if not pid:
151
+ continue
152
+
153
+ candidate = candidate_lookup.get(pid, {})
154
+
155
+ brand = (
156
+ candidate.get("brand_en")
157
+ or candidate.get("brand_zh")
158
+ or item.get("brand")
159
+ or ""
160
+ )
161
+ name = (
162
+ candidate.get("product_name_en")
163
+ or candidate.get("product_name_zh")
164
+ or item.get("name")
165
+ or f"Product {idx}"
166
+ )
167
+ category = (
168
+ candidate.get("category_en")
169
+ or candidate.get("category_zh")
170
+ or item.get("category")
171
+ or None
172
+ )
173
+ price_value = candidate.get("price_value")
174
+ price_currency = candidate.get("price_currency")
175
+ why = item.get("why") or "Supports the user’s concern."
176
+ how = item.get("how") or "Use as directed on the product label."
177
+ url = candidate.get("source_url") or item.get("url")
178
+ image_url = candidate.get("image_url") or item.get("image_url")
179
+
180
+ lines.extend([
181
+ f"{idx}. **{name}**",
182
+ f"- **Why it helps:** {why}",
183
+ f"- **How to use:** {how}",
184
+ "",
185
+ ])
186
+
187
+ products_payload.append({
188
+ "id": pid,
189
+ "brand": brand,
190
+ "name": name,
191
+ "category": category,
192
+ "price_value": price_value,
193
+ "price_currency": price_currency,
194
+ "why": why,
195
+ "how": how,
196
+ "url": url,
197
+ "image_url": image_url,
198
+ })
199
+
200
+ if not products_payload:
201
+ return None, None
202
+
203
+ suggestion_text = "\n".join(lines).strip()
204
+ product_json_payload = json.dumps(
205
+ {"version": 1, "products": products_payload},
206
+ ensure_ascii=False,
207
+ )
208
+ return suggestion_text, product_json_payload
209
+
210
  # ---- Inference on GPU (ZeroGPU pattern) ----
211
  @spaces.GPU(duration=120)
212
  def generate_answer(image, question, temperature=0.7, top_p=0.95, max_tokens=256):
 
271
 
272
  # Step 2: retrieve product candidates (humans/skincare; model will decide relevance)
273
  cands = product_search(question, k=8)
274
+ cand_block_json, cand_list = format_candidates_for_llm(cands, budget_twd=budget_twd)
275
+ candidate_lookup = {
276
+ str(c.get("id")).strip(): c for c in cand_list if c.get("id") is not None
277
+ }
278
 
279
  # Step 3: build a small, text-only prompt for suggestions
280
  # IMPORTANT: we use the same Qwen2.5-VL model in text mode
 
283
  "content": [
284
  {"type": "text", "text":
285
  "You are DermalCare's assistant.\n"
286
+ "Respond ONLY with valid JSON (no markdown, no explanations).\n"
287
+ "Expected schema: {\"recommend\": bool, \"recommendations\": [ {\"id\": str, \"why\": str, \"how\": str } ], \"notes\": str }.\n"
288
+ "Use candidate_products as the exclusive source of items. If a product is recommended, its id must exist in candidate_products.\n"
289
+ "If no products are relevant, return {\"recommend\": false, \"recommendations\": [], \"notes\": \"No relevant products.\"}."}
 
 
 
 
 
 
290
  ]
291
  },{
292
  "role": "user",
293
  "content": [
294
  {"type": "text", "text": f"User message:\n{question}"},
295
+ {"type": "text", "text": f"candidate_products = {cand_block_json}"}
296
  ]
297
  }]
298