JerameeUC
Works just fine tuning.
a48e2a2
# core/storefront.py
import json, os
def clean_generation(text: str) -> str:
s = (text or "").strip()
# Keep only text after the last "Assistant:"
last = s.rfind("Assistant:")
if last != -1:
s = s[last + len("Assistant:"):].strip()
# Cut at the first sign of a new turn or meta
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()
# Remove egregious token loops like "Account/Account/..."
s = re.sub(r"(?:\b([A-Z][a-zA-Z0-9_/.-]{2,})\b(?:\s*/\s*\1\b)+)", r"\1", s)
# Collapse consecutive duplicate lines
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
# Main router (drop-in)
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()
# 1) Single-word / exact intents to avoid LLM hallucinations
if t in {"parking"}:
_, pr = get_rules(data)
if pr:
return "Parking rules:\n- " + "\n- ".join(pr)
# Map 'wear/attire' variants directly to venue rules
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)
# Parking passes (multiple allowed)
if t in {"passes", "parking pass", "parking passes"}:
return "Yes, multiple parking passes are allowed per student."
# 2) Help / capability intent → deterministic guidance
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?”"
)
# 3) JSON-driven FAQ (if available)
try:
a = answer_faq(data, t)
if a:
return a
except Exception:
pass # answer_faq may not exist or data may be None
# 4) Explicit rules phrasing (keeps answers tight and consistent)
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)
# 5) “When do lots open?” / hours / time
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."
# 6) Product info (cap/gown/parking pass)
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)
# No deterministic match → let the caller fall back to the LLM
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", [])