JerameeUC commited on
Commit
6f00bfe
·
1 Parent(s): 9509b5b

Added Storefront

Browse files
Files changed (1) hide show
  1. storefront_app.py +231 -0
storefront_app.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, json
2
+ import gradio as gr
3
+ from transformers import pipeline
4
+
5
+ # ---------------- Model (unchanged from your demo) ----------------
6
+ MODEL_NAME = os.getenv("HF_MODEL_GENERATION", "distilgpt2")
7
+ _pipe = None
8
+ def _get_pipe():
9
+ global _pipe
10
+ if _pipe is None:
11
+ _pipe = pipeline("text-generation", model=MODEL_NAME)
12
+ return _pipe
13
+
14
+ def model_generate(message, max_new_tokens=128, temperature=0.8, top_p=0.95):
15
+ pipe = _get_pipe()
16
+ out = pipe(
17
+ message,
18
+ max_new_tokens=int(max_new_tokens),
19
+ do_sample=True,
20
+ temperature=float(temperature),
21
+ top_p=float(top_p),
22
+ pad_token_id=50256,
23
+ )
24
+ return out[0]["generated_text"]
25
+
26
+ # ---------------- Storefront knowledge (prefers your helper module) ----------------
27
+ # First try to import your module (recommended location)
28
+ STORE_DATA = None
29
+ USE_HELPERS = False
30
+ try:
31
+ from agenticcore.storefront_rules import (
32
+ load_storefront, answer_faq, get_parking_rules, get_venue_rules, search_products
33
+ )
34
+ STORE_DATA = load_storefront() # loads agenticcore/storefront_data.json by default
35
+ USE_HELPERS = True
36
+ except Exception:
37
+ # Fallback: try JSON next to this file or under agenticcore/
38
+ CANDIDATE_JSON = [
39
+ os.path.join(os.path.dirname(__file__), "storefront_data.json"),
40
+ os.path.join(os.path.dirname(__file__), "agenticcore", "storefront_data.json"),
41
+ ]
42
+ for p in CANDIDATE_JSON:
43
+ if os.path.exists(p):
44
+ with open(p, "r", encoding="utf-8") as f:
45
+ STORE_DATA = json.load(f)
46
+ break
47
+
48
+ # Defaults (used when no JSON/module found)
49
+ DEFAULT_PRODUCTS = [
50
+ {"SKU": "CG-SET", "Name": "Cap & Gown Set", "Price": 59.00, "Notes": "Tassel included; ship until 10 days before event"},
51
+ {"SKU": "PK-1", "Name": "Parking Pass", "Price": 10.00, "Notes": "Multiple passes allowed per student"},
52
+ ]
53
+ DEFAULT_PARKING = ["No double parking.", "Vehicles parked in handicap will be towed."]
54
+ DEFAULT_VENUE = ["Formal attire recommended (not required).", "No muscle shirts.", "No sagging pants."]
55
+
56
+ if STORE_DATA:
57
+ # Build right-panel tables from JSON schema
58
+ try:
59
+ DEFAULT_PRODUCTS = []
60
+ for p in STORE_DATA.get("products", []):
61
+ DEFAULT_PRODUCTS.append({
62
+ "SKU": p.get("sku",""),
63
+ "Name": p.get("name",""),
64
+ "Price": p.get("price_usd", ""),
65
+ "Notes": (p.get("description") or "")[:120],
66
+ })
67
+ pr = STORE_DATA.get("policies", {}).get("parking_rules", [])
68
+ vr = STORE_DATA.get("policies", {}).get("venue_rules", [])
69
+ DEFAULT_PARKING = pr or DEFAULT_PARKING
70
+ DEFAULT_VENUE = vr or DEFAULT_VENUE
71
+ except Exception:
72
+ pass
73
+
74
+ def storefront_qna(text: str) -> str | None:
75
+ """Try to answer with storefront data (FAQ, rules, products)."""
76
+ t = (text or "").lower().strip()
77
+ if not t:
78
+ return None
79
+
80
+ # Use your helper functions if available (preferred)
81
+ if USE_HELPERS and STORE_DATA:
82
+ a = answer_faq(STORE_DATA, t)
83
+ if a:
84
+ return a
85
+ if "parking rule" in t or ("parking" in t and "rule" in t):
86
+ rules = get_parking_rules(STORE_DATA)
87
+ if rules:
88
+ return "Parking rules:\n- " + "\n- ".join(rules)
89
+ if "venue rule" in t or "dress code" in t or "attire" in t:
90
+ rules = get_venue_rules(STORE_DATA)
91
+ if rules:
92
+ return "Venue rules:\n- " + "\n- ".join(rules)
93
+ hits = search_products(STORE_DATA, t)
94
+ if hits:
95
+ lines = []
96
+ for p in hits:
97
+ price = p.get("price_usd")
98
+ price_str = f"${price:.2f}" if isinstance(price, (int, float)) else str(price)
99
+ lines.append(f"{p.get('name','Item')} — {price_str}: {p.get('description','')}")
100
+ return "\n".join(lines)
101
+ return None
102
+
103
+ # Fallback logic (no helper module)
104
+ if "parking" in t and ("more than one" in t or "multiple" in t or "extra" in t):
105
+ return "Yes, multiple parking passes are allowed per student."
106
+ if "parking rule" in t or ("parking" in t and "rule" in t):
107
+ return "Parking rules:\n- " + "\n- ".join(DEFAULT_PARKING)
108
+ if "venue rule" in t or "dress code" in t or "attire" in t:
109
+ return "Venue rules:\n- " + "\n- ".join(DEFAULT_VENUE)
110
+ if "cap" in t or "gown" in t:
111
+ lines = []
112
+ for p in DEFAULT_PRODUCTS:
113
+ price = p.get("Price")
114
+ price_str = f"${price:.2f}" if isinstance(price, (int, float)) else str(price)
115
+ lines.append(f"{p.get('Name','Item')} — {price_str}: {p.get('Notes','')}")
116
+ return "\n".join(lines)
117
+ return None
118
+
119
+ def chat_pipeline(message, max_new_tokens=128, temperature=0.8, top_p=0.95):
120
+ sf = storefront_qna(message)
121
+ if sf:
122
+ return sf
123
+ return model_generate(message, max_new_tokens, temperature, top_p)
124
+
125
+ # ---------------- Gradio UI (storefront look) ----------------
126
+ CUSTOM_CSS = """
127
+ :root { --bg:#0b0d12; --panel:#0f172a; --panel-2:#111827; --border:#1f2940; --text:#e5e7eb; --muted:#9ca3af; }
128
+ .gradio-container { background: var(--bg) !important; color: var(--text) !important; }
129
+ #header, .panel { border:1px solid var(--border); border-radius:16px; background:var(--panel); }
130
+ .small { font-size:12px; color: var(--muted); }
131
+ .badge { font-size:12px; opacity:.9; padding:4px 8px; border:1px solid var(--border); border-radius:999px; background: rgba(255,255,255,.04); }
132
+ """
133
+
134
+ with gr.Blocks(title="Storefront Chat • Cap & Gown + Parking", css=CUSTOM_CSS) as demo:
135
+ with gr.Row(elem_id="header"):
136
+ gr.Markdown("## Storefront Chat • Cap & Gown + Parking")
137
+ gr.Markdown("<div class='badge'>Gradio • LLM • Storefront Q&A</div>", elem_classes=["small"])
138
+
139
+ with gr.Row():
140
+ with gr.Column(scale=3):
141
+ with gr.Group(elem_classes=["panel"]):
142
+ with gr.Row():
143
+ health_btn = gr.Button("Health", variant="secondary")
144
+ caps_btn = gr.Button("Capabilities", variant="secondary")
145
+ status_md = gr.Markdown("Status: not checked", elem_classes=["small"])
146
+
147
+ chatbot = gr.Chatbot(value=[], height=360, bubble_full_width=False, avatar_images=(None, None), label="Chat")
148
+
149
+ with gr.Row():
150
+ msg = gr.Textbox(placeholder="Ask about cap & gown sizes, parking rules, refunds, etc…", scale=5)
151
+ send = gr.Button("Send", scale=1)
152
+
153
+ # Quick chips
154
+ with gr.Row():
155
+ chip1 = gr.Button("Parking rules", variant="secondary")
156
+ chip2 = gr.Button("Multiple passes", variant="secondary")
157
+ chip3 = gr.Button("Attire", variant="secondary")
158
+ chip4 = gr.Button("Sizing", variant="secondary")
159
+ chip5 = gr.Button("Refunds", variant="secondary")
160
+ chip6 = gr.Button("Shipping cutoff", variant="secondary")
161
+
162
+ with gr.Row():
163
+ max_new = gr.Slider(32, 512, 128, 1, label="Max new tokens")
164
+ with gr.Row():
165
+ temp = gr.Slider(0.1, 1.5, 0.8, 0.05, label="Temperature")
166
+ with gr.Row():
167
+ topp = gr.Slider(0.1, 1.0, 0.95, 0.05, label="Top-p")
168
+
169
+ with gr.Column(scale=2):
170
+ with gr.Group(elem_classes=["panel"]):
171
+ gr.Markdown("### Products")
172
+ cols = list(DEFAULT_PRODUCTS[0].keys()) if DEFAULT_PRODUCTS else ["SKU","Name","Price","Notes"]
173
+ data = [[p.get(c,"") for c in cols] for p in DEFAULT_PRODUCTS]
174
+ products_tbl = gr.Dataframe(headers=cols, value=data, interactive=False, wrap=True)
175
+
176
+ with gr.Group(elem_classes=["panel"]):
177
+ gr.Markdown("### Rules (Venue & Parking)")
178
+ rules_md = gr.Markdown("- " + "\n- ".join(DEFAULT_VENUE + DEFAULT_PARKING))
179
+
180
+ with gr.Group(elem_classes=["panel"]):
181
+ gr.Markdown("### Logistics")
182
+ gr.Markdown(
183
+ "- Shipping available until 10 days before event (typ. 3–5 business days)\n"
184
+ "- Pickup: Student Center Bookstore during week prior to event\n"
185
+ "- Graduates arrive 90 minutes early; guests 60 minutes early\n"
186
+ "- Lots A & B open 2 hours before; overflow as needed\n"
187
+ "\n*Try: “What time should I arrive?” or “Where do I pick up the gown?”*"
188
+ )
189
+
190
+ # ---- wiring ----
191
+ def on_send(history, message, max_new_tokens, temperature, top_p):
192
+ message = (message or "").strip()
193
+ if not message:
194
+ return history, ""
195
+ history = history + [[message, None]]
196
+ reply = chat_pipeline(message, max_new_tokens, temperature, top_p)
197
+ history[-1][1] = reply
198
+ return history, ""
199
+
200
+ send.click(on_send, [chatbot, msg, max_new, temp, topp], [chatbot, msg])
201
+ msg.submit(on_send, [chatbot, msg, max_new, temp, topp], [chatbot, msg])
202
+
203
+ # Quick chips → prefill
204
+ chip1.click(lambda: "What are the parking rules?", outputs=msg)
205
+ chip2.click(lambda: "Can I buy multiple parking passes?", outputs=msg)
206
+ chip3.click(lambda: "Is formal attire required?", outputs=msg)
207
+ chip4.click(lambda: "How do I pick a cap & gown size?", outputs=msg)
208
+ chip5.click(lambda: "What is the refund policy?", outputs=msg)
209
+ chip6.click(lambda: "When is the shipping cutoff for cap & gown?", outputs=msg)
210
+
211
+ # Health / capabilities
212
+ def ui_health_check():
213
+ return (f"### Status: ✅ Healthy\n- Model: `{MODEL_NAME}`\n"
214
+ f"- Storefront module: {'yes' if USE_HELPERS else 'no'}\n"
215
+ f"- Storefront JSON: {'loaded' if bool(STORE_DATA) else 'not found'}")
216
+ def ui_capabilities():
217
+ caps = [
218
+ "Chat (LLM – text-generation)",
219
+ "Quick storefront Q&A (parking rules, attire, products)",
220
+ "Adjustable: max_new_tokens, temperature, top-p",
221
+ ]
222
+ return "### Capabilities\n- " + "\n- ".join(caps)
223
+
224
+ def _health():
225
+ return ui_health_check(), "Status: ✅ Healthy"
226
+
227
+ health_btn.click(_health, outputs=[chatbot, status_md])
228
+ caps_btn.click(ui_capabilities, outputs=chatbot)
229
+
230
+ if __name__ == "__main__":
231
+ demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))