""" 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*")