Spaces:
Sleeping
Sleeping
| """ | |
| app.py — Streamlit UI: prompt box + filters + product grid with "why this matches". | |
| Run: streamlit run app.py | |
| Prereq: run `python ingest.py` first to build ./chroma_db. | |
| """ | |
| import streamlit as st | |
| from search import search | |
| from llm import explain_matches | |
| st.set_page_config(page_title="GIVA Discovery", page_icon="💍", layout="wide") | |
| PLACEHOLDER_IMG = "https://via.placeholder.com/300x300?text=No+Image" | |
| st.title("💍 GIVA — Describe it, find it") | |
| st.caption("Type what you want in plain English. Searches GIVA's real inventory by meaning, not keywords.") | |
| # ---- Search bar ----------------------------------------------------------- | |
| prompt = st.text_input( | |
| "What are you looking for?", | |
| placeholder="dainty flower ring in rose gold under 15000", | |
| ) | |
| # ---- Filters -------------------------------------------------------------- | |
| with st.sidebar: | |
| st.header("Refine") | |
| material = st.selectbox("Material", ["Any", "Silver", "Gold"]) | |
| colour = st.selectbox( | |
| "Colour", ["Any", "Silver", "Gold", "Rose Gold", "Yellow Gold"]) | |
| shop_for = st.selectbox("Shop for", ["Any", "Women", "Men", "Kids"]) | |
| solid_gold = st.checkbox("Solid gold only (9KT/14KT)") | |
| price_range = st.slider("Budget (Rs.)", 0, 150000, (0, 15000), step=500) | |
| top_n = st.slider("Results", 4, 24, 12) | |
| use_llm = st.checkbox("Explain matches (Claude)", value=True) | |
| filters = {} | |
| if material != "Any": | |
| filters["primary_category"] = material | |
| if colour != "Any": | |
| filters["colour"] = colour | |
| if shop_for != "Any": | |
| filters["shop_for"] = shop_for | |
| if solid_gold: | |
| filters["solid_gold_only"] = True | |
| filters["min_price"] = price_range[0] | |
| filters["max_price"] = price_range[1] | |
| def render_card(r): | |
| img = r.get("img") or PLACEHOLDER_IMG | |
| st.image(img, use_container_width=True) | |
| st.markdown(f"**{r.get('title', '')}**") | |
| price = r.get("price") | |
| cap = r.get("compare_at_price") | |
| if isinstance(price, (int, float)) and price > 0: | |
| if isinstance(cap, (int, float)) and cap > price: | |
| st.markdown(f"**Rs.{price:,.0f}** ~~Rs.{cap:,.0f}~~") | |
| else: | |
| st.markdown(f"**Rs.{price:,.0f}**") | |
| label = "Gold-plated silver" if ( | |
| r.get("primary_category") == "Silver" | |
| and "gold" in str(r.get("colour", "")).lower() | |
| ) else r.get("primary_category", "") | |
| st.caption(f"{label} · {r.get('colour','')} · {r.get('sku','')}") | |
| if r.get("why"): | |
| st.success(r["why"], icon="✨") | |
| if prompt: | |
| with st.spinner("Searching GIVA's catalogue..."): | |
| results = search(prompt, filters, top_n=top_n) | |
| if use_llm and results: | |
| results = explain_matches(prompt, results) | |
| if not results: | |
| st.warning("No strong match for that. Try loosening the filters or rephrasing.") | |
| else: | |
| st.write(f"### {len(results)} matches") | |
| cols_per_row = 4 | |
| for i in range(0, len(results), cols_per_row): | |
| cols = st.columns(cols_per_row) | |
| for col, r in zip(cols, results[i:i + cols_per_row]): | |
| with col: | |
| render_card(r) | |
| else: | |
| st.info("Try: *twin heart pendant for her* · *evil eye anklet in silver* " | |
| "· *14KT gold diamond studs*") | |