Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import socket | |
| import requests | |
| import gradio as gr | |
| from huggingface_hub import login | |
| from smolagents import CodeAgent, InferenceClientModel, DuckDuckGoSearchTool, VisitWebpageTool | |
| # Auth | |
| hf_token = os.getenv("HF_TOKEN") | |
| if hf_token: | |
| login(token=hf_token) | |
| print("HF login OK") | |
| else: | |
| print("No HF_TOKEN set") | |
| # Model | |
| MODEL_ID = "Qwen/Qwen2.5-7B-Instruct" | |
| def build_agent(enable_tools: bool): | |
| model = InferenceClientModel(model_id=MODEL_ID, token=hf_token) | |
| tools = [DuckDuckGoSearchTool(), VisitWebpageTool()] if enable_tools else [] | |
| return CodeAgent(tools=tools, model=model, max_steps=8) | |
| # Perfume data and logic | |
| CATALOG = [ | |
| {"name": "Dior Sauvage EDT", "notes": ["bergamot", "ambroxan", "pepper"], "season": ["spring", "summer"], "style": "fresh", "price": 95, "gender": "masculine"}, | |
| {"name": "Chanel Bleu de Chanel EDP", "notes": ["citrus", "incense", "cedar"], "season": ["all"], "style": "fresh-woody", "price": 120, "gender": "masculine"}, | |
| {"name": "Creed Aventus", "notes": ["pineapple", "birch", "musk"], "season": ["spring", "summer", "fall"], "style": "fruity-woody", "price": 365, "gender": "masculine"}, | |
| {"name": "Maison Francis Kurkdjian Baccarat Rouge 540", "notes": ["saffron", "ambergris", "cedar"], "season": ["fall", "winter"], "style": "ambery", "price": 325, "gender": "unisex"}, | |
| {"name": "Le Labo Santal 33", "notes": ["sandalwood", "leather", "violet"], "season": ["fall", "winter", "spring"], "style": "woody", "price": 220, "gender": "unisex"}, | |
| {"name": "Tom Ford Black Orchid", "notes": ["truffle", "patchouli", "chocolate"], "season": ["fall", "winter", "night"], "style": "gourmand", "price": 160, "gender": "unisex"}, | |
| {"name": "Byredo Gypsy Water", "notes": ["juniper", "lemon", "vanilla"], "season": ["spring", "summer"], "style": "fresh-woody", "price": 205, "gender": "unisex"}, | |
| {"name": "Chanel Chance Eau Tendre", "notes": ["grapefruit", "jasmine", "musk"], "season": ["spring", "summer"], "style": "fresh-floral", "price": 120, "gender": "feminine"}, | |
| {"name": "YSL Libre EDP", "notes": ["lavender", "orange blossom", "vanilla"], "season": ["fall", "winter"], "style": "floral-amber", "price": 130, "gender": "feminine"}, | |
| {"name": "Jo Malone Wood Sage & Sea Salt", "notes": ["sage", "ambrette", "sea salt"], "season": ["summer", "spring"], "style": "fresh-aromatic", "price": 165, "gender": "unisex"}, | |
| ] | |
| def shortlist_catalog(preferred_notes, season, budget, gender_pref): | |
| notes = [n.strip().lower() for n in preferred_notes.split(",") if n.strip()] if preferred_notes else [] | |
| season = (season or "all").lower() | |
| gender_pref = (gender_pref or "unisex").lower() | |
| max_price = float(budget) if budget else 9999.0 | |
| def score(item): | |
| s = 0 | |
| if season in item["season"] or "all" in item["season"]: | |
| s += 1 | |
| if gender_pref in ["any", "unisex"] or gender_pref == item["gender"]: | |
| s += 1 | |
| s += sum(1 for n in notes if n in item["notes"]) | |
| if item["price"] <= max_price: | |
| s += 1 | |
| return s | |
| ranked = sorted(CATALOG, key=score, reverse=True) | |
| top = [p for p in ranked if score(p) > 0][:5] | |
| return top or ranked[:3] | |
| def recommend_perfumes(preferred_notes, season, occasion, budget, gender_pref, use_web_tools): | |
| agent = build_agent(enable_tools=use_web_tools) | |
| shortlist = shortlist_catalog(preferred_notes, season, budget, gender_pref) | |
| prompt = ( | |
| "Recommend three perfumes, ranked 1 to 3.\n" | |
| f"Preferred notes: {preferred_notes or 'not specified'}\n" | |
| f"Season: {season or 'any'}\n" | |
| f"Occasion: {occasion or 'any'}\n" | |
| f"Budget cap (USD): {budget or 'no cap'}\n" | |
| f"Gender preference: {gender_pref or 'any'}\n\n" | |
| "Candidates you can consider:\n" | |
| f"{json.dumps(shortlist, indent=2)}\n\n" | |
| "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." | |
| ) | |
| try: | |
| return str(agent.run(prompt)) | |
| except Exception as e: | |
| lines = [f"{i+1}. {p['name']} — ${p['price']} • {', '.join(p['notes'])}" for i, p in enumerate(shortlist[:3])] | |
| return "Local shortlist:\n" + "\n".join(lines) + f"\nError: {e}" | |
| # GAIA test | |
| GAIA_BASE = "https://agents-course-unit4-scoring.hf.space" | |
| def fetch_random_question(): | |
| r = requests.get(f"{GAIA_BASE}/random-question", timeout=15) | |
| r.raise_for_status() | |
| return r.json() | |
| def _check_match(agent_answer, expected_answer): | |
| a = str(agent_answer).strip().lower() | |
| e = str(expected_answer).strip().lower() | |
| if a == e: | |
| return "Exact match" | |
| if e and e in a: | |
| return "Partial match" | |
| return "No match" | |
| def run_agent_on_gaia(): | |
| try: | |
| q = fetch_random_question() | |
| except Exception as e: | |
| return f"Error fetching GAIA question: {e}" | |
| question_text = q.get("question") or "" | |
| expected_answer = q.get("final_answer") or "" | |
| try: | |
| agent = build_agent(enable_tools=True) | |
| agent_answer = str(agent.run(question_text)) | |
| except Exception as e: | |
| agent_answer = f"Error running agent: {e}" | |
| return ( | |
| "### Question\n" | |
| f"{question_text}\n\n" | |
| "### Agent answer\n" | |
| f"{agent_answer}\n\n" | |
| "### Expected answer\n" | |
| f"{expected_answer}\n\n" | |
| "### Match\n" | |
| f"{_check_match(agent_answer, expected_answer)}" | |
| ) | |
| # Diagnostics | |
| def net_diagnostics(): | |
| results = {} | |
| hosts = { | |
| "gaia": "agents-course-unit4-scoring.hf.space", | |
| "huggingface": "huggingface.co", | |
| "google": "www.google.com", | |
| } | |
| for name, host in hosts.items(): | |
| try: | |
| ip = socket.gethostbyname(host) | |
| results[f"dns_{name}"] = f"OK {host} -> {ip}" | |
| except Exception as e: | |
| results[f"dns_{name}"] = f"FAIL {host} -> {e}" | |
| urls = { | |
| "gaia_random_question": f"{GAIA_BASE}/random-question", | |
| "hf_home": "https://huggingface.co", | |
| } | |
| for key, url in urls.items(): | |
| try: | |
| r = requests.get(url, timeout=10) | |
| results[f"http_{key}"] = f"{r.status_code} {len(r.content)} bytes" | |
| except Exception as e: | |
| results[f"http_{key}"] = f"FAIL {e}" | |
| return "```\n" + json.dumps(results, indent=2) + "\n```" | |
| # UI | |
| with gr.Blocks(title="Perfume Recommender and GAIA Test") as demo: | |
| with gr.Tab("Perfume"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| notes = gr.Textbox(label="Preferred notes (comma separated)") | |
| season = gr.Dropdown(label="Season", choices=["any","spring","summer","fall","winter","night"], value="any") | |
| occasion = gr.Dropdown(label="Occasion", choices=["any","work","date","party","gym","formal"], value="any") | |
| budget = gr.Textbox(label="Budget cap in USD") | |
| gender = gr.Dropdown(label="Gender preference", choices=["any","masculine","feminine","unisex"], value="any") | |
| use_tools = gr.Checkbox(label="Use web tools", value=False) | |
| go = gr.Button("Recommend") | |
| with gr.Column(): | |
| rec_out = gr.Markdown() | |
| go.click(recommend_perfumes, [notes, season, occasion, budget, gender, use_tools], [rec_out], show_progress="full") | |
| with gr.Tab("GAIA"): | |
| gaia_btn = gr.Button("Get random question and answer") | |
| gaia_out = gr.Markdown() | |
| gaia_btn.click(run_agent_on_gaia, None, [gaia_out], show_progress="full") | |
| with gr.Tab("Diagnostics"): | |
| diag_btn = gr.Button("Network diagnostics") | |
| diag_out = gr.Markdown() | |
| diag_btn.click(net_diagnostics, None, [diag_out], show_progress="minimal") | |
| demo.queue().launch() | |