jbisal's picture
Update app.py
a1d5f1e verified
raw
history blame
7.1 kB
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"<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)}"}
@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()