File size: 7,102 Bytes
cd99dae
9b5b26a
 
 
c19d193
cd99dae
 
 
a39af42
a1d5f1e
47fef3f
6aae614
6b2b0d0
9b5b26a
2ca8bea
 
c08f631
4b914c5
cd99dae
c08f631
 
a1d5f1e
c08f631
cd99dae
 
 
4b914c5
c08f631
cd99dae
c08f631
 
 
 
 
cd99dae
a1d5f1e
cd99dae
47fef3f
cd99dae
 
a1d5f1e
cd99dae
 
4b914c5
a1d5f1e
1ae6984
4b914c5
1ae6984
4b914c5
a1d5f1e
4b914c5
 
1ae6984
 
a1d5f1e
1ae6984
 
 
 
 
 
 
a1d5f1e
cd99dae
1ae6984
 
4b914c5
1ae6984
 
 
 
 
cd99dae
1ae6984
 
cd99dae
1ae6984
 
 
 
 
 
 
 
a1d5f1e
1ae6984
cd99dae
4b914c5
cd99dae
4b914c5
a1d5f1e
cd99dae
8c01ffb
fcb1511
a09556a
0509962
fcb1511
0509962
 
 
 
fcb1511
0509962
 
 
a1d5f1e
0509962
 
a1d5f1e
0509962
a1d5f1e
fcb1511
a09556a
a1d5f1e
fcb1511
 
 
a1d5f1e
fcb1511
43a7d4c
a1d5f1e
 
 
 
 
fcb1511
a1d5f1e
fcb1511
a1d5f1e
fcb1511
 
a1d5f1e
6aae614
cf165b2
f8537ed
27c187e
ae7a494
e121372
cd99dae
 
a1d5f1e
cd99dae
13d500a
8c01ffb
9b5b26a
 
8c01ffb
cd99dae
861422e
 
9b5b26a
cd99dae
8c01ffb
8fe992b
cd99dae
 
 
 
 
 
 
fcb1511
a1d5f1e
8c01ffb
 
cd99dae
 
b5347b6
 
8fe992b
 
cd99dae
6b2b0d0
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
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()