File size: 7,194 Bytes
ea2fe88
 
 
 
 
 
 
 
 
 
 
 
 
 
4e33a26
 
 
 
ea2fe88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4e33a26
 
 
 
 
 
 
ea2fe88
 
4e33a26
ea2fe88
 
 
 
 
 
 
4e33a26
 
ea2fe88
 
 
 
 
 
 
 
 
 
 
 
 
 
5de4b4b
ea2fe88
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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

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()  # 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()

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"] 
)

 
# 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)
    
# pdb.set_trace() 

# 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_BoxScores_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()