Spaces:
Running
Running
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') | |
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"<h1>(.*?) 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)}"} | |
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() | |