# main.py import os import asyncio from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel import uvicorn from serpapi import GoogleSearch from openai import OpenAI import json # --- Securely get API keys from environment variables (provided by Cloud Run) --- OPENROUTER_API_KEY = os.environ.get('OPENROUTER_API_KEY') SERPAPI_API_KEY = os.environ.get('SERPAPI_API_KEY') app = FastAPI(title="AI Shopping Assistant API") app.add_middleware( CORSMiddleware, allow_origins=["*"], # Allows all origins allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) class UserQuery(BaseModel): product_name: str description: str budget: float # ========================================================================= # === PASTE YOUR 'search_with_serpapi' FUNCTION FROM COLAB HERE === # ========================================================================= def search_with_serpapi(search_query: str): """Performs a search on Google Shopping (India) using the SerpApi.""" print(f"🕵️‍♂️ Searching Google Shopping with SerpApi for: '{search_query}'") try: search = GoogleSearch({ "engine": "google_shopping", "q": search_query, "api_key": SERPAPI_API_KEY, "gl": "in", # Geolocation: India "hl": "en", # Host Language: English "currency": "INR" # Currency: Indian Rupee }) results = search.get_dict() shopping_results = results.get("shopping_results", []) if not shopping_results: return [] # Extract the same detailed information your frontend expects top_results = [] for item in shopping_results[:7]: # Get top 7 results for the AI to choose from price = item.get('extracted_price') # This field is usually just the number if not price: continue top_results.append({ "title": item.get("title"), "price": price, "source": item.get("source"), # e.g., "Amazon.in", "Flipkart" "rating": item.get("rating"), "reviews_count": item.get("reviews"), "image_url": item.get("thumbnail"), # Use thumbnail for the image "link": item.get("link"), "features": [] # SerpApi doesn't provide feature bullets in the same way }) print(f"✅ Found {len(top_results)} products from Google Shopping via SerpApi.") return top_results except Exception as e: print(f"❌ Error during SerpApi search: {e}") return None # ========================================================================= # === PASTE YOUR 'get_llm_recommendations' FUNCTION FROM COLAB HERE === # ========================================================================= def get_llm_recommendations(user_query: UserQuery, product_data: list): """Generates a complex JSON object tailored for the React frontend.""" print("🧠 Sending SerpApi data to LLM for analysis...") client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key=OPENROUTER_API_KEY, ) # --- Step 3: Updated Prompt --- # The prompt is updated to reflect the new, broader data source prompt = f""" You are an expert virtual shopping assistant. Your goal is to generate a structured JSON response to power a rich user interface. My Request: - Product: "{user_query.product_name}" - Needs: "{user_query.description}" - Budget: "Under ₹{user_query.budget} INR" Detailed Product Data from various online stores in India (via Google Shopping): {json.dumps(product_data, indent=2)} Based on my request and this data, return a single JSON object with the following exact keys: "summary", "comparison", "products", and "completeTheLook". 1. "summary": (String) A concise, friendly summary of your findings and top recommendation. 2. "comparison": (Object) A comparison between your top two recommendations. Must have "headers" and "rows" keys. 3. "products": (Array of Objects) A list of your top 2-3 recommended products. Each object must have these exact keys: "id", "name", "brand", "price", "rating", "image", and "link". Infer the brand from the title. 4. "completeTheLook": (Object) A suggestion for a complementary product. Must have "item" and "link" keys. Do not include any text or markdown outside of the main JSON object. """ # ------------------------------- try: response = client.chat.completions.create( model="google/gemini-flash-1.5", response_format={"type": "json_object"}, messages=[{"role": "user", "content": prompt}], temperature=0.6, ) print("✅ LLM response for React UI received.") return json.loads(response.choices[0].message.content) except Exception as e: print(f"❌ Error during OpenRouter API call: {e}") raise HTTPException(status_code=500, detail=f"Error communicating with the AI model: {e}") # --- API Endpoint (This part is already complete) --- @app.post("/search") async def search_products_endpoint(query: UserQuery): search_query = f"{query.product_name} {query.description}" product_data = search_with_serpapi(search_query) if product_data is None: raise HTTPException(status_code=503, detail="The product search service (SerpApi) failed.") if not product_data: raise HTTPException(status_code=404, detail="Could not find any products matching your query.") recommendation_json = get_llm_recommendations(query, product_data) return recommendation_json @app.get("/") def read_root(): return {"message": "AI Shopping Assistant Backend is running!"}