| 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 |
| from Gradio_UI import GradioUI |
|
|
| |
| @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' |
| """ |
| |
| search_url = f"https://www.gsmarena.com/res.php3?sSearch={phone_name.replace(' ', '+')}" |
| res = requests.get(search_url) |
| soup = BeautifulSoup(res.text, "html.parser") |
| |
| 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") |
| |
| reviews = [] |
| for rev in phone_soup.find_all("div", class_="user-review"): |
| reviews.append(rev.text) |
| |
| 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 |
| 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 |
| return churn_score |
|
|
| |
| @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)) |
| |
| |
| return ["Phone1", "Phone2", "Phone3"] |
|
|
| |
| @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 |
|
|
| |
| 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 |
|
|
| |
| 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']} |
| """ |
|
|
| |
| 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], |
| max_steps=6, |
| verbosity_level=1, |
| grammar=None, |
| planning_interval=None, |
| name=None, |
| description=None, |
| prompt_templates=prompt_templates |
| ) |
|
|
| GradioUI(agent_ui).launch() |
|
|