from smolagents import CodeAgent, DuckDuckGoSearchTool, tool, load_tool, HfApiModel import requests from bs4 import BeautifulSoup import pandas as pd import yaml import datetime import pytz from textblob import TextBlob # Simple sentiment analysis from Gradio_UI import GradioUI # For building Gradio UI #### TOOL 1: Scrape GSMArena Reviews & Specs #### @tool def scrape_gsmarena_reviews(phone_name: str) -> dict: """Scrapes GSMArena for reviews and specs of a given smartphone. Args: phone_name: The model name (exact/substantial match). Returns: Dictionary with keys: 'reviews', 'price', 'specs' """ # You’ll need accurate GSMArena URL fetching logic per phone_name. search_url = f"https://www.gsmarena.com/res.php3?sSearch={phone_name.replace(' ', '+')}" res = requests.get(search_url) soup = BeautifulSoup(res.text, "html.parser") # Find first matching phone page link link = soup.find("div", class_="makers").find("a")["href"] phone_url = f"https://www.gsmarena.com/{link}" phone_page = requests.get(phone_url) phone_soup = BeautifulSoup(phone_page.text, "html.parser") # Parse reviews and price reviews = [] for rev in phone_soup.find_all("div", class_="user-review"): reviews.append(rev.text) # Fallback if structure changes price = "Unknown" for spec in phone_soup.find_all("td", string="Price"): price = spec.find_next_sibling("td").text specs = {li.find('span').text: li.text for li in phone_soup.find_all("li")} return {"reviews": reviews, "price": price, "specs": specs} #### TOOL 2: Churn/Sentiment Scoring #### @tool def score_churn(reviews: list) -> float: """Calculates aggregate churn score from reviews using sentiment analysis. Args: reviews: List of review texts. Returns: float churn score (avg polarity; lower = less churn) """ scores = [TextBlob(r).sentiment.polarity for r in reviews] churn_score = 1 - (sum(scores) / len(scores)) if scores else 1 # Lower is better return churn_score #### TOOL 3: Find Competing Phones #### @tool def find_competitors(price: str) -> list: """Scrapes or queries GSM Arena for phones in the same price bracket. Args: price: The reference price. Returns: List of phone names. """ price_value = ''.join(filter(str.isdigit, price)) # For demo: just pick other search results in same price bracket # Expand with real price logic as needed return ["Phone1", "Phone2", "Phone3"] #### FINAL TOOL: Recommend Phone #### @tool def recommend_smartphone(user_phone: str): """Main logic for recommendation.""" user_data = scrape_gsmarena_reviews(user_phone) user_reviews = user_data['reviews'] user_score = score_churn(user_reviews) user_price = user_data['price'] competitors = find_competitors(user_price) best_score = user_score best_phone = user_phone for c in competitors: c_data = scrape_gsmarena_reviews(c) c_reviews = c_data['reviews'] c_score = score_churn(c_reviews) if c_score < best_score: best_score = c_score best_phone = c # If best_phone is not user's, recommend; else try next bracket result = { "Your phone": user_phone, "Your phone score": user_score, "Best competitor": best_phone, "Best score": best_score, "Recommended": "Yes" if best_phone != user_phone else "No better phone in range", } return result #### Gradio UI #### def agent_ui(user_phone_name): """Gradio UI interface handler.""" result = recommend_smartphone(user_phone_name) return f""" **Your phone:** {result['Your phone']} **Churn score:** {result['Your phone score']} **Best alternative:** {result['Best competitor']} (score: {result['Best score']}) **Recommended:** {result['Recommended']} """ # Connect everything for Hugging Face Space with open("prompts.yaml", 'r') as stream: prompt_templates = yaml.safe_load(stream) model = HfApiModel( max_tokens=2096, temperature=0.5, model_id='Qwen/Qwen2.5-Coder-32B-Instruct', ) agent = CodeAgent( model=model, tools=[recommend_smartphone], # add your main tool, you can add others as desired max_steps=6, verbosity_level=1, grammar=None, planning_interval=None, name=None, description=None, prompt_templates=prompt_templates ) GradioUI(agent_ui).launch()