import os, json import gradio as gr from transformers import pipeline # ---------------- Model (unchanged from your demo) ---------------- MODEL_NAME = os.getenv("HF_MODEL_GENERATION", "distilgpt2") _pipe = None def _get_pipe(): global _pipe if _pipe is None: _pipe = pipeline("text-generation", model=MODEL_NAME) return _pipe def model_generate(message, max_new_tokens=128, temperature=0.8, top_p=0.95): pipe = _get_pipe() out = pipe( message, max_new_tokens=int(max_new_tokens), do_sample=True, temperature=float(temperature), top_p=float(top_p), pad_token_id=50256, ) return out[0]["generated_text"] # ---------------- Storefront knowledge (prefers your helper module) ---------------- # First try to import your module (recommended location) STORE_DATA = None USE_HELPERS = False try: from agenticcore.storefront_rules import ( load_storefront, answer_faq, get_parking_rules, get_venue_rules, search_products ) STORE_DATA = load_storefront() # loads agenticcore/storefront_data.json by default USE_HELPERS = True except Exception: # Fallback: try JSON next to this file or under agenticcore/ CANDIDATE_JSON = [ os.path.join(os.path.dirname(__file__), "storefront_data.json"), os.path.join(os.path.dirname(__file__), "agenticcore", "storefront_data.json"), ] for p in CANDIDATE_JSON: if os.path.exists(p): with open(p, "r", encoding="utf-8") as f: STORE_DATA = json.load(f) break # Defaults (used when no JSON/module found) DEFAULT_PRODUCTS = [ {"SKU": "CG-SET", "Name": "Cap & Gown Set", "Price": 59.00, "Notes": "Tassel included; ship until 10 days before event"}, {"SKU": "PK-1", "Name": "Parking Pass", "Price": 10.00, "Notes": "Multiple passes allowed per student"}, ] DEFAULT_PARKING = ["No double parking.", "Vehicles parked in handicap will be towed."] DEFAULT_VENUE = ["Formal attire recommended (not required).", "No muscle shirts.", "No sagging pants."] if STORE_DATA: # Build right-panel tables from JSON schema try: DEFAULT_PRODUCTS = [] for p in STORE_DATA.get("products", []): DEFAULT_PRODUCTS.append({ "SKU": p.get("sku",""), "Name": p.get("name",""), "Price": p.get("price_usd", ""), "Notes": (p.get("description") or "")[:120], }) pr = STORE_DATA.get("policies", {}).get("parking_rules", []) vr = STORE_DATA.get("policies", {}).get("venue_rules", []) DEFAULT_PARKING = pr or DEFAULT_PARKING DEFAULT_VENUE = vr or DEFAULT_VENUE except Exception: pass def storefront_qna(text: str) -> str | None: """Try to answer with storefront data (FAQ, rules, products).""" t = (text or "").lower().strip() if not t: return None # Use your helper functions if available (preferred) if USE_HELPERS and STORE_DATA: a = answer_faq(STORE_DATA, t) if a: return a if "parking rule" in t or ("parking" in t and "rule" in t): rules = get_parking_rules(STORE_DATA) if rules: return "Parking rules:\n- " + "\n- ".join(rules) if "venue rule" in t or "dress code" in t or "attire" in t: rules = get_venue_rules(STORE_DATA) if rules: return "Venue rules:\n- " + "\n- ".join(rules) hits = search_products(STORE_DATA, t) if hits: lines = [] for p in hits: price = p.get("price_usd") price_str = f"${price:.2f}" if isinstance(price, (int, float)) else str(price) lines.append(f"{p.get('name','Item')} — {price_str}: {p.get('description','')}") return "\n".join(lines) return None # Fallback logic (no helper module) if "parking" in t and ("more than one" in t or "multiple" in t or "extra" in t): return "Yes, multiple parking passes are allowed per student." if "parking rule" in t or ("parking" in t and "rule" in t): return "Parking rules:\n- " + "\n- ".join(DEFAULT_PARKING) if "venue rule" in t or "dress code" in t or "attire" in t: return "Venue rules:\n- " + "\n- ".join(DEFAULT_VENUE) if "cap" in t or "gown" in t: lines = [] for p in DEFAULT_PRODUCTS: price = p.get("Price") price_str = f"${price:.2f}" if isinstance(price, (int, float)) else str(price) lines.append(f"{p.get('Name','Item')} — {price_str}: {p.get('Notes','')}") return "\n".join(lines) return None def chat_pipeline(message, max_new_tokens=128, temperature=0.8, top_p=0.95): sf = storefront_qna(message) if sf: return sf return model_generate(message, max_new_tokens, temperature, top_p) # ---------------- Gradio UI (storefront look) ---------------- CUSTOM_CSS = """ :root { --bg:#0b0d12; --panel:#0f172a; --panel-2:#111827; --border:#1f2940; --text:#e5e7eb; --muted:#9ca3af; } .gradio-container { background: var(--bg) !important; color: var(--text) !important; } #header, .panel { border:1px solid var(--border); border-radius:16px; background:var(--panel); } .small { font-size:12px; color: var(--muted); } .badge { font-size:12px; opacity:.9; padding:4px 8px; border:1px solid var(--border); border-radius:999px; background: rgba(255,255,255,.04); } """ with gr.Blocks(title="Storefront Chat • Cap & Gown + Parking", css=CUSTOM_CSS) as demo: with gr.Row(elem_id="header"): gr.Markdown("## Storefront Chat • Cap & Gown + Parking") gr.Markdown("
Gradio • LLM • Storefront Q&A
", elem_classes=["small"]) with gr.Row(): with gr.Column(scale=3): with gr.Group(elem_classes=["panel"]): with gr.Row(): health_btn = gr.Button("Health", variant="secondary") caps_btn = gr.Button("Capabilities", variant="secondary") status_md = gr.Markdown("Status: not checked", elem_classes=["small"]) chatbot = gr.Chatbot(value=[], height=360, bubble_full_width=False, avatar_images=(None, None), label="Chat") with gr.Row(): msg = gr.Textbox(placeholder="Ask about cap & gown sizes, parking rules, refunds, etc…", scale=5) send = gr.Button("Send", scale=1) # Quick chips with gr.Row(): chip1 = gr.Button("Parking rules", variant="secondary") chip2 = gr.Button("Multiple passes", variant="secondary") chip3 = gr.Button("Attire", variant="secondary") chip4 = gr.Button("Sizing", variant="secondary") chip5 = gr.Button("Refunds", variant="secondary") chip6 = gr.Button("Shipping cutoff", variant="secondary") with gr.Row(): max_new = gr.Slider(32, 512, 128, 1, label="Max new tokens") with gr.Row(): temp = gr.Slider(0.1, 1.5, 0.8, 0.05, label="Temperature") with gr.Row(): topp = gr.Slider(0.1, 1.0, 0.95, 0.05, label="Top-p") with gr.Column(scale=2): with gr.Group(elem_classes=["panel"]): gr.Markdown("### Products") cols = list(DEFAULT_PRODUCTS[0].keys()) if DEFAULT_PRODUCTS else ["SKU","Name","Price","Notes"] data = [[p.get(c,"") for c in cols] for p in DEFAULT_PRODUCTS] products_tbl = gr.Dataframe(headers=cols, value=data, interactive=False, wrap=True) with gr.Group(elem_classes=["panel"]): gr.Markdown("### Rules (Venue & Parking)") rules_md = gr.Markdown("- " + "\n- ".join(DEFAULT_VENUE + DEFAULT_PARKING)) with gr.Group(elem_classes=["panel"]): gr.Markdown("### Logistics") gr.Markdown( "- Shipping available until 10 days before event (typ. 3–5 business days)\n" "- Pickup: Student Center Bookstore during week prior to event\n" "- Graduates arrive 90 minutes early; guests 60 minutes early\n" "- Lots A & B open 2 hours before; overflow as needed\n" "\n*Try: “What time should I arrive?” or “Where do I pick up the gown?”*" ) # ---- wiring ---- def on_send(history, message, max_new_tokens, temperature, top_p): message = (message or "").strip() if not message: return history, "" history = history + [[message, None]] reply = chat_pipeline(message, max_new_tokens, temperature, top_p) history[-1][1] = reply return history, "" send.click(on_send, [chatbot, msg, max_new, temp, topp], [chatbot, msg]) msg.submit(on_send, [chatbot, msg, max_new, temp, topp], [chatbot, msg]) # Quick chips → prefill chip1.click(lambda: "What are the parking rules?", outputs=msg) chip2.click(lambda: "Can I buy multiple parking passes?", outputs=msg) chip3.click(lambda: "Is formal attire required?", outputs=msg) chip4.click(lambda: "How do I pick a cap & gown size?", outputs=msg) chip5.click(lambda: "What is the refund policy?", outputs=msg) chip6.click(lambda: "When is the shipping cutoff for cap & gown?", outputs=msg) # Health / capabilities def ui_health_check(): return (f"### Status: ✅ Healthy\n- Model: `{MODEL_NAME}`\n" f"- Storefront module: {'yes' if USE_HELPERS else 'no'}\n" f"- Storefront JSON: {'loaded' if bool(STORE_DATA) else 'not found'}") def ui_capabilities(): caps = [ "Chat (LLM – text-generation)", "Quick storefront Q&A (parking rules, attire, products)", "Adjustable: max_new_tokens, temperature, top-p", ] return "### Capabilities\n- " + "\n- ".join(caps) def _health(): return ui_health_check(), "Status: ✅ Healthy" health_btn.click(_health, outputs=[chatbot, status_md]) caps_btn.click(ui_capabilities, outputs=chatbot) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))