CoolFace
Apppublic

Devansh959/Zomato-AI-Recommender

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
main.py175 linesDownload Raw Back to src
1import os2import sqlite33import pandas as pd4import json5from fastapi import FastAPI, HTTPException6from pydantic import BaseModel7from typing import Optional, List8from contextlib import asynccontextmanager9from dotenv import load_dotenv10from groq import Groq11 12load_dotenv()13 14GROQ_API_KEY = os.getenv("GROQ_API_KEY")15if GROQ_API_KEY:16    client = Groq(api_key=GROQ_API_KEY)17else:18    client = None19    print("WARNING: GROQ_API_KEY not found in .env file.")20 21df_restaurants = pd.DataFrame()22 23def get_clean_dataframe():24    db_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data', 'zomato.sqlite')25    with sqlite3.connect(db_path) as conn:26        df = pd.read_sql('SELECT * FROM restaurants', conn)27    28    df['clean_rate'] = df['rate'].astype(str).str.extract(r'(\d+\.\d+)').astype(float)29    df['clean_cost'] = df['approx_cost(for_two_people)'].astype(str).str.replace(',', '').str.extract(r'(\d+)').astype(float)30    df['clean_votes'] = pd.to_numeric(df['votes'], errors='coerce').fillna(0)31    32    return df33 34@asynccontextmanager35async def lifespan(app: FastAPI):36    global df_restaurants37    try:38        print("Loading and cleaning restaurant dataset into memory...")39        df_restaurants = get_clean_dataframe()40        print(f"Loaded {len(df_restaurants)} restaurants.")41    except Exception as e:42        print(f"Failed to load dataset: {e}")43    yield44    df_restaurants = pd.DataFrame()45 46app = FastAPI(title="Zomato Recommender API", lifespan=lifespan)47 48class UserPreferences(BaseModel):49    location: Optional[str] = None50    max_budget: Optional[float] = None51    cuisine: Optional[str] = None52    min_rating: Optional[float] = None53    additional_preferences: Optional[str] = None54 55@app.get("/api/locations")56def get_locations():57    if df_restaurants.empty:58        return {"locations": []}59    locs = df_restaurants['location'].dropna().unique().tolist()60    locs = sorted([str(loc).strip() for loc in locs if str(loc).strip()])61    return {"locations": ["Any"] + locs}62 63@app.post("/api/recommend")64def recommend_restaurants(prefs: UserPreferences):65    if df_restaurants.empty:66        raise HTTPException(status_code=500, detail="Dataset not loaded.")67    if not client:68        raise HTTPException(status_code=500, detail="Groq API key is missing. Check your .env file.")69        70    df = df_restaurants.copy()71    72    if prefs.location:73        loc_term = prefs.location.lower()74        df = df[df['location'].str.lower().str.contains(loc_term, na=False) | 75                df['listed_in(city)'].str.lower().str.contains(loc_term, na=False)]76    if prefs.cuisine:77        cuisine_term = prefs.cuisine.lower()78        df = df[df['cuisines'].str.lower().str.contains(cuisine_term, na=False)]79    if prefs.min_rating:80        df = df[df['clean_rate'] >= prefs.min_rating]81    if prefs.max_budget is not None:82        df = df[df['clean_cost'] <= prefs.max_budget]83            84    df = df.sort_values(by=['clean_rate', 'clean_votes'], ascending=[False, False])85    df = df.drop_duplicates(subset=['name'])86    87    # Get up to 15 matches to show to the user88    top_matches = df.head(15) 89    90    if len(top_matches) == 0:91        return {"recommendations": [], "message": "No restaurants found matching your exact criteria."}92    93    # Prepare MINIMAL JSON structure for Groq to save massive amounts of tokens94    context_data = []95    for _, row in top_matches.iterrows():96        context_data.append({97            "name": str(row['name']),98            "cuisines": str(row['cuisines']),99            "rating": row['clean_rate'] if pd.notnull(row['clean_rate']) else "Unknown"100        })101        102    system_prompt = """103    You are an expert Zomato restaurant recommendation AI. 104    You will receive a list of filtered restaurants and the user's exact preferences.105    Your task is to provide a personalized explanation for ALL the restaurants provided in the list.106    You MUST return ONLY a raw JSON object. Do NOT include markdown blocks like ```json.107    Format your response EXACTLY as this JSON object:108    {109      "recommendations": [110        {111          "restaurant_name": "Name",112          "ai_explanation": "A 1-2 sentence personalized explanation of why this fits the user's specific preferences."113        }114      ]115    }116    """117    118    user_prompt = f"""119    User Preferences:120    - Location: {prefs.location or 'Any'}121    - Max Budget for Two: {prefs.max_budget or 'Any'}122    - Cuisine: {prefs.cuisine or 'Any'}123    - Min Rating: {prefs.min_rating or 'Any'}124    - Additional Preferences: {prefs.additional_preferences or 'None specifically'}125    126    Here is the structured data of restaurants:127    {json.dumps(context_data, indent=2)}128    129    Please return the JSON object with explanations for all restaurants.130    """131    132    try:133        response = client.chat.completions.create(134            messages=[135                {"role": "system", "content": system_prompt},136                {"role": "user", "content": user_prompt}137            ],138            model="llama-3.1-8b-instant",139            temperature=0.2, 140            max_tokens=2000,141            response_format={"type": "json_object"}142        )143        144        ai_response_text = response.choices[0].message.content.strip()145        146        if ai_response_text.startswith("```json"):147            ai_response_text = ai_response_text[7:]148        if ai_response_text.endswith("```"):149            ai_response_text = ai_response_text[:-3]150            151        ai_json = json.loads(ai_response_text)152        ai_recs = ai_json.get("recommendations", [])153        154        # Map AI explanations back to the original full dataframe!155        exp_map = {r.get("restaurant_name"): r.get("ai_explanation") for r in ai_recs}156        157        final_results = []158        for _, row in top_matches.iterrows():159            name = str(row['name'])160            final_results.append({161                "restaurant_name": name,162                "cuisine": str(row['cuisines']),163                "rating": row['clean_rate'] if pd.notnull(row['clean_rate']) else None,164                "estimated_cost": str(row['approx_cost(for_two_people)']),165                "url": str(row['url']),166                "ai_explanation": exp_map.get(name, "A great match based on your standard filters.")167            })168            169        return {170            "total_matches_filtered": len(df),171            "recommendations": final_results172        }173    except Exception as e:174        raise HTTPException(status_code=500, detail=f"Error communicating with AI engine: {str(e)}")175