from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, load_tool, tool, VisitWebpageTool, UserInputTool import datetime import requests import pytz import yaml import matplotlib.pyplot as plt import pandas as pd import re import logging import inspect from bs4 import BeautifulSoup # Fixed Import from tools.final_answer import FinalAnswerTool from Gradio_UI import GradioUI logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') @tool def get_box_score_links() -> list: """A tool that fetches the URLs to the boxscores for each of last night's NBA games.""" BASE_URL = "https://www.basketball-reference.com" MAIN_URL = f"{BASE_URL}/boxscores/" logging.debug(f"[Line {inspect.currentframe().f_lineno}] Fetching box score links from: {MAIN_URL}") try: response = requests.get(MAIN_URL) response.raise_for_status() # Raise exception for HTTP errors soup = BeautifulSoup(response.text, 'html.parser') box_score_links = [] # Find all box score links for link in soup.find_all('a', href=True): href = link['href'] if '/boxscores/' in href and href.endswith('.html') and 's/202' in href: box_score_links.append(BASE_URL + href) logging.info(f"[Line {inspect.currentframe().f_lineno}] Found {len(box_score_links)} box score links.") # Return unique links while preserving order return list(dict.fromkeys(box_score_links)) except requests.exceptions.RequestException as e: logging.error(f"[Line {inspect.currentframe().f_lineno}] Error fetching boxScore links: {str(e)}") # Return error message as a list to maintain consistent return type return [f"Error fetching boxScore links: {str(e)}"] def get_box_score_data(url: str) -> dict: """A tool that fetches the boxscores data from a provided URL. Args: url: A string representing the URL to a box score of an nba game from last night. """ logging.debug(f"[Line {inspect.currentframe().f_lineno}] Fetching box score data from URL: {url}") try: box_scores = {} response = requests.get(url) response.raise_for_status() # Raise exception for HTTP errors soup = BeautifulSoup(response.text, 'html.parser') pattern = r"

(.*?) at (.*?) Box Score" match = re.search(pattern, str(soup.find('div', id="content").find('h1'))) if match: team1 = match.group(1) team2 = match.group(2) logging.info(f"[Line {inspect.currentframe().f_lineno}] Game found: {team1} vs {team2}") # Read HTML tables tables = pd.read_html(url) # Check if the expected tables exist before accessing if len(tables) > 0: df_team1 = tables[0].to_dict(orient='records') else: df_team1 = [{"Error": "Team 1 data not found"}] if len(tables) > 8: df_team2 = tables[8].to_dict(orient='records') else: df_team2 = [{"Error": "Team 2 data not found"}] # Store box score data box_scores[team1] = df_team1 box_scores[team2] = df_team2 else: # If regex pattern did not match logging.warning(f"[Line {inspect.currentframe().f_lineno}] Team names not found in the page title for URL: {url}") box_scores[url] = [{"Error": "Team names not found in the page title"}] return box_scores except Exception as e: logging.error(f"[Line {inspect.currentframe().f_lineno}] Error fetching boxScore data: {str(e)}") return {"Error": f"Error fetching boxScore data: {str(e)}"} @tool def get_stats_from_boxScore_data(url: str, stat: str) -> dict: """A tool that fetches the player names and box score statistic for the provided box score url. Args: url: A string representing the URL to the box score for a game. stat: A string representing the statistic that this function will return for each player in the box score. Must be one of: 'MP', 'FG', 'FGA', 'FG%', '3P', '3PA', '3P%', 'FT', 'FTA', 'FT%', 'ORB', 'DRB', 'TRB', 'AST', 'STL', 'BLK', 'TOV', 'PF', 'PTS', 'GmSc', '+/-' """ allowed_stats = ['MP', 'FG', 'FGA', 'FG%', '3P', '3PA', '3P%', 'FT', 'FTA', 'FT%', 'ORB', 'DRB', 'TRB', 'AST', 'STL', 'BLK', 'TOV', 'PF', 'PTS', 'GmSc', '+/-'] # Check if stat is valid if stat not in allowed_stats: logging.error(f"[Line {inspect.currentframe().f_lineno}] Invalid stat requested: {stat}") return {"Error": f"Invalid stat '{stat}'. Allowed values are: {', '.join(allowed_stats)}"} try: box_scores = get_box_score_data(url) logging.debug(f"[Line {inspect.currentframe().f_lineno}] Box Scores: {box_scores}") stats = {} stat_key = ('Basic Box Score Stats', stat) for teams in box_scores.keys(): logging.debug(f"[Line {inspect.currentframe().f_lineno}] Processing team: {teams}") for player in box_scores[teams]: if stat_key in player: if player[stat_key] is not None and player[stat_key].replace('.', '').isdigit(): if player[list(player.keys())[0]] != "Team Totals": stats[player[list(player.keys())[0]]] = pd.to_numeric(player[stat_key], errors='coerce') logging.info(f"[Line {inspect.currentframe().f_lineno}] Stats for {stat}: {stats}") return stats except Exception as e: logging.error(f"[Line {inspect.currentframe().f_lineno}] Error fetching boxScore data for given statistic: {str(e)}") return {"Error": f"Error fetching boxScore data for given statistic: {str(e)}"} # Instantiate Tools and Model final_answer = FinalAnswerTool() search_tool = DuckDuckGoSearchTool() visit_webpage_tool = VisitWebpageTool() user_input_tool = UserInputTool() model = HfApiModel( max_tokens=2096, temperature=0.5, model_id='Qwen/Qwen2.5-Coder-32B-Instruct', custom_role_conversions=None, ) # Import tool from Hub image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True) # Load prompt templates with open("prompts.yaml", 'r') as stream: prompt_templates = yaml.safe_load(stream) # Setup the Agent agent = CodeAgent( model=model, tools=[ final_answer, image_generation_tool, search_tool, visit_webpage_tool, user_input_tool, get_box_score_links, get_stats_from_boxScore_data ], max_steps=6, verbosity_level=1, name="NBA Box Scores Agent", description="Fetches NBA box scores and player points from last night's games.", prompt_templates=prompt_templates, additional_authorized_imports=["matplotlib"] ) # Launch Gradio UI GradioUI(agent).launch()