Tutorial 3: Build a Gift Recommender
Create an agent that suggests gifts based on recipient personality, occasion, and budget constraints.
What You'll Build
A Python-based gift recommender that takes recipient details and budget, then queries BuyWhere to find appropriate product recommendations across multiple categories.
Prerequisites
- BuyWhere API key
- Python 3.10+
Step 1: Define Gift Categories and Mappings
Create a gift_recommender.py:
import os
import requests
from typing import Optional
BUYWHERE_API_KEY = os.environ["BUYWHERE_API_KEY"]
BUYWHERE_BASE_URL = "https://api.buywhere.ai"
HEADERS = {
"Authorization": f"Bearer {BUYWHERE_API_KEY}",
"Content-Type": "application/json"
}
OCCASION_CATEGORIES = {
"birthday": ["Electronics", "Fashion", "Beauty", "Sports", "Books"],
"anniversary": ["Jewelry", "Fashion", "Beauty", "Home & Living"],
"graduation": ["Electronics", "Books", "Fashion", "Sports"],
"wedding": ["Home & Living", "Kitchen", "Jewelry"],
"christmas": ["Electronics", "Toys & Games", "Beauty", "Fashion"],
"mother's_day": ["Beauty", "Fashion", "Jewelry", "Home & Living"],
"father's_day": ["Electronics", "Sports", "Fashion", "Tools"],
"valentines": ["Jewelry", "Beauty", "Fashion", "Flowers"],
}
INTEREST_KEYWORDS = {
"tech": ["laptop", "headphones", "smartwatch", "tablet", "wireless earbuds"],
"fashion": ["watch", "bag", "shoes", "wallet", "sunglasses"],
"fitness": ["running shoes", "yoga mat", "dumbbells", "fitness tracker", "sports wear"],
"gaming": ["console", "controller", "gaming headset", "gaming chair", "keyboard"],
"cooking": ["blender", "air fryer", "knife set", "coffee maker", "stand mixer"],
"reading": ["kindle", "books", "bookshelf", "reading light", "book stand"],
"beauty": ["skincare", "makeup", "perfume", "hair dryer", "beauty device"],
}
def search_products(query: str, category: str = None,
max_price: float = None, limit: int = 10) -> dict:
"""Search for products with filters."""
params = {"q": query, "limit": limit}
if category:
params["category"] = category
if max_price:
params["max_price"] = max_price
response = requests.get(
f"{BUYWHERE_BASE_URL}/v2/products",
headers=HEADERS,
params=params,
timeout=30
)
response.raise_for_status()
return response.json()
Step 2: Build the Recommendation Engine
def get_gift_recommendations(
occasion: str,
budget: float,
interests: list[str] = None,
recipient_age: int = None,
exclude_categories: list[str] = None
) -> dict:
"""
Get personalized gift recommendations.
Args:
occasion: Birthday, anniversary, graduation, etc.
budget: Maximum budget in SGD
interests: List of interests (tech, fashion, fitness, etc.)
recipient_age: Age of recipient
exclude_categories: Categories to avoid
"""
categories = OCCASION_CATEGORIES.get(occasion.lower(), ["Electronics", "Fashion"])
recommendations = []
seen_products = set()
if interests:
for interest in interests[:3]:
keywords = INTEREST_KEYWORDS.get(interest.lower(), [interest])
for keyword in keywords[:2]:
result = search_products(keyword, max_price=budget, limit=15)
recommendations.extend(_filter_and_rank(
result.get("items", []),
budget,
exclude_categories
))
else:
for category in categories[:3]:
result = search_products(category, max_price=budget, limit=15)
recommendations.extend(_filter_and_rank(
result.get("items", []),
budget,
exclude_categories
))
unique_recommendations = []
for rec in recommendations:
if rec["id"] not in seen_products:
seen_products.add(rec["id"])
unique_recommendations.append(rec)
unique_recommendations.sort(key=lambda x: (
x.get("rating", 0) or 0,
-(float(x.get("price_sgd", x["price"])))
), reverse=True)
return {
"occasion": occasion,
"budget": budget,
"recommendations": unique_recommendations[:10],
"total_found": len(unique_recommendations)
}
def _filter_and_rank(products: list, budget: float,
exclude: list[str] = None) -> list:
"""Filter products by budget and rank by rating."""
exclude = exclude or []
filtered = []
for p in products:
price = float(p.get("price_sgd", p["price"]))
if price > budget:
continue
if p.get("category") in exclude:
continue
if not p.get("is_available", True):
continue
p["_sort_score"] = (
(p.get("rating") or 3.0) *
(1.0 / (price / budget + 0.1))
)
filtered.append(p)
return filtered
Step 3: Add Gift Card and Bundle Suggestions
def suggest_gift_bundles(primary_gift: dict, budget: float) -> list[dict]:
"""Suggest complementary items to bundle with a primary gift."""
primary_price = float(primary_gift.get("price_sgd", primary_gift["price"]))
remaining_budget = budget - primary_price
if remaining_budget < 10:
return []
category = primary_gift.get("category", "")
bundle_keywords = {
"Electronics": ["phone case", "screen protector", "power bank", "cable"],
"Fashion": ["wallet", "belt", "scarf", "cufflinks"],
"Beauty": ["makeup bag", "perfume", "skincare set"],
"Sports": ["water bottle", "gym bag", "sports socks"],
"Books": ["bookmark", "reading light", "book stand"],
}
keywords = bundle_keywords.get(category, ["accessory"])
suggestions = []
for keyword in keywords[:3]:
result = search_products(keyword, max_price=remaining_budget, limit=5)
for item in result.get("items", []):
item_price = float(item.get("price_sgd", item["price"]))
if item_price <= remaining_budget:
suggestions.append({
**item,
"bundle_with": primary_gift["name"],
"total_cost": primary_price + item_price
})
if len(suggestions) >= 3:
break
return suggestions
Step 4: Format Recommendations for Display
def format_recommendation(recommendation: dict, rank: int) -> str:
"""Format a single recommendation for display."""
name = recommendation["name"]
price = recommendation["price"]
currency = recommendation["currency"]
source = recommendation["source"]
rating = recommendation.get("rating", "N/A")
url = recommendation.get("affiliate_url", recommendation["buy_url"])
return (
f"{rank}. **{name}**\n"
f" š° {currency} {price} | ā {rating} | {source}\n"
f" š {url}"
)
def print_recommendations(result: dict):
"""Print formatted recommendations."""
print(f"\nš Gift Recommendations for {result['occasion']}")
print(f" Budget: SGD {result['budget']:.2f}")
print("=" * 60)
for i, rec in enumerate(result["recommendations"], 1):
print(format_recommendation(rec, i))
print(f"\nFound {result['total_found']} options")
Step 5: Run the Gift Recommender
if __name__ == "__main__":
result = get_gift_recommendations(
occasion="birthday",
budget=150.0,
interests=["tech", "fitness"],
recipient_age=28
)
print_recommendations(result)
if result["recommendations"]:
primary = result["recommendations"][0]
bundles = suggest_gift_bundles(primary, result["budget"])
if bundles:
print("\n" + "=" * 60)
print("š¦ Bundle Suggestions:")
for bundle in bundles:
print(
f" + {bundle['name']}: SGD {bundle['price']} "
f"(total: SGD {bundle['total_cost']:.2f})"
)
Output:
š Gift Recommendations for birthday
Budget: SGD 150.00
============================================================
1. **Sony WH-CH520 Wireless Headphones**
š° SGD 129.00 | ā 4.5 | shopee_sg
š https://api.buywhere.ai/v1/track/abc123
2. **Anker Soundcore Life Q20 Headphones**
š° SGD 89.00 | ā 4.6 | lazada_sg
š https://api.buywhere.ai/v1/track/def456
...
Found 10 options
============================================================
š¦ Bundle Suggestions:
+ Anker PowerCore 10000 Power Bank: SGD 35.00 (total: SGD 164.00)
+ JBL Tune 510BT Headphones: SGD 79.00 (total: SGD 208.00)
Step 6: Create a Gift Advisor Agent Class
class GiftAdvisor:
def __init__(self, api_key: str):
self.api_key = api_key
def ask(self, description: str) -> dict:
"""
Parse a natural language gift request.
Example: "I need a birthday gift for my 35-year-old brother
who likes gaming and cooking, under $200"
"""
prompt = f"""
Parse this gift request and extract:
- occasion
- budget
- recipient_age
- interests
- any constraints
Request: {description}
Return JSON with these fields.
"""
import json
# In production, use an LLM to parse the natural language request
# For now, we'll use simple keyword extraction
return self._simple_parse(description)
def _simple_parse(self, description: str) -> dict:
"""Simple rule-based parsing for gift requests."""
result = {
"occasion": "birthday",
"budget": 100.0,
"interests": [],
"recipient_age": None
}
desc_lower = description.lower()
if "birthday" in desc_lower:
result["occasion"] = "birthday"
elif "anniversary" in desc_lower:
result["occasion"] = "anniversary"
elif "graduation" in desc_lower:
result["occasion"] = "graduation"
import re
budget_match = re.search(r"under\s*\$?(\d+)", desc_lower)
if budget_match:
result["budget"] = float(budget_match.group(1))
age_match = re.search(r"(\d+)[- ]year[- ]old", desc_lower)
if age_match:
result["recipient_age"] = int(age_match.group(1))
for interest in ["gaming", "tech", "fitness", "cooking", "fashion", "beauty"]:
if interest in desc_lower:
result["interests"].append(interest)
return result