Spaces:
Sleeping
Sleeping
| """Simplified agent that works without LangChain - pure Python only.""" | |
| import logging | |
| from typing import Dict, Any, List, Optional | |
| import re | |
| import json | |
| from config import settings | |
| from database import db, Platform | |
| logger = logging.getLogger(__name__) | |
| class SimpleAgent: | |
| """Simplified agent for direct product suggestions without complex orchestration.""" | |
| def __init__(self): | |
| self.api_key = settings.gemini_api_key | |
| self.serpapi_key = settings.serpapi_api_key | |
| def process_simple( | |
| self, | |
| platform: str, | |
| platform_user_id: str, | |
| user_message: str | |
| ) -> Dict[str, Any]: | |
| """Process request with simple direct approach.""" | |
| try: | |
| logger.info(f"Simple agent processing: {user_message}") | |
| # Get or create user | |
| user = db.get_user(Platform(platform), platform_user_id) | |
| # Check if user is providing location (pincode or city name) | |
| if self._looks_like_location(user_message): | |
| db.upsert_user(Platform(platform), platform_user_id, location_str=user_message) | |
| return { | |
| "success": True, | |
| "response_text": "Great! Location saved. Now, what's your name?", | |
| "picks": [] | |
| } | |
| # Check profile completion | |
| if not user or not user.get("location_str"): | |
| db.upsert_user(Platform(platform), platform_user_id) | |
| return { | |
| "success": True, | |
| "response_text": "Welcome! To give you personalized product suggestions, I need a few details.\n\n📍 First, what's your location? (e.g., 'Mumbai' or '400001')", | |
| "picks": [] | |
| } | |
| if not user.get("name"): | |
| # Check if this message is a name (simple heuristic) | |
| if not any(char.isdigit() for char in user_message) and len(user_message.split()) <= 3 and not self._looks_like_location(user_message): | |
| db.upsert_user(Platform(platform), platform_user_id, name=user_message.strip()) | |
| return { | |
| "success": True, | |
| "response_text": "Nice to meet you! What's your age?", | |
| "picks": [] | |
| } | |
| return { | |
| "success": True, | |
| "response_text": "Thanks! What's your name?", | |
| "picks": [] | |
| } | |
| if not user.get("age"): | |
| # Check if this message is an age | |
| try: | |
| age = int(user_message.strip()) | |
| if 5 <= age <= 120: | |
| db.upsert_user(Platform(platform), platform_user_id, age=age) | |
| return { | |
| "success": True, | |
| "response_text": "Perfect! Now you can ask me for product suggestions.\n\n" | |
| "Examples:\n" | |
| "• 'I need a mobile under ₹6000'\n" | |
| "• 'Show me cycles under ₹8000'\n" | |
| "• 'Books under ₹300'", | |
| "picks": [] | |
| } | |
| except: | |
| pass | |
| return { | |
| "success": True, | |
| "response_text": "Great! And what's your age?", | |
| "picks": [] | |
| } | |
| # Extract category and budget | |
| category, budget = self._extract_info(user_message) | |
| if not category or not budget: | |
| return { | |
| "success": True, | |
| "response_text": "Please specify what you're looking for and your budget.\n\n" | |
| "Examples:\n" | |
| "• 'I need a mobile under ₹6000'\n" | |
| "• 'Show me cycles under ₹7000'\n" | |
| "• 'Books under ₹300'", | |
| "picks": [] | |
| } | |
| # Check cache | |
| location_key = db.make_location_key(user.get("location_str")) | |
| cached = db.cache_get(category, budget, location_key) | |
| if cached: | |
| logger.info("Using cached results") | |
| return { | |
| "success": True, | |
| "response_text": cached["response_text"], | |
| "picks": cached.get("picks_json", []) | |
| } | |
| # Get product suggestions using search | |
| picks = self._search_products(category, budget, user.get("location_str", "India")) | |
| if not picks: | |
| return { | |
| "success": True, | |
| "response_text": f"I couldn't find any {category} products under ₹{budget}. Try:\n" | |
| f"• Increasing your budget\n" | |
| f"• Checking the spelling\n" | |
| f"• Being more specific", | |
| "picks": [] | |
| } | |
| # Format response | |
| top_picks = picks[:3] | |
| response = f"Top Picks: {', '.join([p['name'] for p in top_picks])}\n\n" | |
| response += f"Here are 3 great {category} options under ₹{budget} for you:\n\n" | |
| for i, pick in enumerate(top_picks, 1): | |
| response += f"{i}. {pick['name']}\n" | |
| response += f" Price: ₹{pick['price']}\n" | |
| if pick.get('rating'): | |
| response += f" Rating: {pick['rating']}⭐\n" | |
| response += "\n" | |
| # Cache the results | |
| picks_json = [{"name": p["name"], "rank": i+1} for i, p in enumerate(top_picks)] | |
| db.cache_put( | |
| user_id=user["id"], | |
| category=category, | |
| budget_inr=budget, | |
| location_key=location_key, | |
| response_text=response, | |
| picks_json=picks_json, | |
| ttl_hours=24 | |
| ) | |
| return { | |
| "success": True, | |
| "response_text": response, | |
| "picks": picks_json | |
| } | |
| except Exception as e: | |
| logger.error(f"Simple agent error: {e}", exc_info=True) | |
| return { | |
| "success": False, | |
| "error": str(e), | |
| "response_text": "Sorry, something went wrong. Please try again.", | |
| "picks": [] | |
| } | |
| def _extract_info(self, message: str) -> tuple: | |
| """Extract category and budget from message.""" | |
| message_lower = message.lower() | |
| # Extract category | |
| category = None | |
| if any(word in message_lower for word in ['mobile', 'phone', 'smartphone']): | |
| category = 'mobile' | |
| elif any(word in message_lower for word in ['cycle', 'bicycle', 'bike']): | |
| category = 'cycle' | |
| elif any(word in message_lower for word in ['book', 'books']): | |
| category = 'books' | |
| # Extract budget | |
| budget = None | |
| patterns = [ | |
| r'₹\s*(\d+k?)', | |
| r'(?:under|below|max|maximum|budget)\s*[:\s]*₹?\s*(\d+k?)', | |
| r'(\d+k?)\s*(?:rupees|inr|rs|₹)', | |
| r'(\d+k?)', # Just a number | |
| ] | |
| for pattern in patterns: | |
| match = re.search(pattern, message_lower) | |
| if match: | |
| budget_str = match.group(1) | |
| if 'k' in budget_str: | |
| budget = int(budget_str.replace('k', '')) * 1000 | |
| else: | |
| budget = int(budget_str) | |
| break | |
| logger.info(f"Extracted: category={category}, budget={budget}") | |
| return category, budget | |
| def _looks_like_location(self, message: str) -> bool: | |
| """Check if message looks like a location (pincode or city name).""" | |
| message = message.strip() | |
| # Check if it's a 6-digit pincode | |
| if message.isdigit() and len(message) == 6: | |
| return True | |
| # Check if it's a city/location name (no numbers, reasonable length) | |
| if not any(char.isdigit() for char in message) and 3 <= len(message) <= 50: | |
| # Exclude common greetings | |
| greetings = ['hi', 'hello', 'hey', 'thanks', 'ok', 'yes', 'no'] | |
| if message.lower() not in greetings: | |
| return True | |
| return False | |
| def _search_products(self, category: str, budget: int, location: str) -> List[Dict]: | |
| """Search for products using SerpApi.""" | |
| try: | |
| import serpapi | |
| query = f"{category} under {budget} INR" | |
| logger.info(f"Searching: {query}") | |
| client = serpapi.Client(api_key=settings.serpapi_api_key) | |
| results = client.search({ | |
| "engine": "google_shopping", | |
| "q": query, | |
| "gl": "in", | |
| "hl": "en", | |
| "num": 10 | |
| }) | |
| candidates = [] | |
| shopping_results = results.get("shopping_results", []) | |
| for item in shopping_results: | |
| try: | |
| price_str = item.get("extracted_price", item.get("price", "0")) | |
| if isinstance(price_str, str): | |
| price = float(price_str.replace(",", "").replace("₹", "").strip()) | |
| else: | |
| price = float(price_str) | |
| if price > 0 and price <= budget: | |
| candidates.append({ | |
| "name": item.get("title", "Unknown"), | |
| "price": int(price), | |
| "rating": item.get("rating", 0), | |
| "source": item.get("source", ""), | |
| "link": item.get("link", "") | |
| }) | |
| except Exception as e: | |
| logger.debug(f"Skipping item: {e}") | |
| continue | |
| # Sort by rating, then price | |
| candidates.sort(key=lambda x: (x.get("rating", 0), -x["price"]), reverse=True) | |
| logger.info(f"Found {len(candidates)} products") | |
| return candidates | |
| except Exception as e: | |
| logger.error(f"Search error: {e}") | |
| return [] | |
| simple_agent = SimpleAgent() | |