Spaces:
Sleeping
Sleeping
| # 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) --- | |
| 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 | |
| def read_root(): return {"message": "AI Shopping Assistant Backend is running!"} |