Spaces:
Running
Running
import pandas as pd | |
from sentence_transformers import SentenceTransformer, util | |
import gradio as gr | |
# βββ Load catalog & compute embeddings βββββββββββββββββββββββββ | |
PRODUCTS_CSV = "products.csv" | |
df = pd.read_csv(PRODUCTS_CSV) | |
descriptions = df["description"].tolist() | |
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") | |
embeddings = model.encode(descriptions, convert_to_tensor=True, normalize_embeddings=True) | |
# βββ Semantic-search function βββββββββββββββββββββββββββββββββ | |
def search_products(query: str, top_k: int = 5): | |
if not query.strip(): | |
return pd.DataFrame(columns=df.columns.tolist() + ["score"]) | |
q_emb = model.encode(query, convert_to_tensor=True, normalize_embeddings=True) | |
hits = util.cos_sim(q_emb, embeddings)[0].topk(k=top_k) | |
indices = hits.indices.cpu().tolist() | |
scores = [round(float(s), 3) for s in hits.values.cpu().tolist()] | |
results = df.iloc[indices].copy() | |
results["score"] = scores | |
return results.reset_index(drop=True) | |
# βββ Gradio UI ββββββββββββββββββββββββββββββββββββββββββββββββ | |
with gr.Blocks(title="ποΈ Salon Catalog Semantic Search") as demo: | |
gr.Markdown(""" | |
# ποΈ Salon Product Search | |
**Natural-language queries** β **top matching products** via MiniLM embeddings | |
(Runs entirely on free CPU; no paid APIs) | |
""") | |
with gr.Row(): | |
query_in = gr.Textbox(placeholder="e.g. sulfate-free shampoo under $15", label="Search query") | |
topk = gr.Slider(1, 10, value=5, step=1, label="Number of results") | |
search_btn = gr.Button("Search π", variant="primary") | |
table = gr.Dataframe( | |
headers=list(df.columns) + ["score"], | |
datatype=["number","str","str","str","number","number"], | |
interactive=False, | |
row_count= topk.value, | |
label="Results" | |
) | |
search_btn.click(search_products, [query_in, topk], table) | |
if __name__ == "__main__": | |
demo.launch(server_name="0.0.0.0") | |