|
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 |
|
from tools.final_answer import FinalAnswerTool |
|
from Gradio_UI import GradioUI |
|
|
|
import pdb |
|
|
|
|
|
|
|
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() |
|
|
|
soup = BeautifulSoup(response.text, 'html.parser') |
|
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 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 [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() |
|
|
|
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}") |
|
|
|
|
|
tables = pd.read_html(url) |
|
|
|
|
|
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"}] |
|
|
|
|
|
box_scores[team1] = df_team1 |
|
box_scores[team2] = df_team2 |
|
|
|
else: |
|
|
|
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', '+/-'] |
|
|
|
|
|
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)}"} |
|
|
|
|
|
final_answer = FinalAnswerTool() |
|
search_tool = DuckDuckGoSearchTool() |
|
visit_webpage_tool = VisitWebpageTool() |
|
user_input_tool = UserInputTool() |
|
|
|
import os |
|
from smolagents import OpenAIServerModel |
|
|
|
model = OpenAIServerModel( |
|
model_id="gpt-4o", |
|
api_base="https://api.openai.com/v1", |
|
api_key=os.environ["OPENAI_API_KEY"] |
|
) |
|
|
|
|
|
|
|
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True) |
|
|
|
|
|
with open("prompts.yaml", 'r') as stream: |
|
prompt_templates = yaml.safe_load(stream) |
|
|
|
|
|
|
|
|
|
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_BoxScores_Agent", |
|
description="Fetches NBA box scores and player points from last night's games.", |
|
prompt_templates=prompt_templates, |
|
additional_authorized_imports=["matplotlib"] |
|
) |
|
|
|
|
|
GradioUI(agent).launch() |
|
|