| |
| import json, os |
|
|
| def clean_generation(text: str) -> str: |
| s = (text or "").strip() |
|
|
| |
| last = s.rfind("Assistant:") |
| if last != -1: |
| s = s[last + len("Assistant:"):].strip() |
|
|
| |
| cut_marks = ["\nUser:", "\nSystem:", "\n###", "\nProducts:", "\nVenue rules:", "\nParking rules:"] |
| cuts = [s.find(m) for m in cut_marks if s.find(m) != -1] |
| if cuts: |
| s = s[:min(cuts)].strip() |
|
|
| |
| s = re.sub(r"(?:\b([A-Z][a-zA-Z0-9_/.-]{2,})\b(?:\s*/\s*\1\b)+)", r"\1", s) |
|
|
| |
| dedup = [] |
| for ln in s.splitlines(): |
| if not dedup or ln.strip() != dedup[-1].strip(): |
| dedup.append(ln) |
| return "\n".join(dedup).strip() |
|
|
| HELP_KEYWORDS = { |
| "help", "assist", "assistance", "tips", "how do i", "what can you do", |
| "graduation help", "help me with graduation", "can you help me with graduation" |
| } |
|
|
| STORE_KEYWORDS = { |
| "cap", "gown", "parking", "pass", "passes", "attire", "dress", |
| "venue", "logistics", "shipping", "pickup", "lot", "lots", "arrival", "size", "sizing" |
| } |
|
|
| def is_storefront_query(text: str) -> bool: |
| t = (text or "").lower() |
| return any(k in t for k in STORE_KEYWORDS) or any(k in t for k in HELP_KEYWORDS) |
|
|
| def _get_lots_open_hours(data) -> int: |
| try: |
| return int(((data or {}).get("logistics") or {}).get("lots_open_hours_before") or 2) |
| except Exception: |
| return 2 |
|
|
| |
| def storefront_qna(data, user_text: str) -> str | None: |
| """ |
| Deterministic storefront answers first: |
| - single-word intents (parking / wear / passes) |
| - help/capability prompt |
| - FAQ (if you have answer_faq) |
| - explicit rules queries |
| - 'lots open' timing |
| - compact products list |
| Returns None to allow LLM fallback in your chat pipeline. |
| """ |
| if not user_text: |
| return None |
| t = user_text.strip().lower() |
|
|
| |
| if t in {"parking"}: |
| _, pr = get_rules(data) |
| if pr: |
| return "Parking rules:\n- " + "\n- ".join(pr) |
|
|
| |
| if t in {"venue", "attire", "dress", "dress code", "wear"} or "what should i wear" in t: |
| vr, _ = get_rules(data) |
| if vr: |
| return "Venue rules:\n- " + "\n- ".join(vr) |
|
|
| |
| if t in {"passes", "parking pass", "parking passes"}: |
| return "Yes, multiple parking passes are allowed per student." |
|
|
| |
| if any(k in t for k in HELP_KEYWORDS): |
| return ( |
| "I can help with the graduation storefront. Try:\n" |
| "- “What are the parking rules?”\n" |
| "- “Can I buy multiple parking passes?”\n" |
| "- “Is formal attire required?”\n" |
| "- “Where do I pick up the gown?”\n" |
| "- “When do lots open?”" |
| ) |
|
|
| |
| try: |
| a = answer_faq(data, t) |
| if a: |
| return a |
| except Exception: |
| pass |
|
|
| |
| if "parking" in t and "rule" in t: |
| _, pr = get_rules(data) |
| if pr: |
| return "Parking rules:\n- " + "\n- ".join(pr) |
|
|
| if ("venue" in t and "rule" in t) or "attire" in t or "dress code" in t: |
| vr, _ = get_rules(data) |
| if vr: |
| return "Venue rules:\n- " + "\n- ".join(vr) |
|
|
| |
| if "parking" in t and ("hours" in t or "time" in t or "open" in t): |
| lots_open = _get_lots_open_hours(data) |
| return f"Parking lots open {lots_open} hours before the ceremony." |
|
|
| |
| if any(k in t for k in ("cap", "gown", "parking pass", "product", "item", "price")): |
| prods = extract_products(data) |
| if prods: |
| lines = [] |
| for p in prods: |
| name = p.get("name", "Item") |
| price = p.get("price", p.get("price_usd", "")) |
| notes = p.get("notes", p.get("description", "")) |
| price_str = f"${price:.2f}" if isinstance(price, (int, float)) else str(price) |
| lines.append(f"{name} — {price_str}: {notes}") |
| return "\n".join(lines) |
|
|
| |
| return None |
|
|
| def _find_json(): |
| candidates = [ |
| os.path.join(os.getcwd(), "storefront_data.json"), |
| os.path.join(os.getcwd(), "agenticcore", "storefront_data.json"), |
| ] |
| for p in candidates: |
| if os.path.exists(p): |
| return p |
| return None |
|
|
| def load_storefront(): |
| p = _find_json() |
| if not p: |
| return None |
| with open(p, "r", encoding="utf-8") as f: |
| return json.load(f) |
|
|
| def _string_in_any(s, variants): |
| s = s.lower() |
| return any(v in s for v in variants) |
|
|
| def answer_faq(data, text: str): |
| """Very small FAQ matcher by substring; safe if faq[] missing.""" |
| faq = (data or {}).get("faq") or [] |
| t = text.lower() |
| for item in faq: |
| qs = item.get("q") or [] |
| if any(q.lower() in t for q in qs): |
| return item.get("a") |
| return None |
|
|
| def extract_products(data): |
| prods = [] |
| for p in (data or {}).get("products", []): |
| prods.append({ |
| "sku": p.get("sku",""), |
| "name": p.get("name",""), |
| "price": p.get("price_usd",""), |
| "notes": (p.get("description") or "")[:140], |
| }) |
| return prods |
|
|
| def get_rules(data): |
| pol = (data or {}).get("policies", {}) or {} |
| return pol.get("venue_rules", []), pol.get("parking_rules", []) |
|
|