JerameeUC commited on
Commit
eedb2c4
·
1 Parent(s): 929cd0b

Fixed Layout

Browse files
README.md CHANGED
@@ -5,7 +5,7 @@ colorFrom: indigo
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: "4.38.0"
8
- app_file: storefront_tabs_memory_app.py
9
  pinned: false
10
  license: mit
11
  short_description: Test for the project front-end.
 
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: "4.38.0"
8
+ app_file: app_storefront.py
9
  pinned: false
10
  license: mit
11
  short_description: Test for the project front-end.
storefront_tabs_app.py → app_storefront.py RENAMED
@@ -1,123 +1,25 @@
1
- import os, json
 
2
  import gradio as gr
3
- from transformers import pipeline
4
-
5
- # ---------------- Model (same pipeline you used) ----------------
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
- out = _get_pipe()(
16
- message,
17
- max_new_tokens=int(max_new_tokens),
18
- do_sample=True,
19
- temperature=float(temperature),
20
- top_p=float(top_p),
21
- pad_token_id=50256,
22
- )
23
- return out[0]["generated_text"]
24
-
25
-
26
- # ---------------- Storefront knowledge (helper module preferred) ----------------
27
- STORE_DATA, USE_HELPERS = None, False
28
- try:
29
- # If you keep these under agenticcore/
30
- from agenticcore.storefront_rules import (
31
- load_storefront, answer_faq, get_parking_rules, get_venue_rules, search_products
32
- )
33
- STORE_DATA = load_storefront()
34
- USE_HELPERS = True
35
- except Exception:
36
- # Fallback: look for JSON near this file or under agenticcore/
37
- for p in [
38
- os.path.join(os.path.dirname(__file__), "storefront_data.json"),
39
- os.path.join(os.path.dirname(__file__), "agenticcore", "storefront_data.json"),
40
- ]:
41
- if os.path.exists(p):
42
- with open(p, "r", encoding="utf-8") as f:
43
- STORE_DATA = json.load(f)
44
- break
45
-
46
- # Defaults if JSON is missing
47
- DEFAULT_PRODUCTS = [
48
- {"SKU": "CG-SET", "Name": "Cap & Gown Set", "Price": 59.00, "Notes": "Tassel included; ship until 10 days before event"},
49
- {"SKU": "PK-1", "Name": "Parking Pass", "Price": 10.00, "Notes": "Multiple passes allowed per student"},
50
- ]
51
- DEFAULT_PARKING = ["No double parking.", "Vehicles parked in handicap will be towed."]
52
- DEFAULT_VENUE = ["Formal attire recommended (not required).", "No muscle shirts.", "No sagging pants."]
53
-
54
- if STORE_DATA:
55
- try:
56
- DEFAULT_PRODUCTS = [{
57
- "SKU": p.get("sku",""),
58
- "Name": p.get("name",""),
59
- "Price": p.get("price_usd",""),
60
- "Notes": (p.get("description") or "")[:120],
61
- } for p in STORE_DATA.get("products", [])]
62
- DEFAULT_PARKING = STORE_DATA.get("policies", {}).get("parking_rules", DEFAULT_PARKING)
63
- DEFAULT_VENUE = STORE_DATA.get("policies", {}).get("venue_rules", DEFAULT_VENUE)
64
- except Exception:
65
- pass
66
-
67
-
68
- # ---------------- Routing: storefront first, then LLM ----------------
69
- def storefront_qna(text: str) -> str | None:
70
- t = (text or "").lower().strip()
71
- if not t:
72
- return None
73
-
74
- # Prefer your helper functions if available
75
- if USE_HELPERS and STORE_DATA:
76
- a = answer_faq(STORE_DATA, t)
77
- if a:
78
- return a
79
- if "parking" in t and "rule" in t:
80
- r = get_parking_rules(STORE_DATA)
81
- if r:
82
- return "Parking rules:\n- " + "\n- ".join(r)
83
- if "venue" in t and "rule" in t or "attire" in t or "dress code" in t:
84
- r = get_venue_rules(STORE_DATA)
85
- if r:
86
- return "Venue rules:\n- " + "\n- ".join(r)
87
- # Extra: arrival timing for parking lots
88
- if "parking" in t and ("hours" in t or "time" in t):
89
- return "Parking lots open 2 hours before the ceremony."
90
- hits = search_products(STORE_DATA, t)
91
- if hits:
92
- return "\n".join(
93
- f"{p.get('name','Item')} — ${p.get('price_usd',0):.2f}: {p.get('description','')}"
94
- for p in hits
95
- )
96
- return None
97
-
98
- # Fallback rules (no helper module)
99
- if "parking" in t and ("multiple" in t or "more than one" in t or "extra" in t):
100
- return "Yes, multiple parking passes are allowed per student."
101
- if "parking" in t and "rule" in t:
102
- return "Parking rules:\n- " + "\n- ".join(DEFAULT_PARKING)
103
- if "parking" in t and ("hours" in t or "time" in t):
104
- return "Parking lots open 2 hours before the ceremony."
105
- if "attire" in t or "dress code" in t or ("venue" in t and "rule" in t):
106
- return "Venue rules:\n- " + "\n- ".join(DEFAULT_VENUE)
107
- if "cap" in t or "gown" in t:
108
- return "\n".join(f"{p['Name']} — ${p['Price']:.2f}: {p['Notes']}" for p in DEFAULT_PRODUCTS)
109
- return None
110
-
111
-
112
- def chat_pipeline(message, max_new_tokens=128, temperature=0.8, top_p=0.95):
113
- """Intercept storefront topics first; else generate with the model."""
114
  sf = storefront_qna(message)
115
  if sf:
116
  return sf
117
- return model_generate(message, max_new_tokens, temperature, top_p)
 
 
118
 
119
-
120
- # ---------------- Gradio UI (Tabs + Accordion) ----------------
121
  CSS = """
122
  :root { --bg:#0b0d12; --panel:#0f172a; --border:#1f2940; --text:#e5e7eb; --muted:#9ca3af; }
123
  .gradio-container { background: var(--bg) !important; color: var(--text) !important; }
@@ -128,49 +30,51 @@ CSS = """
128
  with gr.Blocks(title="Storefront Chat", css=CSS) as demo:
129
  gr.Markdown("## Storefront Chat")
130
 
 
 
 
131
  with gr.Tabs():
132
- # --- TAB 1: Chat (sliders tucked away) ---
133
  with gr.TabItem("Chat"):
134
  with gr.Group(elem_classes=["panel"]):
135
  chat = gr.Chatbot(height=360, bubble_full_width=False, label="Chat")
 
136
  with gr.Row():
137
  msg = gr.Textbox(placeholder="Ask about parking rules, attire, cap & gown, pickup times…", scale=5)
138
  send = gr.Button("Send", scale=1)
139
 
140
- # Quick chips
141
  with gr.Row():
142
  chip1 = gr.Button("Parking rules", variant="secondary")
143
  chip2 = gr.Button("Multiple passes", variant="secondary")
144
  chip3 = gr.Button("Attire", variant="secondary")
145
  chip4 = gr.Button("When do lots open?", variant="secondary")
146
 
147
- # Advanced chat options in an Accordion
148
  with gr.Accordion("Advanced chat options", open=False):
149
  max_new = gr.Slider(32, 512, 128, 1, label="Max new tokens")
150
  temp = gr.Slider(0.1, 1.5, 0.8, 0.05, label="Temperature")
151
  topp = gr.Slider(0.1, 1.0, 0.95, 0.05, label="Top-p")
152
 
153
- # Simple health & capabilities as small buttons
154
- with gr.Row():
155
- health_btn = gr.Button("Health", variant="secondary")
156
- caps_btn = gr.Button("Capabilities", variant="secondary")
157
- status_md = gr.Markdown("Status: not checked", elem_classes=["small"])
158
 
159
- # --- TAB 2: Products ---
160
  with gr.TabItem("Products"):
161
  gr.Markdown("### Available Items")
162
  cols = list(DEFAULT_PRODUCTS[0].keys()) if DEFAULT_PRODUCTS else ["SKU","Name","Price","Notes"]
163
  data = [[p.get(c,"") for c in cols] for p in DEFAULT_PRODUCTS]
164
- products_tbl = gr.Dataframe(headers=cols, value=data, interactive=False, wrap=True, label="Products")
165
 
166
- # --- TAB 3: Rules ---
167
  with gr.TabItem("Rules"):
168
  gr.Markdown("### Venue rules")
169
  gr.Markdown("- " + "\n- ".join(DEFAULT_VENUE))
170
  gr.Markdown("### Parking rules")
171
  gr.Markdown("- " + "\n- ".join(DEFAULT_PARKING))
172
 
173
- # --- TAB 4: Logistics ---
174
  with gr.TabItem("Logistics"):
175
  gr.Markdown(
176
  "### Event Logistics\n"
@@ -181,45 +85,52 @@ with gr.Blocks(title="Storefront Chat", css=CSS) as demo:
181
  "\n*Try asking the bot:* “What time should I arrive?” • “Where do I pick up the gown?”"
182
  )
183
 
184
- # ---- Wiring ----
 
 
 
 
 
185
  def on_send(history, message, max_new_tokens, temperature, top_p):
186
  t = (message or "").strip()
187
  if not t:
188
- return history, ""
189
- history = history + [[t, None]]
190
- reply = chat_pipeline(t, max_new_tokens, temperature, top_p)
191
  history[-1][1] = reply
192
- return history, ""
193
 
194
- send.click(on_send, [chat, msg, max_new, temp, topp], [chat, msg])
195
- msg.submit(on_send, [chat, msg, max_new, temp, topp], [chat, msg])
 
 
 
 
 
196
 
197
- # Quick chips → prefill textbox
198
- chip1.click(lambda: "What are the parking rules?", outputs=msg)
199
- chip2.click(lambda: "Can I buy multiple parking passes?", outputs=msg)
200
- chip3.click(lambda: "Is formal attire required?", outputs=msg)
201
- chip4.click(lambda: "What time do the parking lots open?", outputs=msg)
202
-
203
- # Health / capabilities
204
- def ui_health_check():
205
- return (f"### Status: ✅ Healthy\n"
206
- f"- Model: `{MODEL_NAME}`\n"
207
- f"- Storefront module: {'yes' if USE_HELPERS else 'no'}\n"
208
- f"- Storefront JSON: {'loaded' if bool(STORE_DATA) else 'not found'}")
209
-
210
- def ui_capabilities():
211
  caps = [
212
- "Chat (LLM text-generation)",
213
  "Storefront Q&A (parking, attire, products, logistics)",
214
  "Adjustable: max_new_tokens, temperature, top-p",
215
  ]
216
- return "### Capabilities\n- " + "\n- ".join(caps)
 
 
 
 
 
 
217
 
218
- def _health():
219
- return ui_health_check(), "Status: Healthy"
 
 
 
220
 
221
- health_btn.click(_health, outputs=[chat, status_md])
222
- caps_btn.click(ui_capabilities, outputs=chat)
 
223
 
224
  if __name__ == "__main__":
225
  demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))
 
1
+ # app_storefront.py
2
+ import os
3
  import gradio as gr
4
+
5
+ from model_core import model_generate, MODEL_NAME
6
+ from data_storefront import (
7
+ DEFAULT_PRODUCTS, DEFAULT_PARKING, DEFAULT_VENUE,
8
+ STORE_DATA, USE_HELPERS
9
+ )
10
+ from qna_router import storefront_qna
11
+ from memory_core import build_prompt_from_history, clean_generation
12
+
13
+ # ---------------- Chat logic ----------------
14
+ def chat_pipeline(history, message, max_new_tokens=128, temperature=0.8, top_p=0.95):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  sf = storefront_qna(message)
16
  if sf:
17
  return sf
18
+ prompt = build_prompt_from_history(history, message, k=4)
19
+ gen = model_generate(prompt, max_new_tokens, temperature, top_p)
20
+ return clean_generation(gen)
21
 
22
+ # ---------------- UI ----------------
 
23
  CSS = """
24
  :root { --bg:#0b0d12; --panel:#0f172a; --border:#1f2940; --text:#e5e7eb; --muted:#9ca3af; }
25
  .gradio-container { background: var(--bg) !important; color: var(--text) !important; }
 
30
  with gr.Blocks(title="Storefront Chat", css=CSS) as demo:
31
  gr.Markdown("## Storefront Chat")
32
 
33
+ # One state object that mirrors the Chatbot value
34
+ history_state = gr.State([])
35
+
36
  with gr.Tabs():
37
+ # --- Chat tab ---
38
  with gr.TabItem("Chat"):
39
  with gr.Group(elem_classes=["panel"]):
40
  chat = gr.Chatbot(height=360, bubble_full_width=False, label="Chat")
41
+
42
  with gr.Row():
43
  msg = gr.Textbox(placeholder="Ask about parking rules, attire, cap & gown, pickup times…", scale=5)
44
  send = gr.Button("Send", scale=1)
45
 
 
46
  with gr.Row():
47
  chip1 = gr.Button("Parking rules", variant="secondary")
48
  chip2 = gr.Button("Multiple passes", variant="secondary")
49
  chip3 = gr.Button("Attire", variant="secondary")
50
  chip4 = gr.Button("When do lots open?", variant="secondary")
51
 
52
+ # Advanced area (sliders + health/capabilities) — off the main screen
53
  with gr.Accordion("Advanced chat options", open=False):
54
  max_new = gr.Slider(32, 512, 128, 1, label="Max new tokens")
55
  temp = gr.Slider(0.1, 1.5, 0.8, 0.05, label="Temperature")
56
  topp = gr.Slider(0.1, 1.0, 0.95, 0.05, label="Top-p")
57
 
58
+ with gr.Row():
59
+ health_btn = gr.Button("Health", variant="secondary")
60
+ caps_btn = gr.Button("Capabilities", variant="secondary")
61
+ status_md = gr.Markdown("Status: not checked", elem_classes=["small"])
 
62
 
63
+ # --- Products tab ---
64
  with gr.TabItem("Products"):
65
  gr.Markdown("### Available Items")
66
  cols = list(DEFAULT_PRODUCTS[0].keys()) if DEFAULT_PRODUCTS else ["SKU","Name","Price","Notes"]
67
  data = [[p.get(c,"") for c in cols] for p in DEFAULT_PRODUCTS]
68
+ gr.Dataframe(headers=cols, value=data, interactive=False, wrap=True, label="Products")
69
 
70
+ # --- Rules tab ---
71
  with gr.TabItem("Rules"):
72
  gr.Markdown("### Venue rules")
73
  gr.Markdown("- " + "\n- ".join(DEFAULT_VENUE))
74
  gr.Markdown("### Parking rules")
75
  gr.Markdown("- " + "\n- ".join(DEFAULT_PARKING))
76
 
77
+ # --- Logistics tab ---
78
  with gr.TabItem("Logistics"):
79
  gr.Markdown(
80
  "### Event Logistics\n"
 
85
  "\n*Try asking the bot:* “What time should I arrive?” • “Where do I pick up the gown?”"
86
  )
87
 
88
+ # ---------- Helpers that keep Chatbot history valid ----------
89
+ def _append_bot_md(history, md_text):
90
+ history = history or []
91
+ return history + [[None, md_text]]
92
+
93
+ # ---------- Callbacks ----------
94
  def on_send(history, message, max_new_tokens, temperature, top_p):
95
  t = (message or "").strip()
96
  if not t:
97
+ return history, history, "" # no-op
98
+ history = (history or []) + [[t, None]]
99
+ reply = chat_pipeline(history[:-1], t, max_new_tokens, temperature, top_p)
100
  history[-1][1] = reply
101
+ return history, history, ""
102
 
103
+ def _health_cb(history):
104
+ md = (f"### Status: Healthy\n"
105
+ f"- Model: `{MODEL_NAME}`\n"
106
+ f"- Storefront module: {'yes' if USE_HELPERS else 'no'}\n"
107
+ f"- Storefront JSON: {'loaded' if bool(STORE_DATA) else 'not found'}")
108
+ new_hist = _append_bot_md(history, md)
109
+ return new_hist, new_hist, "Status: ✅ Healthy"
110
 
111
+ def _caps_cb(history):
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  caps = [
113
+ "Chat (LLM text-generation, memory-aware prompt)",
114
  "Storefront Q&A (parking, attire, products, logistics)",
115
  "Adjustable: max_new_tokens, temperature, top-p",
116
  ]
117
+ md = "### Capabilities\n- " + "\n- ".join(caps)
118
+ new_hist = _append_bot_md(history, md)
119
+ return new_hist, new_hist
120
+
121
+ # Wire up (update both state and Chatbot)
122
+ send.click(on_send, [history_state, msg, max_new, temp, topp], [history_state, chat, msg])
123
+ msg.submit(on_send, [history_state, msg, max_new, temp, topp], [history_state, chat, msg])
124
 
125
+ # Chips
126
+ chip1.click(lambda: "What are the parking rules?", outputs=msg)
127
+ chip2.click(lambda: "Can I buy multiple parking passes?", outputs=msg)
128
+ chip3.click(lambda: "Is formal attire required?", outputs=msg)
129
+ chip4.click(lambda: "What time do the parking lots open?", outputs=msg)
130
 
131
+ # Health / Capabilities (now inside the Advanced accordion)
132
+ health_btn.click(_health_cb, inputs=[history_state], outputs=[history_state, chat, status_md])
133
+ caps_btn.click(_caps_cb, inputs=[history_state], outputs=[history_state, chat])
134
 
135
  if __name__ == "__main__":
136
  demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))
core/memory.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def build_prompt_from_history(history, user_text, k=4) -> str:
2
+ """
3
+ history is a list of [user, bot] pairs (Gradio Chatbot format).
4
+ We embed short storefront reminders to reduce hallucinations.
5
+ """
6
+ lines = [
7
+ "You are answering questions about a university graduation storefront.",
8
+ "Products:",
9
+ "- Cap & Gown Set (CG-SET) — $59.00: Tassel included; ships until 10 days before the event",
10
+ "- Parking Pass (PK-1) — $10.00: Multiple passes allowed per student",
11
+ "Venue rules: Formal attire recommended (not required). No muscle shirts. No sagging pants.",
12
+ "Parking rules: No double parking. Vehicles parked in handicap spaces will be towed.",
13
+ "Answer concisely using these facts. If unsure, say what’s known from the list above."
14
+ ]
15
+ for u, b in (history or [])[-k:]:
16
+ if u: lines.append(f"User: {u}")
17
+ if b: lines.append(f"Assistant: {b}")
18
+ lines.append(f"User: {user_text}")
19
+ lines.append("Assistant:")
20
+ return "\n".join(lines)
core/model.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from transformers import pipeline
3
+
4
+ MODEL_NAME = os.getenv("HF_MODEL_GENERATION", "distilgpt2")
5
+ _PIPE = None
6
+
7
+ def get_pipe():
8
+ global _PIPE
9
+ if _PIPE is None:
10
+ _PIPE = pipeline("text-generation", model=MODEL_NAME)
11
+ return _PIPE
12
+
13
+ def model_generate(prompt: str, max_new_tokens=128, temperature=0.8, top_p=0.95) -> str:
14
+ out = get_pipe()(
15
+ prompt,
16
+ max_new_tokens=int(max_new_tokens),
17
+ do_sample=True,
18
+ temperature=float(temperature),
19
+ top_p=float(top_p),
20
+ pad_token_id=50256,
21
+ )
22
+ return out[0]["generated_text"]
core/storefront.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json, os
2
+
3
+ def _find_json():
4
+ candidates = [
5
+ os.path.join(os.getcwd(), "storefront_data.json"),
6
+ os.path.join(os.getcwd(), "agenticcore", "storefront_data.json"),
7
+ ]
8
+ for p in candidates:
9
+ if os.path.exists(p):
10
+ return p
11
+ return None
12
+
13
+ def load_storefront():
14
+ p = _find_json()
15
+ if not p:
16
+ return None
17
+ with open(p, "r", encoding="utf-8") as f:
18
+ return json.load(f)
19
+
20
+ def _string_in_any(s, variants):
21
+ s = s.lower()
22
+ return any(v in s for v in variants)
23
+
24
+ def answer_faq(data, text: str):
25
+ """Very small FAQ matcher by substring; safe if faq[] missing."""
26
+ faq = (data or {}).get("faq") or []
27
+ t = text.lower()
28
+ for item in faq:
29
+ qs = item.get("q") or []
30
+ if any(q.lower() in t for q in qs):
31
+ return item.get("a")
32
+ return None
33
+
34
+ def extract_products(data):
35
+ prods = []
36
+ for p in (data or {}).get("products", []):
37
+ prods.append({
38
+ "sku": p.get("sku",""),
39
+ "name": p.get("name",""),
40
+ "price": p.get("price_usd",""),
41
+ "notes": (p.get("description") or "")[:140],
42
+ })
43
+ return prods
44
+
45
+ def get_rules(data):
46
+ pol = (data or {}).get("policies", {}) or {}
47
+ return pol.get("venue_rules", []), pol.get("parking_rules", [])
48
+
49
+ def storefront_qna(data, user_text: str):
50
+ """
51
+ Lightweight rules: try exact single-word intents first, then faq,
52
+ then rules/products lookup. Return None to allow LLM fallback.
53
+ """
54
+ if not user_text:
55
+ return None
56
+ t = user_text.strip().lower()
57
+
58
+ # single-word catches
59
+ if t in {"parking"}:
60
+ _, pr = get_rules(data)
61
+ if pr: return "Parking rules:\n- " + "\n- ".join(pr)
62
+ if t in {"venue", "attire", "dress", "dress code"}:
63
+ vr, _ = get_rules(data)
64
+ if vr: return "Venue rules:\n- " + "\n- ".join(vr)
65
+ if t in {"passes", "parking pass", "parking passes"}:
66
+ return "Yes, multiple parking passes are allowed per student."
67
+
68
+ # faq
69
+ a = answer_faq(data, t)
70
+ if a: return a
71
+
72
+ # explicit rule asks
73
+ if "parking" in t and "rule" in t:
74
+ _, pr = get_rules(data)
75
+ if pr: return "Parking rules:\n- " + "\n- ".join(pr)
76
+ if ("venue" in t and "rule" in t) or "attire" in t or "dress code" in t:
77
+ vr, _ = get_rules(data)
78
+ if vr: return "Venue rules:\n- " + "\n- ".join(vr)
79
+
80
+ # “lots open” style
81
+ if "parking" in t and ("hours" in t or "time" in t or "open" in t):
82
+ lots_open = (((data or {}).get("logistics") or {}).get("lots_open_hours_before") or 2)
83
+ return f"Parking lots open {lots_open} hours before the ceremony."
84
+
85
+ # products
86
+ if "cap" in t or "gown" in t or "parking pass" in t or "product" in t:
87
+ prods = extract_products(data)
88
+ if prods:
89
+ lines = []
90
+ for p in prods:
91
+ price = p["price"]
92
+ price_str = f"${price:.2f}" if isinstance(price, (int,float)) else str(price)
93
+ lines.append(f"{p['name']} — {price_str}: {p['notes']}")
94
+ return "\n".join(lines)
95
+
96
+ return None
storefront_app.py DELETED
@@ -1,231 +0,0 @@
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")))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
storefront_data.json ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "products": [
3
+ {
4
+ "sku": "CG-SET",
5
+ "name": "Cap & Gown Set",
6
+ "price_usd": 59.0,
7
+ "description": "Tassel included; ships until 10 days before the event. Sizes available at pickup; exchange allowed on-site if inventory permits."
8
+ },
9
+ {
10
+ "sku": "PK-1",
11
+ "name": "Parking Pass",
12
+ "price_usd": 10.0,
13
+ "description": "One vehicle per pass. Multiple passes are allowed per student for extended family or guests."
14
+ }
15
+ ],
16
+ "policies": {
17
+ "parking_rules": [
18
+ "No double parking.",
19
+ "Vehicles parked in handicap spaces will be towed."
20
+ ],
21
+ "venue_rules": [
22
+ "Formal attire is recommended (not required).",
23
+ "No muscle shirts.",
24
+ "No sagging pants."
25
+ ]
26
+ },
27
+ "logistics": {
28
+ "shipping_cutoff_days": 10,
29
+ "shipping_window_business_days": "3–5",
30
+ "pickup_location": "Student Center Bookstore",
31
+ "arrival_times": {
32
+ "graduates_minutes_early": 90,
33
+ "guests_minutes_early": 60
34
+ },
35
+ "lots_open_hours_before": 2,
36
+ "lots": ["A", "B"],
37
+ "overflow": "As needed"
38
+ },
39
+ "faq": [
40
+ {
41
+ "q": [
42
+ "Can I buy multiple parking passes?",
43
+ "multiple passes",
44
+ "more than one parking pass",
45
+ "extra parking pass"
46
+ ],
47
+ "a": "Yes, multiple parking passes are allowed per student."
48
+ },
49
+ {
50
+ "q": [
51
+ "What time do the parking lots open?",
52
+ "When do lots open",
53
+ "parking hours",
54
+ "what time parking"
55
+ ],
56
+ "a": "Parking lots open 2 hours before the ceremony."
57
+ },
58
+ {
59
+ "q": [
60
+ "Is formal attire required?",
61
+ "dress code",
62
+ "what should I wear",
63
+ "attire rules"
64
+ ],
65
+ "a": "Formal attire is recommended but not required. No muscle shirts or sagging pants."
66
+ },
67
+ {
68
+ "q": [
69
+ "Where do I pick up the gown?",
70
+ "gown pickup",
71
+ "pickup location"
72
+ ],
73
+ "a": "Pickup is at the Student Center Bookstore during the week prior to the event."
74
+ },
75
+ {
76
+ "q": [
77
+ "Shipping cutoff",
78
+ "last day to ship",
79
+ "when is shipping available until"
80
+ ],
81
+ "a": "Shipping is available until 10 days before the event. Typical shipping takes 3–5 business days."
82
+ }
83
+ ]
84
+ }