CoolFace
Apppublic

wowpixels/Perfume-recommender-agent

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
app.py180 linesDownload Raw Back to root
1import os2import json3import socket4import requests5import gradio as gr6from huggingface_hub import login7from smolagents import CodeAgent, InferenceClientModel, DuckDuckGoSearchTool, VisitWebpageTool8 9# Auth10hf_token = os.getenv("HF_TOKEN")11if hf_token:12    login(token=hf_token)13    print("HF login OK")14else:15    print("No HF_TOKEN set")16 17# Model18MODEL_ID = "Qwen/Qwen2.5-7B-Instruct"19 20def build_agent(enable_tools: bool):21    model = InferenceClientModel(model_id=MODEL_ID, token=hf_token)22    tools = [DuckDuckGoSearchTool(), VisitWebpageTool()] if enable_tools else []23    return CodeAgent(tools=tools, model=model, max_steps=8)24 25# Perfume data and logic26CATALOG = [27    {"name": "Dior Sauvage EDT", "notes": ["bergamot", "ambroxan", "pepper"], "season": ["spring", "summer"], "style": "fresh", "price": 95, "gender": "masculine"},28    {"name": "Chanel Bleu de Chanel EDP", "notes": ["citrus", "incense", "cedar"], "season": ["all"], "style": "fresh-woody", "price": 120, "gender": "masculine"},29    {"name": "Creed Aventus", "notes": ["pineapple", "birch", "musk"], "season": ["spring", "summer", "fall"], "style": "fruity-woody", "price": 365, "gender": "masculine"},30    {"name": "Maison Francis Kurkdjian Baccarat Rouge 540", "notes": ["saffron", "ambergris", "cedar"], "season": ["fall", "winter"], "style": "ambery", "price": 325, "gender": "unisex"},31    {"name": "Le Labo Santal 33", "notes": ["sandalwood", "leather", "violet"], "season": ["fall", "winter", "spring"], "style": "woody", "price": 220, "gender": "unisex"},32    {"name": "Tom Ford Black Orchid", "notes": ["truffle", "patchouli", "chocolate"], "season": ["fall", "winter", "night"], "style": "gourmand", "price": 160, "gender": "unisex"},33    {"name": "Byredo Gypsy Water", "notes": ["juniper", "lemon", "vanilla"], "season": ["spring", "summer"], "style": "fresh-woody", "price": 205, "gender": "unisex"},34    {"name": "Chanel Chance Eau Tendre", "notes": ["grapefruit", "jasmine", "musk"], "season": ["spring", "summer"], "style": "fresh-floral", "price": 120, "gender": "feminine"},35    {"name": "YSL Libre EDP", "notes": ["lavender", "orange blossom", "vanilla"], "season": ["fall", "winter"], "style": "floral-amber", "price": 130, "gender": "feminine"},36    {"name": "Jo Malone Wood Sage & Sea Salt", "notes": ["sage", "ambrette", "sea salt"], "season": ["summer", "spring"], "style": "fresh-aromatic", "price": 165, "gender": "unisex"},37]38 39def shortlist_catalog(preferred_notes, season, budget, gender_pref):40    notes = [n.strip().lower() for n in preferred_notes.split(",") if n.strip()] if preferred_notes else []41    season = (season or "all").lower()42    gender_pref = (gender_pref or "unisex").lower()43    max_price = float(budget) if budget else 9999.044 45    def score(item):46        s = 047        if season in item["season"] or "all" in item["season"]:48            s += 149        if gender_pref in ["any", "unisex"] or gender_pref == item["gender"]:50            s += 151        s += sum(1 for n in notes if n in item["notes"])52        if item["price"] <= max_price:53            s += 154        return s55 56    ranked = sorted(CATALOG, key=score, reverse=True)57    top = [p for p in ranked if score(p) > 0][:5]58    return top or ranked[:3]59 60def recommend_perfumes(preferred_notes, season, occasion, budget, gender_pref, use_web_tools):61    agent = build_agent(enable_tools=use_web_tools)62    shortlist = shortlist_catalog(preferred_notes, season, budget, gender_pref)63 64    prompt = (65        "Recommend three perfumes, ranked 1 to 3.\n"66        f"Preferred notes: {preferred_notes or 'not specified'}\n"67        f"Season: {season or 'any'}\n"68        f"Occasion: {occasion or 'any'}\n"69        f"Budget cap (USD): {budget or 'no cap'}\n"70        f"Gender preference: {gender_pref or 'any'}\n\n"71        "Candidates you can consider:\n"72        f"{json.dumps(shortlist, indent=2)}\n\n"73        "Return a short list with brand and perfume name, brief notes and vibe, why it fits, and approx price or where to sample. Keep it under 120 words."74    )75 76    try:77        return str(agent.run(prompt))78    except Exception as e:79        lines = [f"{i+1}. {p['name']} — ${p['price']} • {', '.join(p['notes'])}" for i, p in enumerate(shortlist[:3])]80        return "Local shortlist:\n" + "\n".join(lines) + f"\nError: {e}"81 82# GAIA test83GAIA_BASE = "https://agents-course-unit4-scoring.hf.space"84 85def fetch_random_question():86    r = requests.get(f"{GAIA_BASE}/random-question", timeout=15)87    r.raise_for_status()88    return r.json()89 90def _check_match(agent_answer, expected_answer):91    a = str(agent_answer).strip().lower()92    e = str(expected_answer).strip().lower()93    if a == e:94        return "Exact match"95    if e and e in a:96        return "Partial match"97    return "No match"98 99def run_agent_on_gaia():100    try:101        q = fetch_random_question()102    except Exception as e:103        return f"Error fetching GAIA question: {e}"104 105    question_text = q.get("question") or ""106    expected_answer = q.get("final_answer") or ""107 108    try:109        agent = build_agent(enable_tools=True)110        agent_answer = str(agent.run(question_text))111    except Exception as e:112        agent_answer = f"Error running agent: {e}"113 114    return (115        "### Question\n"116        f"{question_text}\n\n"117        "### Agent answer\n"118        f"{agent_answer}\n\n"119        "### Expected answer\n"120        f"{expected_answer}\n\n"121        "### Match\n"122        f"{_check_match(agent_answer, expected_answer)}"123    )124 125# Diagnostics126def net_diagnostics():127    results = {}128    hosts = {129        "gaia": "agents-course-unit4-scoring.hf.space",130        "huggingface": "huggingface.co",131        "google": "www.google.com",132    }133    for name, host in hosts.items():134        try:135            ip = socket.gethostbyname(host)136            results[f"dns_{name}"] = f"OK {host} -> {ip}"137        except Exception as e:138            results[f"dns_{name}"] = f"FAIL {host} -> {e}"139 140    urls = {141        "gaia_random_question": f"{GAIA_BASE}/random-question",142        "hf_home": "https://huggingface.co",143    }144    for key, url in urls.items():145        try:146            r = requests.get(url, timeout=10)147            results[f"http_{key}"] = f"{r.status_code} {len(r.content)} bytes"148        except Exception as e:149            results[f"http_{key}"] = f"FAIL {e}"150 151    return "```\n" + json.dumps(results, indent=2) + "\n```"152 153# UI154with gr.Blocks(title="Perfume Recommender and GAIA Test") as demo:155    with gr.Tab("Perfume"):156        with gr.Row():157            with gr.Column():158                notes = gr.Textbox(label="Preferred notes (comma separated)")159                season = gr.Dropdown(label="Season", choices=["any","spring","summer","fall","winter","night"], value="any")160                occasion = gr.Dropdown(label="Occasion", choices=["any","work","date","party","gym","formal"], value="any")161                budget = gr.Textbox(label="Budget cap in USD")162                gender = gr.Dropdown(label="Gender preference", choices=["any","masculine","feminine","unisex"], value="any")163                use_tools = gr.Checkbox(label="Use web tools", value=False)164                go = gr.Button("Recommend")165            with gr.Column():166                rec_out = gr.Markdown()167        go.click(recommend_perfumes, [notes, season, occasion, budget, gender, use_tools], [rec_out], show_progress="full")168 169    with gr.Tab("GAIA"):170        gaia_btn = gr.Button("Get random question and answer")171        gaia_out = gr.Markdown()172        gaia_btn.click(run_agent_on_gaia, None, [gaia_out], show_progress="full")173 174    with gr.Tab("Diagnostics"):175        diag_btn = gr.Button("Network diagnostics")176        diag_out = gr.Markdown()177        diag_btn.click(net_diagnostics, None, [diag_out], show_progress="minimal")178 179demo.queue().launch()180