Spaces:
No application file
No application file
Liss, Alex (NYC-HUG)
commited on
Commit
·
4c2914b
1
Parent(s):
9e940d2
cleaned up documentation
Browse files- components/player_card_component.py +158 -0
- docs/{game_recap_implementation_instructions.md → Phase 1/Task 1.2.1 Game Recap Search Implementation.md} +141 -15
- docs/Phase 1/Task 1.2.2 Player Search Implementation.md +299 -0
- docs/requirements.md +6 -104
- gradio_agent.py +21 -12
- gradio_app.py +104 -148
- prompts.py +26 -17
- test.ipynb +246 -0
- tools/player_search.py +259 -0
components/player_card_component.py
ADDED
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import html
|
3 |
+
|
4 |
+
def create_player_card_component(player_data=None):
|
5 |
+
"""
|
6 |
+
Creates a Gradio HTML component to display player information based on test.ipynb structure.
|
7 |
+
|
8 |
+
Args:
|
9 |
+
player_data (dict, optional): Dictionary containing player data.
|
10 |
+
Expected keys: 'Name', 'Position', 'Jersey_number',
|
11 |
+
'headshot_url', 'College', 'Height', 'Weight',
|
12 |
+
'Years_in_nfl', 'instagram_url'. Defaults to None.
|
13 |
+
|
14 |
+
Returns:
|
15 |
+
gr.HTML: A Gradio HTML component displaying the player card, or empty if no data.
|
16 |
+
"""
|
17 |
+
print("--- Entered create_player_card_component ---") # DEBUG LOG
|
18 |
+
if not player_data or not isinstance(player_data, dict):
|
19 |
+
print("Component received no player data, returning empty.") # DEBUG LOG
|
20 |
+
return gr.HTML("")
|
21 |
+
|
22 |
+
print(f"Component received player_data: {player_data}") # DEBUG LOG
|
23 |
+
|
24 |
+
try:
|
25 |
+
# Extract data with defaults, using html.escape
|
26 |
+
name = html.escape(player_data.get('Name', 'N/A'))
|
27 |
+
position = html.escape(player_data.get('Position', ''))
|
28 |
+
number = html.escape(str(player_data.get('Jersey_number', '')))
|
29 |
+
headshot_url = html.escape(player_data.get('headshot_url', ''))
|
30 |
+
college = html.escape(player_data.get('College', 'N/A'))
|
31 |
+
height = html.escape(player_data.get('Height', 'N/A'))
|
32 |
+
weight = html.escape(player_data.get('Weight', 'N/A'))
|
33 |
+
exp = html.escape(str(player_data.get('Years_in_nfl', 'N/A')))
|
34 |
+
instagram_url = html.escape(player_data.get('instagram_url', ''))
|
35 |
+
|
36 |
+
# CSS from test.ipynb, adapted slightly for theme integration
|
37 |
+
css = """
|
38 |
+
<style>
|
39 |
+
.player-card-container {
|
40 |
+
/* Add a container to allow for centering or specific placement */
|
41 |
+
padding: 10px 0;
|
42 |
+
}
|
43 |
+
.player-card {
|
44 |
+
background-color: #222222; /* Dark background */
|
45 |
+
border: 1px solid #333333;
|
46 |
+
margin: 0.5rem auto; /* Center card */
|
47 |
+
padding: 1rem;
|
48 |
+
border-radius: 8px;
|
49 |
+
width: 320px; /* Slightly wider */
|
50 |
+
box-sizing: border-box;
|
51 |
+
font-family: 'Arial', sans-serif;
|
52 |
+
color: #E6E6E6; /* Light text */
|
53 |
+
overflow: hidden; /* Clear float */
|
54 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
55 |
+
}
|
56 |
+
.player-photo {
|
57 |
+
width: 80px;
|
58 |
+
height: 80px;
|
59 |
+
background-color: #444; /* Darker placeholder */
|
60 |
+
float: left;
|
61 |
+
margin-right: 1rem;
|
62 |
+
border-radius: 50%; /* Make it round */
|
63 |
+
object-fit: cover; /* Cover ensures image fills circle */
|
64 |
+
border: 2px solid #B3995D; /* Gold accent */
|
65 |
+
}
|
66 |
+
.card-content {
|
67 |
+
float: left;
|
68 |
+
width: calc(100% - 100px); /* Adjust width considering photo and margin */
|
69 |
+
}
|
70 |
+
.card-content h4 {
|
71 |
+
margin: 0 0 0.5rem 0;
|
72 |
+
font-size: 1.2em;
|
73 |
+
color: #FFFFFF; /* White name */
|
74 |
+
font-weight: bold;
|
75 |
+
}
|
76 |
+
.card-content ul {
|
77 |
+
margin: 0.25rem 0;
|
78 |
+
padding-left: 1.2rem;
|
79 |
+
list-style: none; /* Remove default bullets */
|
80 |
+
font-size: 0.9em;
|
81 |
+
color: #B0B0B0;
|
82 |
+
}
|
83 |
+
.card-content ul li {
|
84 |
+
margin-bottom: 4px; /* Space between list items */
|
85 |
+
}
|
86 |
+
.player-social-link {
|
87 |
+
margin-top: 10px;
|
88 |
+
clear: both; /* Ensure it appears below floated elements if needed */
|
89 |
+
}
|
90 |
+
.player-social-link a {
|
91 |
+
display: inline-block;
|
92 |
+
text-decoration: none;
|
93 |
+
color: #FFFFFF;
|
94 |
+
background-color: #AA0000; /* 49ers Red */
|
95 |
+
padding: 5px 10px;
|
96 |
+
border-radius: 4px;
|
97 |
+
font-size: 0.8em;
|
98 |
+
transition: background-color 0.2s ease;
|
99 |
+
}
|
100 |
+
.player-social-link a:hover {
|
101 |
+
background-color: #B3995D; /* Gold accent on hover */
|
102 |
+
}
|
103 |
+
</style>
|
104 |
+
"""
|
105 |
+
|
106 |
+
# HTML structure based on test.ipynb
|
107 |
+
# Using an outer container for better layout control in Gradio
|
108 |
+
html_content = f"""
|
109 |
+
{css}
|
110 |
+
<div class="player-card-container">
|
111 |
+
<div class="player-card">
|
112 |
+
<img src="{headshot_url}" class="player-photo" alt="{name} Headshot" onerror="this.style.display='none'" />
|
113 |
+
<div class="card-content">
|
114 |
+
<h4>{name} {f'- #{number}' if number else ''} {f'({position})' if position else ''}</h4>
|
115 |
+
<ul>
|
116 |
+
<li>Ht: {height} | Wt: {weight} lbs</li>
|
117 |
+
<li>College: {college}</li>
|
118 |
+
<li>Experience: {exp} Years</li>
|
119 |
+
</ul>
|
120 |
+
"""
|
121 |
+
# Add Instagram link conditionally
|
122 |
+
if instagram_url:
|
123 |
+
html_content += f"""
|
124 |
+
<div class="player-social-link">
|
125 |
+
<a href="{instagram_url}" target="_blank">Instagram Profile</a>
|
126 |
+
</div>
|
127 |
+
"""
|
128 |
+
|
129 |
+
html_content += """
|
130 |
+
</div>
|
131 |
+
</div>
|
132 |
+
</div>
|
133 |
+
"""
|
134 |
+
|
135 |
+
print(f"Component generated HTML (first 100 chars): {html_content[:100]}...") # DEBUG LOG
|
136 |
+
return gr.HTML(html_content)
|
137 |
+
|
138 |
+
except Exception as e:
|
139 |
+
print(f"Error creating player card component: {str(e)}")
|
140 |
+
# Return a simple error message component
|
141 |
+
return gr.HTML("<div style='padding: 1rem; color: red;'>⚠️ Error loading player card.</div>")
|
142 |
+
|
143 |
+
# Example Usage (for testing component independently if needed)
|
144 |
+
# if __name__ == '__main__':
|
145 |
+
# example_data = {
|
146 |
+
# 'Name': 'Brock Purdy',
|
147 |
+
# 'headshot_url': 'https://a.espncdn.com/i/headshots/nfl/players/full/4433216.png', # Example URL
|
148 |
+
# 'instagram_url': 'https://www.instagram.com/brock.purdy13/',
|
149 |
+
# 'Position': 'QB',
|
150 |
+
# 'Number': '13'
|
151 |
+
# }
|
152 |
+
# component = create_player_card_component(example_data)
|
153 |
+
#
|
154 |
+
# with gr.Blocks() as demo:
|
155 |
+
# gr.Markdown("## Player Card Example")
|
156 |
+
# demo.add(component)
|
157 |
+
#
|
158 |
+
# demo.launch()
|
docs/{game_recap_implementation_instructions.md → Phase 1/Task 1.2.1 Game Recap Search Implementation.md}
RENAMED
@@ -12,11 +12,46 @@ When writing code, your focus should be on creating new functionality that build
|
|
12 |
If the data files and code you need to use as inputs to complete your task do not conform to the structure you expected based on the instructions, please pause your work and ask the human for review and guidance on how to proceed.
|
13 |
|
14 |
## Objective
|
15 |
-
|
16 |
|
17 |
## Implementation Steps
|
18 |
|
19 |
-
### 1.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
1. Review the current Neo4j schema for games nodes
|
21 |
2. Create a new subfolder in the 'new_final_april 11' directory for the Neo4j update script
|
22 |
3. Use neo4j_ingestion.py as a reference for previous Neo4j uploads
|
@@ -30,7 +65,7 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
30 |
7. Request user confirmation in the cloud interface
|
31 |
8. Document the updated schema for future reference
|
32 |
|
33 |
-
###
|
34 |
1. Review existing LangChain <-> Neo4j integration functions in agent.py and cypher.py
|
35 |
2. Create a new LangChain function specific to game recap search:
|
36 |
- Define clear tool description for LangChain recognition
|
@@ -48,7 +83,7 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
48 |
5. IMPORTANT: Do NOT use the vector search functionality in tools/vector.py for game recap generation
|
49 |
6. Use the LLM to generate game recaps based on structured data returned from Cypher queries
|
50 |
|
51 |
-
###
|
52 |
1. Analyze current game_recap_component.py implementation
|
53 |
2. Identify static elements that need to be made dynamic
|
54 |
3. Create new function structure:
|
@@ -61,7 +96,7 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
61 |
8. IMPORTANT: The component should NOT be pinned to the top of the app as a static element
|
62 |
9. Instead, implement it as a dynamic component that can be called in response to user queries
|
63 |
|
64 |
-
###
|
65 |
1. Review current gradio_app.py implementation
|
66 |
2. Remove the static game recap component from the top of the app
|
67 |
3. Update app architecture:
|
@@ -72,7 +107,13 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
72 |
6. Add feedback mechanism for user queries
|
73 |
7. Implement session persistence for game context
|
74 |
|
75 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
1. Test Neo4j data updates
|
77 |
2. Verify LangChain query generation
|
78 |
3. Test component rendering with various game data
|
@@ -148,9 +189,75 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
148 |
|
149 |
## Implementation Log
|
150 |
|
151 |
-
### Step 1:
|
152 |
|
153 |
-
**Date Completed:**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
|
155 |
**Actions Performed:**
|
156 |
1. Created a new directory for the Neo4j update script:
|
@@ -186,6 +293,9 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
186 |
- 17 games with team logo URLs
|
187 |
- 15 games with highlight video URLs
|
188 |
|
|
|
|
|
|
|
189 |
**Challenges and Solutions:**
|
190 |
- Initially had issues with the location of the .env file. Fixed by updating the script to look in the correct location (ifx-sandbox/.env).
|
191 |
- Added command-line flag (--yes) for non-interactive execution.
|
@@ -196,9 +306,9 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
196 |
3. URLs provided in the CSV file are valid and accessible.
|
197 |
4. The script should only update existing nodes, not create new ones.
|
198 |
|
199 |
-
### Step
|
200 |
|
201 |
-
**Date Completed:**
|
202 |
|
203 |
**Actions Performed:**
|
204 |
1. Created a new `game_recap.py` file in the tools directory with these components:
|
@@ -222,9 +332,12 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
222 |
- Created gradio_agent.py that doesn't rely on Streamlit
|
223 |
- Added proper imports to allow tools to find these modules
|
224 |
|
225 |
-
|
|
|
226 |
|
227 |
-
|
|
|
|
|
228 |
|
229 |
**Actions Performed:**
|
230 |
1. Enhanced game_recap_component.py:
|
@@ -248,9 +361,9 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
248 |
- Created fallbacks for common teams and games
|
249 |
- Added debugging logs for tracking game data
|
250 |
|
251 |
-
### Step
|
252 |
|
253 |
-
**Date Completed:**
|
254 |
|
255 |
**Actions Performed:**
|
256 |
1. Updated gradio_app.py:
|
@@ -271,7 +384,20 @@ Refactor the game_recap_component.py and underlying code so that the game recap
|
|
271 |
- Implemented data caching to preserve structured data during agent processing
|
272 |
- Addressed HTML rendering issues by using proper Gradio components
|
273 |
|
274 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
275 |
|
276 |
**Test Cases:**
|
277 |
1. **Neo4j Database Update:**
|
|
|
12 |
If the data files and code you need to use as inputs to complete your task do not conform to the structure you expected based on the instructions, please pause your work and ask the human for review and guidance on how to proceed.
|
13 |
|
14 |
## Objective
|
15 |
+
Implement the Game Recap Search feature (Feature 3 from Feature Overview) with focus on game recap display functionality. Then refactor the game_recap_component.py and underlying code so that the game recap function can be called by the user and displayed as a dynamic UI element – instead of the current build in which it is 'pinned' to the top of the gradio app and appears statically. The user will be able to ask the app a question about a specific game and get the result including a text summary of the result as well as a visual UI component.
|
16 |
|
17 |
## Implementation Steps
|
18 |
|
19 |
+
### 1. CSS Integration
|
20 |
+
1. Add required CSS styles to the Gradio app
|
21 |
+
2. Ensure styles support responsive layout
|
22 |
+
3. Implement 49ers theme colors from Design System section
|
23 |
+
|
24 |
+
### 2. Data Requirements Enhancement
|
25 |
+
1. Review existing game score & result data
|
26 |
+
2. Identify home team name and logo source
|
27 |
+
3. Identify away team name and logo source
|
28 |
+
4. Document data structure requirements
|
29 |
+
|
30 |
+
### 3. CSV File Update
|
31 |
+
1. Open `schedule_with_result_april_11.csv`
|
32 |
+
2. Add columns for home team logo
|
33 |
+
3. Add columns for away team logo
|
34 |
+
4. Merge data from `nfl_team_logos_revised.csv`
|
35 |
+
5. Validate data integrity
|
36 |
+
6. Save as new version
|
37 |
+
|
38 |
+
### 4. Static Gradio Component Development
|
39 |
+
1. Create new component file
|
40 |
+
2. Implement layout matching `game recap layout example.png`:
|
41 |
+
- Top row: away team elements
|
42 |
+
- Bottom row: home team elements
|
43 |
+
- Score display with winning team highlight
|
44 |
+
- Video preview box
|
45 |
+
3. Use static assets for 49ers first game
|
46 |
+
4. Implement responsive design
|
47 |
+
|
48 |
+
### 5. Component Testing
|
49 |
+
1. Add component as first element in Gradio app
|
50 |
+
2. Test CSV data integration
|
51 |
+
3. Verify static display
|
52 |
+
4. Document any display issues
|
53 |
+
|
54 |
+
### 6. Neo4j Database Update
|
55 |
1. Review the current Neo4j schema for games nodes
|
56 |
2. Create a new subfolder in the 'new_final_april 11' directory for the Neo4j update script
|
57 |
3. Use neo4j_ingestion.py as a reference for previous Neo4j uploads
|
|
|
65 |
7. Request user confirmation in the cloud interface
|
66 |
8. Document the updated schema for future reference
|
67 |
|
68 |
+
### 7. LangChain Integration
|
69 |
1. Review existing LangChain <-> Neo4j integration functions in agent.py and cypher.py
|
70 |
2. Create a new LangChain function specific to game recap search:
|
71 |
- Define clear tool description for LangChain recognition
|
|
|
83 |
5. IMPORTANT: Do NOT use the vector search functionality in tools/vector.py for game recap generation
|
84 |
6. Use the LLM to generate game recaps based on structured data returned from Cypher queries
|
85 |
|
86 |
+
### 8. Component Refactoring
|
87 |
1. Analyze current game_recap_component.py implementation
|
88 |
2. Identify static elements that need to be made dynamic
|
89 |
3. Create new function structure:
|
|
|
96 |
8. IMPORTANT: The component should NOT be pinned to the top of the app as a static element
|
97 |
9. Instead, implement it as a dynamic component that can be called in response to user queries
|
98 |
|
99 |
+
### 9. Gradio App Integration
|
100 |
1. Review current gradio_app.py implementation
|
101 |
2. Remove the static game recap component from the top of the app
|
102 |
3. Update app architecture:
|
|
|
107 |
6. Add feedback mechanism for user queries
|
108 |
7. Implement session persistence for game context
|
109 |
|
110 |
+
### 10. Final Deployment
|
111 |
+
1. Deploy to HuggingFace Spaces
|
112 |
+
2. Perform final UI checks
|
113 |
+
3. Verify data accuracy
|
114 |
+
4. Document any issues
|
115 |
+
|
116 |
+
### 11. Testing and Validation
|
117 |
1. Test Neo4j data updates
|
118 |
2. Verify LangChain query generation
|
119 |
3. Test component rendering with various game data
|
|
|
189 |
|
190 |
## Implementation Log
|
191 |
|
192 |
+
### Step 1: CSS Integration ✅
|
193 |
|
194 |
+
**Date Completed:** April 15, 2025
|
195 |
+
|
196 |
+
**Actions Performed:**
|
197 |
+
- Added required CSS styles to the Gradio app
|
198 |
+
- Ensured styles support responsive layout
|
199 |
+
- Implemented 49ers theme colors from Design System section
|
200 |
+
|
201 |
+
**Implementation Details:**
|
202 |
+
CSS styles were embedded directly in the Gradio app as a string variable, ensuring compatibility with both local development and Hugging Face Spaces deployment. The implementation includes comprehensive styling for all UI components with the 49ers theme colors.
|
203 |
+
|
204 |
+
### Step 2: Data Requirements Enhancement ✅
|
205 |
+
|
206 |
+
**Date Completed:** April 15, 2025
|
207 |
+
|
208 |
+
**Actions Performed:**
|
209 |
+
- Reviewed existing game score & result data
|
210 |
+
- Identified home team name and logo source
|
211 |
+
- Identified away team name and logo source
|
212 |
+
- Documented data structure requirements
|
213 |
+
|
214 |
+
**Implementation Details:**
|
215 |
+
Analyzed the schedule CSV file and identified that home team names are in the "Home Team" column and away team names are in the "Away Team" column. Logo sources were identified in the "logo_url" column of the team logos CSV file, which provides direct URLs to team logos from ESPN's CDN.
|
216 |
+
|
217 |
+
### Step 3: CSV File Update ✅
|
218 |
+
|
219 |
+
**Date Completed:** April 15, 2025
|
220 |
+
|
221 |
+
**Actions Performed:**
|
222 |
+
- Opened `schedule_with_result_april_11.csv`
|
223 |
+
- Added columns for home team logo
|
224 |
+
- Added columns for away team logo
|
225 |
+
- Merged data from `nfl_team_logos_revised.csv`
|
226 |
+
- Validated data integrity
|
227 |
+
- Saved as new version
|
228 |
+
|
229 |
+
**Implementation Details:**
|
230 |
+
Created a Python script to merge the schedule data with team logo URLs. The script maps team names to their corresponding logo URLs and adds two new columns to the schedule CSV: 'home_team_logo_url' and 'away_team_logo_url'. The merged data was saved as 'schedule_with_result_and_logo_urls.csv'.
|
231 |
+
|
232 |
+
### Step 4: Static Gradio Component Development ✅
|
233 |
+
|
234 |
+
**Date Completed:** April 15, 2025
|
235 |
+
|
236 |
+
**Actions Performed:**
|
237 |
+
- Created new component file
|
238 |
+
- Implemented layout matching `game recap layout example.png`
|
239 |
+
- Used static assets for 49ers first game
|
240 |
+
- Implemented responsive design
|
241 |
+
|
242 |
+
**Implementation Details:**
|
243 |
+
Created a reusable game recap component in `components/game_recap_component.py` that displays team logos, names, scores, and highlights the winning team. The component uses the data from the merged CSV file and applies the 49ers theme styling. The component was integrated into the main Gradio app and tested independently. (Note -- this is a v1, WIP build with additional visual styling to be applied later.)
|
244 |
+
|
245 |
+
### Step 5: Component Testing ✅
|
246 |
+
|
247 |
+
**Date Completed:** April 15, 2025
|
248 |
+
|
249 |
+
**Actions Performed:**
|
250 |
+
- Added component as first element in Gradio app
|
251 |
+
- Tested CSV data integration
|
252 |
+
- Verified static display
|
253 |
+
- Documented display issues
|
254 |
+
|
255 |
+
**Implementation Details:**
|
256 |
+
Created a reusable game recap component in `components/game_recap_component.py` that displays team logos, names, scores, and highlights the winning team. The component uses the data from the merged CSV file and applies the 49ers theme styling. The component was integrated into the main Gradio app and tested independently. (Note -- this is a v1, WIP build with additional visual styling to be applied later.)
|
257 |
+
|
258 |
+
### Step 6: Neo4j Database Update ✅
|
259 |
+
|
260 |
+
**Date Completed:** April 15, 2025
|
261 |
|
262 |
**Actions Performed:**
|
263 |
1. Created a new directory for the Neo4j update script:
|
|
|
293 |
- 17 games with team logo URLs
|
294 |
- 15 games with highlight video URLs
|
295 |
|
296 |
+
**Implementation Details:**
|
297 |
+
Successfully updated Neo4j database with game attributes including team logo URLs and highlight video URLs. Created update scripts that use game_id as the primary key and verified data integrity with proper error handling. All existing nodes were preserved while adding the new multimedia attributes.
|
298 |
+
|
299 |
**Challenges and Solutions:**
|
300 |
- Initially had issues with the location of the .env file. Fixed by updating the script to look in the correct location (ifx-sandbox/.env).
|
301 |
- Added command-line flag (--yes) for non-interactive execution.
|
|
|
306 |
3. URLs provided in the CSV file are valid and accessible.
|
307 |
4. The script should only update existing nodes, not create new ones.
|
308 |
|
309 |
+
### Step 7: LangChain Integration ✅
|
310 |
|
311 |
+
**Date Completed:** April 15, 2025
|
312 |
|
313 |
**Actions Performed:**
|
314 |
1. Created a new `game_recap.py` file in the tools directory with these components:
|
|
|
332 |
- Created gradio_agent.py that doesn't rely on Streamlit
|
333 |
- Added proper imports to allow tools to find these modules
|
334 |
|
335 |
+
**Implementation Details:**
|
336 |
+
Created game_recap.py with Cypher generation templates and GraphCypherQAChain for retrieving game data. Implemented natural language understanding for game identification through date formats, team names, and relative references. Successfully established data flow from Neo4j to the Gradio component with proper structured data handling.
|
337 |
|
338 |
+
### Step 8: Component Refactoring ✅
|
339 |
+
|
340 |
+
**Date Completed:** April 15, 2025
|
341 |
|
342 |
**Actions Performed:**
|
343 |
1. Enhanced game_recap_component.py:
|
|
|
361 |
- Created fallbacks for common teams and games
|
362 |
- Added debugging logs for tracking game data
|
363 |
|
364 |
+
### Step 9: Gradio App Integration ✅
|
365 |
|
366 |
+
**Date Completed:** April 15, 2025
|
367 |
|
368 |
**Actions Performed:**
|
369 |
1. Updated gradio_app.py:
|
|
|
384 |
- Implemented data caching to preserve structured data during agent processing
|
385 |
- Addressed HTML rendering issues by using proper Gradio components
|
386 |
|
387 |
+
### Step 10: Final Deployment ✅
|
388 |
+
|
389 |
+
**Date Completed:** April 15, 2025
|
390 |
+
|
391 |
+
**Actions Performed:**
|
392 |
+
- Deployed to HuggingFace Spaces: https://huggingface.co/spaces/aliss77777/ifx-sandbox
|
393 |
+
- Performed final UI checks
|
394 |
+
- Verified data accuracy
|
395 |
+
- Documented issues
|
396 |
+
|
397 |
+
**Implementation Details:**
|
398 |
+
Successfully deployed to HuggingFace Spaces using Gradio's built-in deployment feature. Secured environment variables as HuggingFace Secrets. Verified all connections, data accuracy, and UI functionality on the deployed version.
|
399 |
+
|
400 |
+
### Step 11: Testing and Verification ✅
|
401 |
|
402 |
**Test Cases:**
|
403 |
1. **Neo4j Database Update:**
|
docs/Phase 1/Task 1.2.2 Player Search Implementation.md
ADDED
@@ -0,0 +1,299 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Player Search Feature Implementation Instructions
|
2 |
+
|
3 |
+
## Context
|
4 |
+
You are an expert at UI/UX design and software front-end development and architecture. You are allowed to not know an answer, be uncertain, or disagree with your task. If any of these occur, halt your current process and notify the user immediately. You should not hallucinate. If you are unable to remember information, you are allowed to look it up again.
|
5 |
+
|
6 |
+
You are not allowed to hallucinate. You may only use data that exists in the files specified. You are not allowed to create new data if it does not exist in those files.
|
7 |
+
|
8 |
+
You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.
|
9 |
+
|
10 |
+
When writing code, your focus should be on creating new functionality that builds on the existing code base without breaking things that are already working. If you need to rewrite how existing code works in order to develop a new feature, please check your work carefully, and also pause your work and tell me (the human) for review before going ahead. We want to avoid software regression as much as possible.
|
11 |
+
|
12 |
+
**I WILL REPEAT, WHEN UPDATING EXISTING CODE FILES, PLEASE DO NOT OVERWRITE EXISTING CODE, PLEASE ADD OR MODIFY COMPONENTS TO ALIGN WITH THE NEW FUNCTIONALITY. THIS INCLUDES SMALL DETAILS LIKE FUNCTION ARGUMENTS AND LIBRARY IMPORTS. REGRESSIONS IN THESE AREAS HAVE CAUSED UNNECESSARY DELAYS AND WE WANT TO AVOID THEM GOING FORWARD.**
|
13 |
+
|
14 |
+
When you need to modify existing code (in accordance with the instruction above), **please present your recommendation to the user before taking action, and explain your rationale.**
|
15 |
+
|
16 |
+
If the data files and code you need to use as inputs to complete your task do not conform to the structure you expected based on the instructions, please pause your work and ask the human for review and guidance on how to proceed.
|
17 |
+
|
18 |
+
If you have difficulty finding mission critical updates in the codebase (e.g. .env files, data files) ask the user for help in finding the path and directory.
|
19 |
+
|
20 |
+
## Objective
|
21 |
+
Follow the step-by-step process to build the Player Search feature (Task 1.2.2 from requirements.md). Start with a simple use case of displaying a UI component with the player's headshot, Instagram handle link, and a summary of their roster info. The goal is for the user to ask the app a question about a specific player and receive both a text summary and a visual UI component with information for that player.
|
22 |
+
|
23 |
+
## Implementation Steps
|
24 |
+
|
25 |
+
1. **Review Code Base:** Familiarize yourself with the current project structure, particularly the Gradio app (`gradio_app.py`), existing components (`components/`), services, and utilities. Pay close attention to how the Game Recap feature was integrated.
|
26 |
+
2. **Neo4j Update Script Creation:**
|
27 |
+
* Create a new subfolder within `ifx-sandbox/data/april_11_multimedia_data_collect/new_final_april 11/` specifically for the player data update script (e.g., `neo4j_player_update/`).
|
28 |
+
* Create a Python script (`update_player_nodes.py`) within this new subfolder.
|
29 |
+
* Use the existing script `ifx-sandbox/data/april_11_multimedia_data_collect/new_final_april 11/neo4j_update/update_game_nodes.py` as a reference for connecting to Neo4j and performing updates.
|
30 |
+
3. **Neo4j Database Update:**
|
31 |
+
* The script should read player data from `ifx-sandbox/data/april_11_multimedia_data_collect/new_final_april 11/roster_april_11.csv`.
|
32 |
+
* Update existing `Player` nodes in the Neo4j database. **Do not create new nodes.**
|
33 |
+
* Use the `Player_id` attribute as the primary key to match records in the CSV file with nodes in the graph database.
|
34 |
+
* Add the following new attributes to the corresponding `Player` nodes:
|
35 |
+
* `headshot_url`
|
36 |
+
* `instagram_url`
|
37 |
+
* `highlight_video_url` (Note: Confirm if this specific column name exists in `roster_april_11.csv` or if it needs mapping).
|
38 |
+
* Implement verification steps within the script to confirm successful updates for each player.
|
39 |
+
* Report the number of updated nodes and any errors encountered.
|
40 |
+
* **Pause and request user confirmation** that the update completed successfully in the cloud interface before proceeding.
|
41 |
+
4. **Player Component Development:**
|
42 |
+
* Create a new component file (e.g., `components/player_card_component.py`).
|
43 |
+
* Design the component structure based on the requirements (headshot, name, potentially key stats, Instagram link). Use `components/game_recap_component.py` as a structural reference for creating a dynamic Gradio component.
|
44 |
+
* Ensure the component accepts player data (retrieved from Neo4j) as input.
|
45 |
+
* Implement responsive design and apply the established 49ers theme CSS.
|
46 |
+
5. **LangChain Integration:**
|
47 |
+
* Review existing LangChain integration in `gradio_agent.py` and `cypher.py` (and potentially `tools/game_recap.py`).
|
48 |
+
* Create a new file, potentially `tools/player_search.py`, for the player-specific LangChain logic.
|
49 |
+
* Define a new LangChain tool specifically for player search with a clear description so the agent recognizes when to use it.
|
50 |
+
* Implement text-to-Cypher query generation to retrieve player information based on natural language queries (e.g., searching by name, jersey number).
|
51 |
+
* Ensure the Cypher query retrieves all necessary attributes (`name`, `headshot_url`, `instagram_url`, relevant stats, etc.) using `Player_id` or `Name` for matching.
|
52 |
+
* The tool function should return both a text summary (generated by the LLM based on retrieved data) and the structured data needed for the UI component.
|
53 |
+
6. **Gradio App Integration:**
|
54 |
+
* **Propose changes first:** Before modifying `gradio_app.py` or related files, outline the necessary changes (e.g., adding a new placeholder for the player component, updating the chat processing function to handle player data, modifying event handlers) and **request user approval.**
|
55 |
+
* Import the new player search tool into `gradio_agent.py` and add it to the agent's tool list.
|
56 |
+
* Import the new player card component into `gradio_app.py`.
|
57 |
+
* Modify the main chat/response function in `gradio_app.py` to:
|
58 |
+
* Recognize when the agent returns player data.
|
59 |
+
* Extract the text summary and structured data.
|
60 |
+
* Update the Gradio UI to display the player card component with the structured data.
|
61 |
+
* Display the text summary in the chat interface.
|
62 |
+
* Ensure the player card component is initially hidden and only displayed when relevant data is available (similar to the game recap component).
|
63 |
+
* Update the "Clear Chat" functionality to also hide/reset the player card component.
|
64 |
+
7. **Testing and Validation:**
|
65 |
+
* Test the Neo4j update script thoroughly.
|
66 |
+
* Verify the LangChain tool correctly identifies player queries and generates appropriate Cypher.
|
67 |
+
* Test retrieving data for various players.
|
68 |
+
* Validate that the player card component renders correctly with different player data.
|
69 |
+
* Test the end-to-end flow in the Gradio app with various natural language queries about players.
|
70 |
+
* Check error handling for cases like player not found or ambiguous queries.
|
71 |
+
|
72 |
+
## Data Flow Architecture
|
73 |
+
1. User submits a natural language query about a specific player.
|
74 |
+
2. LangChain agent processes the query and selects the Player Search tool (likely implemented in `tools/player_search.py`).
|
75 |
+
3. The tool generates a Cypher query to retrieve player data from Neo4j based on the user's query.
|
76 |
+
4. Neo4j returns the player data including attributes like name, position, headshot URL, Instagram URL, etc.
|
77 |
+
5. The tool receives the data, potentially uses an LLM to generate a text summary, and structures the data for the UI component.
|
78 |
+
6. The tool returns the text summary and structured data to the agent/Gradio app.
|
79 |
+
7. The Gradio app receives the response.
|
80 |
+
8. The player card component function is called with the structured data, generating the visual UI.
|
81 |
+
9. The UI component is displayed to the user, and the text summary appears in the chat.
|
82 |
+
|
83 |
+
## Error Handling Strategy
|
84 |
+
1. Implement specific error handling for:
|
85 |
+
* Player not found in the database.
|
86 |
+
* Ambiguous player identification (e.g., multiple players with similar names).
|
87 |
+
* Missing required attributes in Neo4j (e.g., missing `headshot_url`).
|
88 |
+
* Database connection issues during query.
|
89 |
+
* Failures in rendering the UI component.
|
90 |
+
2. Provide user-friendly error messages in the chat interface.
|
91 |
+
3. Implement graceful degradation (e.g., show text summary even if the visual component fails).
|
92 |
+
4. Add logging for debugging player search queries and component rendering.
|
93 |
+
|
94 |
+
## Performance Optimization
|
95 |
+
1. Optimize Neo4j Cypher queries for player search.
|
96 |
+
2. Consider caching frequently accessed player data if performance becomes an issue.
|
97 |
+
3. Ensure efficient loading of player headshot images in the UI component.
|
98 |
+
|
99 |
+
## Failure Condition
|
100 |
+
If you are unable to complete any step after 3 attempts, immediately halt the process, document the failure point and reason, and consult with the user on how to continue. Do not proceed without resolution.
|
101 |
+
|
102 |
+
## Success Criteria
|
103 |
+
- Neo4j database successfully updated with new player attributes (`headshot_url`, `instagram_url`, etc.).
|
104 |
+
- LangChain correctly identifies player search queries and retrieves accurate data.
|
105 |
+
- The Player Card component renders correctly in the Gradio UI, displaying headshot, relevant info, and links.
|
106 |
+
- User can query specific players using natural language and receive both text and visual responses.
|
107 |
+
- Integration does not cause regressions in existing functionality (like Game Recap search).
|
108 |
+
- Error handling functions correctly for anticipated issues.
|
109 |
+
|
110 |
+
## Notes
|
111 |
+
- Prioritize non-destructive updates to the Neo4j database.
|
112 |
+
- Confirm the exact column names in `roster_april_11.csv` before scripting the Neo4j update.
|
113 |
+
- Reuse existing patterns for agent tools, component creation, and Gradio integration where possible.
|
114 |
+
- Document all changes, especially modifications to existing files like `gradio_agent.py` and `gradio_app.py`.
|
115 |
+
- Test thoroughly after each significant step.
|
116 |
+
|
117 |
+
## Implementation Log
|
118 |
+
*(This section will be filled in as steps are completed)*
|
119 |
+
|
120 |
+
### Step 1: Review Code Base
|
121 |
+
**Date Completed:** April 16, 2025
|
122 |
+
**Actions Performed:**
|
123 |
+
- Reviewed key files: `gradio_app.py`, `gradio_agent.py`, `components/game_recap_component.py`, `tools/game_recap.py`, `tools/cypher.py`, `gradio_utils.py`.
|
124 |
+
- Analyzed patterns for component creation (`gr.HTML` generation), tool definition (prompts, QA chains), agent integration (tool list in `gradio_agent.py`), and UI updates in `gradio_app.py`.
|
125 |
+
- Noted the use of a global cache (`LAST_GAME_DATA` in `tools/game_recap.py`) as a workaround to pass structured data for UI components.
|
126 |
+
**Challenges and Solutions:** N/A for review step.
|
127 |
+
**Assumptions:** The existing patterns are suitable for implementing the Player Search feature.
|
128 |
+
|
129 |
+
### Step 2: Neo4j Update Script Creation
|
130 |
+
**Date Completed:** April 16, 2025
|
131 |
+
**Actions Performed:**
|
132 |
+
- Created directory `ifx-sandbox/data/april_11_multimedia_data_collect/new_final_april 11/neo4j_player_update/`.
|
133 |
+
- Created script file `update_player_nodes.py` within the new directory.
|
134 |
+
- Adapted logic from `update_game_nodes.py` to read `roster_april_11.csv`.
|
135 |
+
- Implemented Cypher query to `MATCH` on `Player` nodes using `Player_id` and `SET` `headshot_url`, `instagram_url`, and `highlight_video_url` attributes.
|
136 |
+
- Included connection handling, error reporting, verification, and user confirmation.
|
137 |
+
**Challenges and Solutions:** Confirmed column names (`headshot_url`, `instagram_url`, `highlight_video_url`) exist in `roster_april_11.csv` before including them in the script.
|
138 |
+
**Assumptions:** `Player_id` in the CSV correctly matches the `Player_id` property on `Player` nodes in Neo4j. Neo4j credentials in `.env` are correct.
|
139 |
+
|
140 |
+
### Step 3: Neo4j Database Update
|
141 |
+
**Date Completed:** April 16, 2025
|
142 |
+
**Actions Performed:**
|
143 |
+
- Executed the `update_player_nodes.py` script.
|
144 |
+
- Confirmed successful connection to Neo4j and loading of 73 players from CSV.
|
145 |
+
- Monitored the update process, confirming 73 Player nodes were matched and updated.
|
146 |
+
- Reviewed the summary and verification output: 73 successful updates, 0 errors. 56 players verified with headshot/Instagram URLs, 18 with highlight URLs.
|
147 |
+
**Challenges and Solutions:**
|
148 |
+
- Corrected `.env` file path calculation in the script (initially looked in the wrong directory).
|
149 |
+
- Fixed script error due to case mismatch for `player_id` column in CSV vs. script's check.
|
150 |
+
- Corrected Cypher query to use lowercase `player_id` property and correct parameter name (`$match_player_id`).
|
151 |
+
**Assumptions:** The counts reported by the verification step accurately reflect the state of the database.
|
152 |
+
|
153 |
+
### Step 4: Player Component Development
|
154 |
+
**Date Completed:** April 16, 2025
|
155 |
+
**Actions Performed:**
|
156 |
+
- Created new file `ifx-sandbox/components/player_card_component.py`.
|
157 |
+
- Defined function `create_player_card_component(player_data=None)`.
|
158 |
+
- Implemented HTML structure for a player card display (headshot, name, position, number, Instagram link).
|
159 |
+
- Included inline CSS adapted from 49ers theme and existing components.
|
160 |
+
- Function accepts a dictionary and returns `gr.HTML`.
|
161 |
+
- Added basic error handling and safe defaults for missing data.
|
162 |
+
- Included commented example usage for testing.
|
163 |
+
**Challenges and Solutions:** Ensured `html.escape()` was used for all dynamic text/URLs. Handled potential variations in the player number key (`Number` vs. `Jersey_number`).
|
164 |
+
**Assumptions:** The data passed to the component will have keys like `Name`, `headshot_url`, `instagram_url`, `Position`, `Number`/`Jersey_number` based on the expected Neo4j node properties.
|
165 |
+
|
166 |
+
### Step 5: LangChain Integration
|
167 |
+
**Date Completed:** April 16, 2025
|
168 |
+
**Actions Performed:**
|
169 |
+
- Created new file `ifx-sandbox/tools/player_search.py`.
|
170 |
+
- Implemented global variable `LAST_PLAYER_DATA` and getter/setter functions for caching structured data (similar to game recap tool).
|
171 |
+
- Defined `PLAYER_SEARCH_TEMPLATE` prompt for Cypher generation, specifying required properties (including new ones like `headshot_url`) and case-insensitive search.
|
172 |
+
- Defined `PLAYER_SUMMARY_TEMPLATE` prompt for generating text summaries.
|
173 |
+
- Created `player_search_chain` using `GraphCypherQAChain` with `return_direct=True`.
|
174 |
+
- Implemented `parse_player_data` function to extract player details from Neo4j results into a dictionary.
|
175 |
+
- Implemented `generate_player_summary` function using the LLM and summary prompt.
|
176 |
+
- Created the main tool function `player_search_qa(input_text)` which:
|
177 |
+
- Clears the cache.
|
178 |
+
- Invokes the `player_search_chain`.
|
179 |
+
- Parses the result.
|
180 |
+
- Generates the summary.
|
181 |
+
- Stores structured data in `LAST_PLAYER_DATA` cache.
|
182 |
+
- Returns a dictionary `{"output": summary, "player_data": data}`.
|
183 |
+
- Included error handling and logging.
|
184 |
+
**Challenges and Solutions:** Replicated the caching mechanism from `game_recap.py` as a likely necessary workaround for passing structured data.
|
185 |
+
**Assumptions:** The `GraphCypherQAChain` will correctly interpret the prompt to retrieve all specified player properties. The caching mechanism will function correctly for passing data to the Gradio UI step.
|
186 |
+
|
187 |
+
### Step 6: Gradio App Integration
|
188 |
+
**Date Completed:** April 16, 2025
|
189 |
+
**Actions Performed:**
|
190 |
+
- **`gradio_agent.py`**: Imported `player_search_qa` tool and added it to the agent's `tools` list with an appropriate name and description.
|
191 |
+
- **`gradio_app.py`**:
|
192 |
+
- Imported `create_player_card_component` and `get_last_player_data`.
|
193 |
+
- Added `player_card_display = gr.HTML(visible=False)` to the `gr.Blocks` layout.
|
194 |
+
- Refactored `process_message` to focus only on returning the agent's text output.
|
195 |
+
- Modified `process_and_respond`:
|
196 |
+
- It now checks `get_last_player_data()` first.
|
197 |
+
- If player data exists, it generates the player card and sets visibility for `player_card_display`.
|
198 |
+
- If no player data, it checks `get_last_game_data()`.
|
199 |
+
- If game data exists, it generates the game recap and sets visibility for `game_recap_display`.
|
200 |
+
- Returns `gr.update()` for both components to ensure only one (or neither) is visible.
|
201 |
+
- Modified `clear_chat` to return updates to clear/hide both `player_card_display` and `game_recap_display`.
|
202 |
+
- Updated the `outputs` list for submit/click events to include both display components.
|
203 |
+
**Challenges and Solutions:** Refactored `process_and_respond` to handle checking both player and game caches sequentially, ensuring only the most relevant component is displayed. Removed older state management (`state.current_game`) in favor of relying solely on the tool caches.
|
204 |
+
**Assumptions:** The caching mechanism (`get_last_player_data`, `get_last_game_data`) reliably indicates which tool ran last and provided structured data. The Gradio `gr.update()` calls correctly target the HTML components.
|
205 |
+
|
206 |
+
### Step 7: Testing and Validation
|
207 |
+
**Date Completed:** [Date]
|
208 |
+
**Actions Performed:**
|
209 |
+
**Challenges and Solutions:**
|
210 |
+
**Assumptions:**
|
211 |
+
|
212 |
+
---
|
213 |
+
|
214 |
+
## Risk Assessment Before Testing (Step 7)
|
215 |
+
|
216 |
+
*Date: April 16, 2025*
|
217 |
+
|
218 |
+
A review of the changes made in Step 6 (Gradio App Integration) was performed before starting Step 7 (Testing and Validation).
|
219 |
+
|
220 |
+
**Summary:**
|
221 |
+
|
222 |
+
1. **`gradio_agent.py`:**
|
223 |
+
* Changes were purely additive (importing `player_search_qa`, adding the "Player Information Search" tool to the `tools` list).
|
224 |
+
* Existing tools, agent creation, memory, and core logic remain unchanged.
|
225 |
+
* *Risk Assessment:* Low risk of regression. Agent is now aware of the new tool.
|
226 |
+
|
227 |
+
2. **`gradio_app.py`:**
|
228 |
+
* Additive changes: Imports added, `player_card_display = gr.HTML(visible=False)` added to layout.
|
229 |
+
* Refactoring of `process_message`: Simplified to only return text output. Relies on tool cache (`LAST_PLAYER_DATA`, `LAST_GAME_DATA`) for component logic.
|
230 |
+
* Refactoring of `process_and_respond`:
|
231 |
+
* Centralizes component display logic based on tool caches.
|
232 |
+
* Checks player cache *first*, then game cache.
|
233 |
+
* Returns `gr.update()` for *both* components to ensure exclusive visibility.
|
234 |
+
* Modification of `clear_chat`: Correctly targets both display components for clearing/hiding.
|
235 |
+
* Modification of Event Handlers: Output lists correctly include both display components.
|
236 |
+
* Removal of `state.current_game`: UI display now fully dependent on the tool caching mechanism.
|
237 |
+
* *Risk Assessment:* Low-to-moderate risk. The core change relies heavily on the **tool caching mechanism** (`get_last_player_data`, `get_last_game_data`) working reliably. If a tool fails to set/clear its cache correctly, the wrong component might be displayed or persist incorrectly. The sequential check (player then game) should prevent conflicts if caching works. The simplification of `process_message` and removal of `state.current_game` are intentional but shift dependency to the cache.
|
238 |
+
|
239 |
+
**Overall Conclusion:**
|
240 |
+
|
241 |
+
The modifications seem logically sound and align with the goal of adding player search alongside game recap. The primary dependency is the correct functioning of the global cache variables (`LAST_PLAYER_DATA`, `LAST_GAME_DATA`) set by the respective tool functions (`player_search_qa`, `game_recap_qa`). Assuming the caching works as designed in the tool files, the integration should function correctly without regressing existing features.
|
242 |
+
|
243 |
+
---
|
244 |
+
|
245 |
+
## Bug Log
|
246 |
+
|
247 |
+
### Initial Testing - April 16, 2025
|
248 |
+
|
249 |
+
Based on the first round of testing after Step 6 completion, the following issues were observed:
|
250 |
+
|
251 |
+
1. **Missing Logo:** App displays placeholder question marks in the top-left corner where a logo is expected.
|
252 |
+
2. **Delayed Welcome Message:** The initial welcome message only appears *after* the first user message is submitted, not immediately on load.
|
253 |
+
3. **Output Visual Glitch:** A gray box or "visual static" appears overlaid on top of the chat outputs (visible on the welcome message screenshot).
|
254 |
+
4. **Game Recap Component Failure:** Queries intended to trigger the Game Recap component (e.g., about the Jets game) return a text response but fail to display the visual game recap component.
|
255 |
+
5. **Player Card Component Failure:** Queries intended to trigger the Player Search tool (e.g., "who is the quarterback") return a text response but fail to display the visual player card component. The terminal output shows the wrong tool (Graph Search) or incorrect data handling might be occurring.
|
256 |
+
|
257 |
+
### Bug Fix Attempts - April 16, 2025
|
258 |
+
|
259 |
+
* **Bug #5 (Player Card Component Failure - Tool Selection & Data Parsing):**
|
260 |
+
* **Observation:** Agent defaults to "49ers Graph Search" for specific player queries. Even when the correct tool is selected (after prompt changes), the component doesn't appear because data parsing fails.
|
261 |
+
* **Attempt 1 (Action - April 16, 2025):** Refined tool descriptions in `gradio_agent.py`.
|
262 |
+
* **Result 1:** Failed (Tool selection still incorrect).
|
263 |
+
* **Attempt 2 (Action - April 16, 2025):** Modified `AGENT_SYSTEM_PROMPT` in `prompts.py` to prioritize Player Search tool.
|
264 |
+
* **Result 2:** Partial Success (Tool selection fixed). Card still not displayed.
|
265 |
+
* **Observation (Post-Attempt 2):** Terminal logs show `parse_player_data` fails due to expecting non-prefixed keys.
|
266 |
+
* **Attempt 3 (Action - April 16, 2025):** Modified `parse_player_data` in `tools/player_search.py` to map prefixed keys (e.g., `p.Name`) to non-prefixed keys (`Name`).
|
267 |
+
* **Result 3:** Failed. Parsing still unsuccessful.
|
268 |
+
* **Observation (Post-Attempt 3):** Terminal logs show the parser check `if 'Name' not in parsed_data` fails. Comparison with `Available keys in result: ['p.player_id', 'p.name', ...]` reveals the `key_map` used incorrect *case* (e.g., `p.Name` vs. actual `p.name`).
|
269 |
+
* **Attempt 4 (Action - April 16, 2025):** Corrected case sensitivity in the `key_map` within `parse_player_data` in `tools/player_search.py` to exactly match the lowercase keys returned by the Cypher query (e.g., `p.name`, `p.position`).
|
270 |
+
* **Next Step:** Re-test player search queries ("tell me about Nick Bosa") to confirm data parsing now succeeds and the player card component appears correctly.
|
271 |
+
|
272 |
+
**Current Plan:** Continue debugging Bug #5 (Data Parsing / Component Display).
|
273 |
+
|
274 |
+
## End of Day Summary - April 16, 2025
|
275 |
+
|
276 |
+
**Progress:**
|
277 |
+
- Focused on debugging **Bug #5 (Player Card Component Failure)**.
|
278 |
+
- Successfully resolved the tool selection and data parsing sub-issues within Bug #5 (Attempts 1-4).
|
279 |
+
- Confirmed via logging (Attempt 5) that the `player_search_qa` tool retrieves data, parses it correctly, and the `create_player_card_component` function generates the expected HTML.
|
280 |
+
- Implemented a debug textbox (Attempt 6) in `gradio_app.py` and modified `process_and_respond` to update it with player data string, aiming to isolate the `gr.update` mechanism.
|
281 |
+
|
282 |
+
**Current Status:**
|
283 |
+
- The backend logic (tool selection, data retrieval via Cypher, data parsing, caching via `LAST_PLAYER_DATA`) appears functional for the Player Search feature.
|
284 |
+
- The primary remaining issue for Bug #5 is the **UI component rendering failure**. Despite the correct data being available and the component generation function running, the `gr.update` call in `process_and_respond` is not successfully updating either the target `gr.HTML` component or the debug `gr.Textbox`.
|
285 |
+
|
286 |
+
**Unresolved Bugs:**
|
287 |
+
- **Bug #1:** Missing Logo
|
288 |
+
- **Bug #2:** Delayed Welcome Message
|
289 |
+
- **Bug #3:** Output Visual Glitch
|
290 |
+
- **Bug #4:** Game Recap Component Failure
|
291 |
+
- **Bug #5:** Player Card Component Failure (Specifically the UI rendering/update part)
|
292 |
+
|
293 |
+
**Next Steps to Resume:**
|
294 |
+
1. Run the application and test a player search query (e.g., "tell me about Nick Bosa").
|
295 |
+
2. Observe the terminal output for confirmation that the player search tool runs and data is cached.
|
296 |
+
3. Check if the **debug textbox** in the Gradio UI is populated with the player data string.
|
297 |
+
- If **YES**: This indicates the `gr.update` mechanism based on the cache *is* working for the Textbox. The issue likely lies specifically with updating the `gr.HTML` component (`player_card_display`). Potential causes: incorrect component reference, issues with rendering raw HTML via `gr.update`, conflicts with other UI elements.
|
298 |
+
- If **NO**: This indicates a more fundamental issue with the `gr.update` call within `process_and_respond` or how the component references are being passed/used in the event handlers/outputs list. The caching check (`if player_data:`) might not be triggering the update path as expected, or the `gr.update` itself is failing silently.
|
299 |
+
4. Based on the outcome of step 3, investigate the specific `gr.update` call for the failing component (`debug_textbox` or `player_card_display`).
|
docs/requirements.md
CHANGED
@@ -72,7 +72,7 @@ Recommendations for how to engage and participate with real-world fan communitie
|
|
72 |
- "How accurate is Brock Purdy's deep ball?"
|
73 |
- **Priority:** High
|
74 |
|
75 |
-
### 3. Game Search
|
76 |
- **Description:** Game-level summaries and key moments/highlights.
|
77 |
- **Data Source:** Preloaded Neo4j
|
78 |
- **Display:** Recap text block + embedded image or video preview
|
@@ -226,12 +226,11 @@ Based on a review of the existing codebase and requirements, here's a structured
|
|
226 |
|
227 |
| Task | Description | Dependencies |
|
228 |
|------|-------------|--------------|
|
229 |
-
| **1.1
|
230 |
-
| **1.
|
231 |
-
| **1.2
|
232 |
-
| **1.2.
|
233 |
-
| **1.2.
|
234 |
-
| **1.2.3 Game Search** | Return queries about a game using a multi-media component | 1.1 |
|
235 |
| **1.3 Develop memory system and UI integration with Zep** | Implement persona-based memory system with Zep | None |
|
236 |
|
237 |
**Demo 1 Milestone:** April 22
|
@@ -359,100 +358,3 @@ Based on a review of the existing codebase and requirements, here's a structured
|
|
359 |
| Persona complexity | Start with simplified personas, then enhance |
|
360 |
| Deployment constraints | Test with Hugging Face resource limits early |
|
361 |
| Memory persistence | Implement simple local fallback if Zep has issues |
|
362 |
-
|
363 |
-
## 11. Feature Work Log
|
364 |
-
|
365 |
-
### Phase 1, Step 1.2: Game Search Feature Implementation
|
366 |
-
|
367 |
-
#### Objective
|
368 |
-
Implement the Game Search feature (Feature 1 from Feature Overview) with focus on game recap display functionality.
|
369 |
-
|
370 |
-
#### Prerequisites
|
371 |
-
- Access to `schedule_with_result_april_11.csv`
|
372 |
-
- Access to `nfl_team_logos_revised.csv`
|
373 |
-
- Reference to `game recap layout example.png`
|
374 |
-
- Gradio app instance
|
375 |
-
- Neo4j database instance
|
376 |
-
|
377 |
-
#### Implementation Steps
|
378 |
-
|
379 |
-
1. **CSS Integration ✅**
|
380 |
-
- Add required CSS styles to the Gradio app
|
381 |
-
- Ensure styles support responsive layout
|
382 |
-
- Implement 49ers theme colors from Design System section
|
383 |
-
- **Implementation:** CSS styles were embedded directly in the Gradio app as a string variable, ensuring compatibility with both local development and Hugging Face Spaces deployment. The implementation includes comprehensive styling for all UI components with the 49ers theme colors.
|
384 |
-
|
385 |
-
2. **Data Requirements Enhancement ✅**
|
386 |
-
- Review existing game score & result data
|
387 |
-
- Identify home team name and logo source
|
388 |
-
- Identify away team name and logo source
|
389 |
-
- Document data structure requirements
|
390 |
-
- **Implementation:** Analyzed the schedule CSV file and identified that home team names are in the "Home Team" column and away team names are in the "Away Team" column. Logo sources were identified in the "logo_url" column of the team logos CSV file, which provides direct URLs to team logos from ESPN's CDN.
|
391 |
-
|
392 |
-
3. **CSV File Update ✅**
|
393 |
-
- Open `schedule_with_result_april_11.csv`
|
394 |
-
- Add columns for home team logo
|
395 |
-
- Add columns for away team logo
|
396 |
-
- Merge data from `nfl_team_logos_revised.csv`
|
397 |
-
- Validate data integrity
|
398 |
-
- Save as new version
|
399 |
-
- **Implementation:** Created a Python script to merge the schedule data with team logo URLs. The script maps team names to their corresponding logo URLs and adds two new columns to the schedule CSV: 'home_team_logo_url' and 'away_team_logo_url'. The merged data was saved as 'schedule_with_result_and_logo_urls.csv'.
|
400 |
-
|
401 |
-
4. **Static Gradio Component Development ✅**
|
402 |
-
- Create new component file
|
403 |
-
- Implement layout matching `game recap layout example.png`:
|
404 |
-
- Top row: away team elements
|
405 |
-
- Bottom row: home team elements
|
406 |
-
- Score display with winning team highlight
|
407 |
-
- Video preview box
|
408 |
-
- Use static assets for 49ers first game
|
409 |
-
- Implement responsive design
|
410 |
-
- **Implementation:** Created a reusable game recap component in `components/game_recap_component.py` that displays team logos, names, scores, and highlights the winning team. The component uses the data from the merged CSV file and applies the 49ers theme styling. The component was integrated into the main Gradio app and tested independently. (Note -- this is a v1, WIP build with additional visual styling to be applied later.)
|
411 |
-
|
412 |
-
5. **Component Testing ✅**
|
413 |
-
- Add component as first element in Gradio app
|
414 |
-
- Test CSV data integration
|
415 |
-
- Verify static display
|
416 |
-
- Document any display issues
|
417 |
-
- **Implementation:** Created a reusable game recap component in `components/game_recap_component.py` that displays team logos, names, scores, and highlights the winning team. The component uses the data from the merged CSV file and applies the 49ers theme styling. The component was integrated into the main Gradio app and tested independently. (Note -- this is a v1, WIP build with additional visual styling to be applied later.)
|
418 |
-
|
419 |
-
6. **Function-Calling Implementation ✅**
|
420 |
-
- Prepare Neo4j merge operations
|
421 |
-
- Update graph with new game data
|
422 |
-
- Preserve existing nodes
|
423 |
-
- Add new attributes
|
424 |
-
- Test data integrity
|
425 |
-
- **Implementation:** Successfully updated Neo4j database with game attributes including team logo URLs and highlight video URLs. Created update scripts that use game_id as the primary key and verified data integrity with proper error handling. All existing nodes were preserved while adding the new multimedia attributes.
|
426 |
-
|
427 |
-
7. **LangChain Integration ✅**
|
428 |
-
- Adapt graph search function
|
429 |
-
- Implement game-specific search
|
430 |
-
- Test attribute retrieval
|
431 |
-
- Verify data flow to Gradio component
|
432 |
-
- **Implementation:** Created game_recap.py with Cypher generation templates and GraphCypherQAChain for retrieving game data. Implemented natural language understanding for game identification through date formats, team names, and relative references. Successfully established data flow from Neo4j to the Gradio component with proper structured data handling.
|
433 |
-
|
434 |
-
8. **Final Deployment ✅**
|
435 |
-
- Deploy to HuggingFace Spaces: https://huggingface.co/spaces/aliss77777/ifx-sandbox
|
436 |
-
- Perform final UI checks
|
437 |
-
- Verify data accuracy
|
438 |
-
- Document any issues
|
439 |
-
- **Implementation:** Successfully deployed to HuggingFace Spaces using Gradio's built-in deployment feature. Secured environment variables as HuggingFace Secrets. Verified all connections, data accuracy, and UI functionality on the deployed version.
|
440 |
-
|
441 |
-
#### Failure Conditions
|
442 |
-
- Halt process if any step fails after 3 attempts
|
443 |
-
- Document failure point and reason
|
444 |
-
- Consult with user for guidance
|
445 |
-
- Do not proceed without resolution
|
446 |
-
|
447 |
-
#### Success Criteria
|
448 |
-
- Component displays correctly in Gradio
|
449 |
-
- Data flows accurately from CSV to display
|
450 |
-
- Graph integration works without data loss
|
451 |
-
- LangChain search returns correct game data
|
452 |
-
- UI matches design specifications
|
453 |
-
|
454 |
-
#### Notes
|
455 |
-
- Maintain existing Neo4j node structure
|
456 |
-
- Preserve all current functionality
|
457 |
-
- Document all changes for future reference
|
458 |
-
- Test thoroughly before proceeding to next phase
|
|
|
72 |
- "How accurate is Brock Purdy's deep ball?"
|
73 |
- **Priority:** High
|
74 |
|
75 |
+
### 3. Game Recap Search
|
76 |
- **Description:** Game-level summaries and key moments/highlights.
|
77 |
- **Data Source:** Preloaded Neo4j
|
78 |
- **Display:** Recap text block + embedded image or video preview
|
|
|
226 |
|
227 |
| Task | Description | Dependencies |
|
228 |
|------|-------------|--------------|
|
229 |
+
| **1.1 ✅ Data ingestion of multimedia (team thumbnails, video highlights, player headshots)** | Download and integrate image files into the graphe database | None |
|
230 |
+
| **1.2 Build and test v1 Gradio UI components** | Create components with multimedia integration, focusing on feasability rather than design polish | 1.1 |
|
231 |
+
| **1.2.1 ✅ Game Recap Search (WIP)** | Returning queries about the a specific game, display through a multi-media component in the UI *(Backend logic implemented, visual component integration pending)* | 1.1 |
|
232 |
+
| **1.2.2 ✅ Player Search (WIP)** | Return queries about the player using a multi-media component *(Backend logic implemented, visual component integration pending)* | 1.1 |
|
233 |
+
| **1.2.3 Team Info Search** | Return queries about a team using a multi-media component | 1.1 |
|
|
|
234 |
| **1.3 Develop memory system and UI integration with Zep** | Implement persona-based memory system with Zep | None |
|
235 |
|
236 |
**Demo 1 Milestone:** April 22
|
|
|
358 |
| Persona complexity | Start with simplified personas, then enhance |
|
359 |
| Deployment constraints | Test with Hugging Face resource limits early |
|
360 |
| Memory persistence | Implement simple local fallback if Zep has issues |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gradio_agent.py
CHANGED
@@ -20,7 +20,8 @@ from gradio_utils import get_session_id
|
|
20 |
# Import tools
|
21 |
from tools.cypher import cypher_qa_wrapper
|
22 |
from tools.vector import get_game_summary
|
23 |
-
from tools.game_recap import game_recap_qa
|
|
|
24 |
|
25 |
# Create a basic chat chain for general football discussion
|
26 |
from langchain_core.prompts import ChatPromptTemplate
|
@@ -77,29 +78,37 @@ def football_chat_wrapper(input_text):
|
|
77 |
tools = [
|
78 |
Tool.from_function(
|
79 |
name="49ers Graph Search",
|
80 |
-
description="""Use for
|
81 |
-
Examples: "Who are the 49ers playing next week?", "Which players are defensive linemen?", "How many fan chapters are in California?"
|
82 |
-
This is your
|
83 |
func=cypher_qa_wrapper
|
84 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
Tool.from_function(
|
86 |
name="Game Recap",
|
87 |
-
description="""Use SPECIFICALLY for detailed game recaps or when users want to see visual information about a particular game.
|
88 |
Examples: "Show me the recap of the 49ers vs Jets game", "I want to see the highlights from the last 49ers game", "What happened in the game against the Patriots?"
|
89 |
Returns both a text summary AND visual game data that can be displayed to the user.
|
90 |
-
PREFER this tool over Game Summary Search for
|
91 |
func=game_recap_qa
|
92 |
),
|
93 |
Tool.from_function(
|
94 |
-
name="Game Summary Search",
|
95 |
-
description="""ONLY use for detailed game summaries or specific match results
|
96 |
-
Examples: "
|
97 |
-
Do NOT use for general schedule
|
98 |
-
func=get_game_summary,
|
99 |
),
|
100 |
Tool.from_function(
|
101 |
name="General Football Chat",
|
102 |
-
description="""ONLY use for general football discussion NOT specific to 49ers
|
103 |
Examples: "How does the NFL draft work?", "What are the basic rules of football?"
|
104 |
Do NOT use for any 49ers-specific questions.""",
|
105 |
func=football_chat_wrapper,
|
|
|
20 |
# Import tools
|
21 |
from tools.cypher import cypher_qa_wrapper
|
22 |
from tools.vector import get_game_summary
|
23 |
+
from tools.game_recap import game_recap_qa
|
24 |
+
from tools.player_search import player_search_qa # Import the new player search tool
|
25 |
|
26 |
# Create a basic chat chain for general football discussion
|
27 |
from langchain_core.prompts import ChatPromptTemplate
|
|
|
78 |
tools = [
|
79 |
Tool.from_function(
|
80 |
name="49ers Graph Search",
|
81 |
+
description="""Use for broader 49ers-related queries about GROUPS of players (e.g., list by position), general team info, schedules, fan chapters, or when other specific tools (like Player Search or Game Recap) are not applicable or fail.
|
82 |
+
Examples: "Who are the 49ers playing next week?", "Which players are defensive linemen?", "How many fan chapters are in California?", "List the running backs".
|
83 |
+
This is your general fallback for 49ers data if a more specific tool isn't a better fit.""",
|
84 |
func=cypher_qa_wrapper
|
85 |
),
|
86 |
+
Tool.from_function(
|
87 |
+
name="Player Information Search",
|
88 |
+
description="""Use this tool FIRST for any questions about a SPECIFIC player identified by name or jersey number.
|
89 |
+
Use it to get player details, stats, headshots, social media links, or an info card.
|
90 |
+
Examples: "Tell me about Brock Purdy", "Who is player number 97?", "Show me Nick Bosa's info card", "Get Deebo Samuel's stats", "Does Kalia Davis have an Instagram?"
|
91 |
+
Returns text summary and potentially visual card data.""",
|
92 |
+
func=player_search_qa
|
93 |
+
),
|
94 |
Tool.from_function(
|
95 |
name="Game Recap",
|
96 |
+
description="""Use SPECIFICALLY for detailed game recaps or when users want to see visual information about a particular game identified by opponent or date.
|
97 |
Examples: "Show me the recap of the 49ers vs Jets game", "I want to see the highlights from the last 49ers game", "What happened in the game against the Patriots?"
|
98 |
Returns both a text summary AND visual game data that can be displayed to the user.
|
99 |
+
PREFER this tool over Game Summary Search or Graph Search for specific game detail requests.""",
|
100 |
func=game_recap_qa
|
101 |
),
|
102 |
Tool.from_function(
|
103 |
+
name="Game Summary Search",
|
104 |
+
description="""ONLY use for detailed game summaries or specific match results if the 'Game Recap' tool fails or doesn't provide enough detail.
|
105 |
+
Examples: "Summarize the 49ers vs Seahawks game", "Give me details about the last playoff game results"
|
106 |
+
Do NOT use for general schedule questions.""",
|
107 |
+
func=get_game_summary,
|
108 |
),
|
109 |
Tool.from_function(
|
110 |
name="General Football Chat",
|
111 |
+
description="""ONLY use for general football discussion NOT specific to the 49ers team, players, or games.
|
112 |
Examples: "How does the NFL draft work?", "What are the basic rules of football?"
|
113 |
Do NOT use for any 49ers-specific questions.""",
|
114 |
func=football_chat_wrapper,
|
gradio_app.py
CHANGED
@@ -9,12 +9,17 @@ from zep_cloud.types import Message
|
|
9 |
from gradio_graph import graph
|
10 |
from gradio_llm import llm
|
11 |
import gradio_utils
|
12 |
-
from components.game_recap_component import create_game_recap_component
|
|
|
13 |
|
14 |
# Import the Gradio-compatible agent instead of the original agent
|
15 |
import gradio_agent
|
16 |
from gradio_agent import generate_response
|
17 |
|
|
|
|
|
|
|
|
|
18 |
# Define CSS directly
|
19 |
css = """
|
20 |
/* Base styles */
|
@@ -133,7 +138,6 @@ else:
|
|
133 |
class AppState:
|
134 |
def __init__(self):
|
135 |
self.chat_history = []
|
136 |
-
self.current_game = None
|
137 |
self.initialized = False
|
138 |
self.user_id = None
|
139 |
self.session_id = None
|
@@ -145,10 +149,6 @@ class AppState:
|
|
145 |
def get_chat_history(self):
|
146 |
return self.chat_history
|
147 |
|
148 |
-
def set_current_game(self, game_data):
|
149 |
-
self.current_game = game_data
|
150 |
-
print(f"Updated current game: {game_data}")
|
151 |
-
|
152 |
# Initialize global state
|
153 |
state = AppState()
|
154 |
|
@@ -205,7 +205,9 @@ async def initialize_chat():
|
|
205 |
|
206 |
# Process a message and return a response
|
207 |
async def process_message(message):
|
208 |
-
"""Process a message and return a response."""
|
|
|
|
|
209 |
try:
|
210 |
# Store user message in Zep memory if available
|
211 |
if zep:
|
@@ -214,103 +216,23 @@ async def process_message(message):
|
|
214 |
session_id=state.session_id,
|
215 |
messages=[Message(role_type="user", content=message, role="user")]
|
216 |
)
|
217 |
-
|
218 |
-
# Add user message to state
|
219 |
-
state.add_message("user", message)
|
220 |
-
|
221 |
# Process with the agent
|
222 |
print('Calling generate_response function...')
|
223 |
agent_response = generate_response(message, state.session_id)
|
224 |
print(f"Agent response received: {agent_response}")
|
225 |
-
|
226 |
-
# Always extract the output
|
227 |
-
output = agent_response.get("output", "")
|
228 |
-
metadata = agent_response.get("metadata", {})
|
229 |
print(f"Extracted output: {output}")
|
230 |
-
|
231 |
-
|
232 |
-
#
|
233 |
-
|
234 |
-
|
235 |
-
# FIRST CHECK: Get the cached game data (this is the most reliable indicator)
|
236 |
-
cached_game_data = get_last_game_data()
|
237 |
-
|
238 |
-
# SECOND CHECK: Look for game-related keywords in the output
|
239 |
-
is_game_related = any(keyword in output.lower() for keyword in [
|
240 |
-
"score", "stadium", "defeated", "won", "lost", "final score",
|
241 |
-
"game at", "home team", "away team", "dolphins", "49ers", "seahawks",
|
242 |
-
"jets", "vikings", "cardinals", "buccaneers", "final"
|
243 |
-
])
|
244 |
-
|
245 |
-
# THIRD CHECK: Check metadata for Game Recap tool usage (rarely works but try)
|
246 |
-
tools_used = metadata.get("tools_used", [])
|
247 |
-
tool_used_game_recap = "Game Recap" in str(tools_used)
|
248 |
-
|
249 |
-
# Determine if this is a game recap response
|
250 |
-
is_game_recap = cached_game_data is not None or (is_game_related and "game" in message.lower())
|
251 |
-
|
252 |
-
print(f"Is game recap detection: cached_data={cached_game_data is not None}, keywords={is_game_related}, tool={tool_used_game_recap}")
|
253 |
-
|
254 |
-
if is_game_recap:
|
255 |
-
print("Game Recap detected in response")
|
256 |
-
|
257 |
-
if cached_game_data:
|
258 |
-
print(f"Found cached game data: {cached_game_data}")
|
259 |
-
state.set_current_game(cached_game_data)
|
260 |
-
print("Set current game from cache")
|
261 |
-
else:
|
262 |
-
# Fallback for cases where the cache doesn't work
|
263 |
-
print("No cached game data found - using text-based game detection")
|
264 |
-
|
265 |
-
# Text-based game detection as a fallback
|
266 |
-
if "Vikings" in output and "49ers" in output:
|
267 |
-
# Create Vikings game data
|
268 |
-
game_data = {
|
269 |
-
'game_id': 'vikings-game',
|
270 |
-
'date': '15/09/2024',
|
271 |
-
'location': 'U.S. Bank Stadium',
|
272 |
-
'home_team': 'Minnesota Vikings',
|
273 |
-
'away_team': 'San Francisco 49ers',
|
274 |
-
'home_score': '23',
|
275 |
-
'away_score': '17',
|
276 |
-
'result': '23-17',
|
277 |
-
'winner': 'home',
|
278 |
-
'home_team_logo_url': 'https://a.espncdn.com/i/teamlogos/nfl/500/min.png',
|
279 |
-
'away_team_logo_url': 'https://a.espncdn.com/i/teamlogos/nfl/500/sf.png',
|
280 |
-
'highlight_video_url': 'https://www.youtube.com/watch?v=jTJw2uf-Pdg'
|
281 |
-
}
|
282 |
-
state.set_current_game(game_data)
|
283 |
-
print("Set current game to Vikings game from text")
|
284 |
-
elif "Dolphins" in output and "49ers" in output:
|
285 |
-
# Create Dolphins game data
|
286 |
-
game_data = {
|
287 |
-
'game_id': 'dolphins-game',
|
288 |
-
'date': '22/12/2024',
|
289 |
-
'location': 'Hard Rock Stadium',
|
290 |
-
'home_team': 'Miami Dolphins',
|
291 |
-
'away_team': 'San Francisco 49ers',
|
292 |
-
'home_score': '29',
|
293 |
-
'away_score': '17',
|
294 |
-
'result': '29-17',
|
295 |
-
'winner': 'home',
|
296 |
-
'home_team_logo_url': 'https://a.espncdn.com/i/teamlogos/nfl/500/mia.png',
|
297 |
-
'away_team_logo_url': 'https://a.espncdn.com/i/teamlogos/nfl/500/sf.png',
|
298 |
-
'highlight_video_url': 'https://www.youtube.com/watch?v=example'
|
299 |
-
}
|
300 |
-
state.set_current_game(game_data)
|
301 |
-
print("Set current game to Dolphins game from text")
|
302 |
-
else:
|
303 |
-
# No game detected
|
304 |
-
state.set_current_game(None)
|
305 |
-
print("No game detected in text")
|
306 |
-
else:
|
307 |
-
# Not a game recap query
|
308 |
-
state.set_current_game(None)
|
309 |
-
print("Not a game recap query")
|
310 |
-
|
311 |
-
# Add assistant response to state
|
312 |
-
state.add_message("assistant", output)
|
313 |
-
|
314 |
# Store assistant's response in Zep memory if available
|
315 |
if zep:
|
316 |
print("Storing assistant response in Zep...")
|
@@ -319,15 +241,15 @@ async def process_message(message):
|
|
319 |
messages=[Message(role_type="assistant", content=output, role="assistant")]
|
320 |
)
|
321 |
print("Assistant response stored in Zep")
|
322 |
-
|
323 |
-
return output
|
324 |
-
|
325 |
except Exception as e:
|
326 |
import traceback
|
327 |
print(f"Error in process_message: {str(e)}")
|
328 |
print(f"Traceback: {traceback.format_exc()}")
|
329 |
error_message = f"I'm sorry, there was an error processing your request: {str(e)}"
|
330 |
-
state.add_message("assistant", error_message)
|
331 |
return error_message
|
332 |
|
333 |
# Function to handle user input in Gradio
|
@@ -362,21 +284,25 @@ def bot_response(history):
|
|
362 |
# Create the Gradio interface
|
363 |
with gr.Blocks(title="49ers FanAI Hub", theme=gr.themes.Soft(), css=css) as demo:
|
364 |
gr.Markdown("# 🏈 49ers FanAI Hub")
|
365 |
-
|
366 |
-
#
|
367 |
-
|
368 |
-
|
369 |
-
|
|
|
|
|
|
|
|
|
370 |
# Chat interface
|
371 |
chatbot = gr.Chatbot(
|
372 |
-
value=state.get_chat_history(),
|
373 |
height=500,
|
374 |
show_label=False,
|
375 |
elem_id="chatbot",
|
376 |
-
type="messages",
|
377 |
render_markdown=True
|
378 |
)
|
379 |
-
|
380 |
# Input components
|
381 |
with gr.Row():
|
382 |
msg = gr.Textbox(
|
@@ -384,50 +310,80 @@ with gr.Blocks(title="49ers FanAI Hub", theme=gr.themes.Soft(), css=css) as demo
|
|
384 |
show_label=False,
|
385 |
scale=9
|
386 |
)
|
387 |
-
|
388 |
-
|
389 |
# Define a combined function for user input and bot response
|
390 |
async def process_and_respond(message, history):
|
391 |
-
|
|
|
392 |
if not state.initialized:
|
393 |
-
|
394 |
-
#
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
#
|
401 |
-
|
402 |
-
|
403 |
-
#
|
404 |
-
|
405 |
-
|
406 |
-
#
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
414 |
else:
|
415 |
-
#
|
416 |
-
|
417 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
418 |
# Set up event handlers with the combined function
|
419 |
-
|
420 |
-
|
421 |
-
|
|
|
|
|
|
|
422 |
# Add a clear button
|
423 |
-
|
424 |
-
|
425 |
-
# Clear function
|
426 |
def clear_chat():
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
|
432 |
# Launch the app
|
433 |
if __name__ == "__main__":
|
|
|
9 |
from gradio_graph import graph
|
10 |
from gradio_llm import llm
|
11 |
import gradio_utils
|
12 |
+
from components.game_recap_component import create_game_recap_component
|
13 |
+
from components.player_card_component import create_player_card_component
|
14 |
|
15 |
# Import the Gradio-compatible agent instead of the original agent
|
16 |
import gradio_agent
|
17 |
from gradio_agent import generate_response
|
18 |
|
19 |
+
# Import cache getter functions
|
20 |
+
from tools.game_recap import get_last_game_data
|
21 |
+
from tools.player_search import get_last_player_data
|
22 |
+
|
23 |
# Define CSS directly
|
24 |
css = """
|
25 |
/* Base styles */
|
|
|
138 |
class AppState:
|
139 |
def __init__(self):
|
140 |
self.chat_history = []
|
|
|
141 |
self.initialized = False
|
142 |
self.user_id = None
|
143 |
self.session_id = None
|
|
|
149 |
def get_chat_history(self):
|
150 |
return self.chat_history
|
151 |
|
|
|
|
|
|
|
|
|
152 |
# Initialize global state
|
153 |
state = AppState()
|
154 |
|
|
|
205 |
|
206 |
# Process a message and return a response
|
207 |
async def process_message(message):
|
208 |
+
"""Process a message and return a response (text only)."""
|
209 |
+
# NOTE: This function now primarily focuses on getting the agent's text response.
|
210 |
+
# UI component updates are handled in process_and_respond based on cached data.
|
211 |
try:
|
212 |
# Store user message in Zep memory if available
|
213 |
if zep:
|
|
|
216 |
session_id=state.session_id,
|
217 |
messages=[Message(role_type="user", content=message, role="user")]
|
218 |
)
|
219 |
+
|
220 |
+
# Add user message to state (for context, though Gradio manages history display)
|
221 |
+
# state.add_message("user", message)
|
222 |
+
|
223 |
# Process with the agent
|
224 |
print('Calling generate_response function...')
|
225 |
agent_response = generate_response(message, state.session_id)
|
226 |
print(f"Agent response received: {agent_response}")
|
227 |
+
|
228 |
+
# Always extract the text output
|
229 |
+
output = agent_response.get("output", "I apologize, I encountered an issue.")
|
230 |
+
# metadata = agent_response.get("metadata", {})
|
231 |
print(f"Extracted output: {output}")
|
232 |
+
|
233 |
+
# Add assistant response to state (for context)
|
234 |
+
# state.add_message("assistant", output)
|
235 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
# Store assistant's response in Zep memory if available
|
237 |
if zep:
|
238 |
print("Storing assistant response in Zep...")
|
|
|
241 |
messages=[Message(role_type="assistant", content=output, role="assistant")]
|
242 |
)
|
243 |
print("Assistant response stored in Zep")
|
244 |
+
|
245 |
+
return output # Return only the text output
|
246 |
+
|
247 |
except Exception as e:
|
248 |
import traceback
|
249 |
print(f"Error in process_message: {str(e)}")
|
250 |
print(f"Traceback: {traceback.format_exc()}")
|
251 |
error_message = f"I'm sorry, there was an error processing your request: {str(e)}"
|
252 |
+
# state.add_message("assistant", error_message)
|
253 |
return error_message
|
254 |
|
255 |
# Function to handle user input in Gradio
|
|
|
284 |
# Create the Gradio interface
|
285 |
with gr.Blocks(title="49ers FanAI Hub", theme=gr.themes.Soft(), css=css) as demo:
|
286 |
gr.Markdown("# 🏈 49ers FanAI Hub")
|
287 |
+
|
288 |
+
# --- Component Display Area --- #
|
289 |
+
# Debug Textbox (Temporary)
|
290 |
+
debug_textbox = gr.Textbox(label="Debug Player Data", visible=True, interactive=False)
|
291 |
+
# Player card display (initially hidden)
|
292 |
+
player_card_display = gr.HTML(visible=False)
|
293 |
+
# Game recap display (initially hidden)
|
294 |
+
game_recap_display = gr.HTML(visible=False)
|
295 |
+
|
296 |
# Chat interface
|
297 |
chatbot = gr.Chatbot(
|
298 |
+
# value=state.get_chat_history(), # Let Gradio manage history display directly
|
299 |
height=500,
|
300 |
show_label=False,
|
301 |
elem_id="chatbot",
|
302 |
+
# type="messages", # Default type often works better
|
303 |
render_markdown=True
|
304 |
)
|
305 |
+
|
306 |
# Input components
|
307 |
with gr.Row():
|
308 |
msg = gr.Textbox(
|
|
|
310 |
show_label=False,
|
311 |
scale=9
|
312 |
)
|
313 |
+
submit_btn = gr.Button("Send", scale=1) # Renamed for clarity
|
314 |
+
|
315 |
# Define a combined function for user input and bot response
|
316 |
async def process_and_respond(message, history):
|
317 |
+
print(f"History IN: {history}")
|
318 |
+
# Initialize if first interaction
|
319 |
if not state.initialized:
|
320 |
+
welcome_msg = await initialize_chat()
|
321 |
+
history = [(None, welcome_msg)] # Start history with welcome message
|
322 |
+
state.initialized = True
|
323 |
+
# Return immediately after initialization to show welcome message
|
324 |
+
# No component updates needed yet
|
325 |
+
return "", history, gr.update(visible=False), gr.update(visible=False), gr.update(value="")
|
326 |
+
|
327 |
+
# Append user message for processing & display
|
328 |
+
history.append((message, None))
|
329 |
+
|
330 |
+
# Process the message to get bot's text response
|
331 |
+
response_text = await process_message(message)
|
332 |
+
|
333 |
+
# Update last message in history with bot response
|
334 |
+
history[-1] = (message, response_text)
|
335 |
+
print(f"History OUT: {history}")
|
336 |
+
|
337 |
+
# --- Check Caches and Update Components --- #
|
338 |
+
# player_card_html = "" # No longer creating HTML directly here for player
|
339 |
+
player_card_visible = False
|
340 |
+
game_recap_html = ""
|
341 |
+
game_recap_visible = False
|
342 |
+
debug_text_update = gr.update(value="") # Default debug text update
|
343 |
+
|
344 |
+
# Check for Player Data first
|
345 |
+
player_data = get_last_player_data() # Check player cache
|
346 |
+
if player_data:
|
347 |
+
print("Player data found in cache, updating debug textbox...")
|
348 |
+
# player_card_html = create_player_card_component(player_data) # Temporarily disable HTML generation
|
349 |
+
player_card_visible = False # Keep HTML hidden for now
|
350 |
+
debug_text_update = gr.update(value=str(player_data)) # Update debug textbox instead
|
351 |
else:
|
352 |
+
# If no player data, check for Game Data
|
353 |
+
game_data = get_last_game_data() # Check game cache
|
354 |
+
if game_data:
|
355 |
+
print("Game data found in cache, creating recap...")
|
356 |
+
game_recap_html = create_game_recap_component(game_data)
|
357 |
+
game_recap_visible = True
|
358 |
+
# No player data found, ensure debug text is cleared
|
359 |
+
debug_text_update = gr.update(value="")
|
360 |
+
|
361 |
+
# Return updates for all relevant components
|
362 |
+
# Note: player_card_display update is temporarily disabled (always hidden)
|
363 |
+
return (
|
364 |
+
"",
|
365 |
+
history,
|
366 |
+
debug_text_update, # Update for the debug textbox
|
367 |
+
gr.update(value="", visible=False), # Keep player HTML hidden
|
368 |
+
gr.update(value=game_recap_html, visible=game_recap_visible) # Update game recap as before
|
369 |
+
)
|
370 |
+
|
371 |
# Set up event handlers with the combined function
|
372 |
+
# Ensure outputs list matches the return values of process_and_respond
|
373 |
+
# Added debug_textbox to the outputs list
|
374 |
+
outputs_list = [msg, chatbot, debug_textbox, player_card_display, game_recap_display]
|
375 |
+
msg.submit(process_and_respond, [msg, chatbot], outputs_list)
|
376 |
+
submit_btn.click(process_and_respond, [msg, chatbot], outputs_list)
|
377 |
+
|
378 |
# Add a clear button
|
379 |
+
clear_btn = gr.Button("Clear Conversation")
|
380 |
+
|
381 |
+
# Clear function - now needs to clear/hide components including debug box
|
382 |
def clear_chat():
|
383 |
+
return [], gr.update(value=""), gr.update(value="", visible=False), gr.update(value="", visible=False)
|
384 |
+
|
385 |
+
# Update clear outputs
|
386 |
+
clear_btn.click(clear_chat, None, [chatbot, debug_textbox, player_card_display, game_recap_display])
|
387 |
|
388 |
# Launch the app
|
389 |
if __name__ == "__main__":
|
prompts.py
CHANGED
@@ -6,12 +6,15 @@ Do not answer any questions that do not relate to the 49ers, players, or fans.
|
|
6 |
|
7 |
Do not answer any questions using your pre-trained knowledge, only use the information provided in the context.
|
8 |
|
9 |
-
IMPORTANT TOOL SELECTION GUIDELINES:
|
10 |
-
1.
|
11 |
-
2.
|
12 |
-
3.
|
|
|
|
|
13 |
|
14 |
-
When in doubt
|
|
|
15 |
|
16 |
TOOLS:
|
17 |
------
|
@@ -36,21 +39,27 @@ Thought: Do I need to use a tool? No
|
|
36 |
Final Answer: [your response here]
|
37 |
```
|
38 |
|
39 |
-
Example 1:
|
40 |
-
User: "
|
41 |
-
Thought:
|
42 |
-
Action:
|
43 |
-
Action Input:
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
-
Example
|
46 |
-
User: "
|
47 |
-
Thought:
|
48 |
-
Action:
|
49 |
-
Action Input:
|
50 |
|
51 |
-
Example
|
52 |
User: "How does the NFL draft work?"
|
53 |
-
Thought: This is asking about general NFL rules, not specific to the 49ers
|
54 |
Action: General Football Chat
|
55 |
Action Input: How does the NFL draft work?
|
56 |
|
|
|
6 |
|
7 |
Do not answer any questions using your pre-trained knowledge, only use the information provided in the context.
|
8 |
|
9 |
+
IMPORTANT TOOL SELECTION GUIDELINES (Use in this order of priority):
|
10 |
+
1. Use "Player Information Search" FIRST for any questions about a SPECIFIC player (identified by name or jersey number) asking for details, stats, info card, headshot, or social media.
|
11 |
+
2. Use "Game Recap" FIRST for any questions asking for details, summaries, or visual information about a SPECIFIC game (identified by opponent or date).
|
12 |
+
3. Use "49ers Graph Search" for broader 49ers queries about GROUPS of players (e.g., list by position), general team info, schedules, fan chapters, or if Player/Game tools are not specific enough or fail.
|
13 |
+
4. ONLY use "Game Summary Search" if the "Game Recap" tool fails or doesn't provide enough detail for a specific game summary.
|
14 |
+
5. ONLY use "General Football Chat" for non-49ers football questions.
|
15 |
|
16 |
+
When in doubt between "Player Information Search" and "49ers Graph Search" for a player query, prefer "Player Information Search" if it seems to be about one specific player.
|
17 |
+
If unsure which 49ers tool to use, use "49ers Graph Search" as a general fallback.
|
18 |
|
19 |
TOOLS:
|
20 |
------
|
|
|
39 |
Final Answer: [your response here]
|
40 |
```
|
41 |
|
42 |
+
Example 1 (Specific Player):
|
43 |
+
User: "Tell me about Brock Purdy"
|
44 |
+
Thought: The user is asking for details about a specific player, Brock Purdy. I should use the "Player Information Search" tool first.
|
45 |
+
Action: Player Information Search
|
46 |
+
Action Input: Tell me about Brock Purdy
|
47 |
+
|
48 |
+
Example 2 (Specific Game):
|
49 |
+
User: "Show me the recap of the 49ers vs Jets game"
|
50 |
+
Thought: The user wants a recap and potentially visual info for a specific game. I should use the "Game Recap" tool first.
|
51 |
+
Action: Game Recap
|
52 |
+
Action Input: Show me the recap of the 49ers vs Jets game
|
53 |
|
54 |
+
Example 3 (Group of Players):
|
55 |
+
User: "List all the running backs"
|
56 |
+
Thought: The user is asking for a list of players based on position, not one specific player. "Player Information Search" isn't right. "49ers Graph Search" is the appropriate tool for this broader query.
|
57 |
+
Action: 49ers Graph Search
|
58 |
+
Action Input: List all 49ers running backs
|
59 |
|
60 |
+
Example 4 (General Football Question):
|
61 |
User: "How does the NFL draft work?"
|
62 |
+
Thought: This is asking about general NFL rules, not specific to the 49ers. I should use the "General Football Chat" tool.
|
63 |
Action: General Football Chat
|
64 |
Action Input: How does the NFL draft work?
|
65 |
|
test.ipynb
ADDED
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "code",
|
5 |
+
"execution_count": 1,
|
6 |
+
"metadata": {},
|
7 |
+
"outputs": [
|
8 |
+
{
|
9 |
+
"data": {
|
10 |
+
"text/html": [
|
11 |
+
"<style>\n",
|
12 |
+
".card {\n",
|
13 |
+
" background-color: #f9f9f9;\n",
|
14 |
+
" border: 1px solid #ddd;\n",
|
15 |
+
" margin: 0.5rem;\n",
|
16 |
+
" padding: 1rem;\n",
|
17 |
+
" border-radius: 8px;\n",
|
18 |
+
" width: 300px; \n",
|
19 |
+
" box-sizing: border-box;\n",
|
20 |
+
" font-family: sans-serif;\n",
|
21 |
+
"}\n",
|
22 |
+
".player-photo {\n",
|
23 |
+
" width: 80px;\n",
|
24 |
+
" height: 80px;\n",
|
25 |
+
" background-color: #ccc; /* placeholder if no real image */\n",
|
26 |
+
" float: left;\n",
|
27 |
+
" margin-right: 1rem;\n",
|
28 |
+
" border-radius: 4px;\n",
|
29 |
+
"}\n",
|
30 |
+
".card-content h4 {\n",
|
31 |
+
" margin: 0 0 0.25rem 0;\n",
|
32 |
+
" font-size: 1.1rem;\n",
|
33 |
+
"}\n",
|
34 |
+
".card-content ul {\n",
|
35 |
+
" margin: 0.25rem 0;\n",
|
36 |
+
" padding-left: 1.2rem;\n",
|
37 |
+
"}\n",
|
38 |
+
".game-recap {\n",
|
39 |
+
" display: flex;\n",
|
40 |
+
" align-items: center;\n",
|
41 |
+
" justify-content: space-around;\n",
|
42 |
+
" background-color: #f9f9f9;\n",
|
43 |
+
" border: 1px solid #ddd;\n",
|
44 |
+
" margin: 0.5rem;\n",
|
45 |
+
" padding: 1rem;\n",
|
46 |
+
" border-radius: 8px;\n",
|
47 |
+
" width: 350px;\n",
|
48 |
+
" box-sizing: border-box;\n",
|
49 |
+
" font-family: sans-serif;\n",
|
50 |
+
"}\n",
|
51 |
+
".team-block {\n",
|
52 |
+
" text-align: center;\n",
|
53 |
+
"}\n",
|
54 |
+
".team-block img {\n",
|
55 |
+
" width: 50px;\n",
|
56 |
+
" display: block;\n",
|
57 |
+
" margin: 0 auto 0.25rem auto;\n",
|
58 |
+
"}\n",
|
59 |
+
".team-name {\n",
|
60 |
+
" font-weight: 600;\n",
|
61 |
+
" font-size: 1rem;\n",
|
62 |
+
"}\n",
|
63 |
+
".score {\n",
|
64 |
+
" font-size: 1.2rem;\n",
|
65 |
+
"}\n",
|
66 |
+
".winner .team-name {\n",
|
67 |
+
" color: gold;\n",
|
68 |
+
"}\n",
|
69 |
+
".summary {\n",
|
70 |
+
" flex: 1;\n",
|
71 |
+
" margin-left: 1rem;\n",
|
72 |
+
" font-size: 0.9rem;\n",
|
73 |
+
"}\n",
|
74 |
+
"</style>\n",
|
75 |
+
"<div class=\"card player-card\">\n",
|
76 |
+
" <div class=\"player-photo\" alt=\"Deebo Samuel Photo\"></div>\n",
|
77 |
+
" <div class=\"card-content\">\n",
|
78 |
+
" <h4>Deebo Samuel – WR</h4>\n",
|
79 |
+
" <ul>\n",
|
80 |
+
" <li>2024 Season Yards: 1,050</li>\n",
|
81 |
+
" <li>Touchdowns: 8</li>\n",
|
82 |
+
" <li>Best Game: 150 yds vs Rams</li>\n",
|
83 |
+
" </ul>\n",
|
84 |
+
" </div>\n",
|
85 |
+
"</div>\n",
|
86 |
+
"\n",
|
87 |
+
"<div class=\"game-recap\">\n",
|
88 |
+
" <div class=\"team-block away\">\n",
|
89 |
+
" <img src=\"\" class=\"team-logo\" alt=\"Eagles Logo\" />\n",
|
90 |
+
" <div class=\"team-name\">Eagles</div>\n",
|
91 |
+
" <div class=\"score\">24</div>\n",
|
92 |
+
" </div>\n",
|
93 |
+
" <div class=\"team-block home winner\">\n",
|
94 |
+
" <img src=\"\" class=\"team-logo\" alt=\"49ers Logo\" />\n",
|
95 |
+
" <div class=\"team-name\">49ers</div>\n",
|
96 |
+
" <div class=\"score\">30</div>\n",
|
97 |
+
" </div>\n",
|
98 |
+
" <p class=\"summary\">The 49ers rallied in the 4th quarter to win at home. \n",
|
99 |
+
" <b>Player of the Game:</b> Deebo Samuel with 2 TDs.</p>\n",
|
100 |
+
"</div>\n"
|
101 |
+
],
|
102 |
+
"text/plain": [
|
103 |
+
"<IPython.core.display.HTML object>"
|
104 |
+
]
|
105 |
+
},
|
106 |
+
"execution_count": 1,
|
107 |
+
"metadata": {},
|
108 |
+
"output_type": "execute_result"
|
109 |
+
}
|
110 |
+
],
|
111 |
+
"source": [
|
112 |
+
"import IPython.display as disp\n",
|
113 |
+
"\n",
|
114 |
+
"# Minimal CSS to style a player stats card and a game recap layout:\n",
|
115 |
+
"css = \"\"\"\n",
|
116 |
+
".card {\n",
|
117 |
+
" background-color: #f9f9f9;\n",
|
118 |
+
" border: 1px solid #ddd;\n",
|
119 |
+
" margin: 0.5rem;\n",
|
120 |
+
" padding: 1rem;\n",
|
121 |
+
" border-radius: 8px;\n",
|
122 |
+
" width: 300px; \n",
|
123 |
+
" box-sizing: border-box;\n",
|
124 |
+
" font-family: sans-serif;\n",
|
125 |
+
"}\n",
|
126 |
+
".player-photo {\n",
|
127 |
+
" width: 80px;\n",
|
128 |
+
" height: 80px;\n",
|
129 |
+
" background-color: #ccc; /* placeholder if no real image */\n",
|
130 |
+
" float: left;\n",
|
131 |
+
" margin-right: 1rem;\n",
|
132 |
+
" border-radius: 4px;\n",
|
133 |
+
"}\n",
|
134 |
+
".card-content h4 {\n",
|
135 |
+
" margin: 0 0 0.25rem 0;\n",
|
136 |
+
" font-size: 1.1rem;\n",
|
137 |
+
"}\n",
|
138 |
+
".card-content ul {\n",
|
139 |
+
" margin: 0.25rem 0;\n",
|
140 |
+
" padding-left: 1.2rem;\n",
|
141 |
+
"}\n",
|
142 |
+
".game-recap {\n",
|
143 |
+
" display: flex;\n",
|
144 |
+
" align-items: center;\n",
|
145 |
+
" justify-content: space-around;\n",
|
146 |
+
" background-color: #f9f9f9;\n",
|
147 |
+
" border: 1px solid #ddd;\n",
|
148 |
+
" margin: 0.5rem;\n",
|
149 |
+
" padding: 1rem;\n",
|
150 |
+
" border-radius: 8px;\n",
|
151 |
+
" width: 350px;\n",
|
152 |
+
" box-sizing: border-box;\n",
|
153 |
+
" font-family: sans-serif;\n",
|
154 |
+
"}\n",
|
155 |
+
".team-block {\n",
|
156 |
+
" text-align: center;\n",
|
157 |
+
"}\n",
|
158 |
+
".team-block img {\n",
|
159 |
+
" width: 50px;\n",
|
160 |
+
" display: block;\n",
|
161 |
+
" margin: 0 auto 0.25rem auto;\n",
|
162 |
+
"}\n",
|
163 |
+
".team-name {\n",
|
164 |
+
" font-weight: 600;\n",
|
165 |
+
" font-size: 1rem;\n",
|
166 |
+
"}\n",
|
167 |
+
".score {\n",
|
168 |
+
" font-size: 1.2rem;\n",
|
169 |
+
"}\n",
|
170 |
+
".winner .team-name {\n",
|
171 |
+
" color: gold;\n",
|
172 |
+
"}\n",
|
173 |
+
".summary {\n",
|
174 |
+
" flex: 1;\n",
|
175 |
+
" margin-left: 1rem;\n",
|
176 |
+
" font-size: 0.9rem;\n",
|
177 |
+
"}\n",
|
178 |
+
"\"\"\"\n",
|
179 |
+
"\n",
|
180 |
+
"# Sample HTML for a player stats card:\n",
|
181 |
+
"player_html = \"\"\"\n",
|
182 |
+
"<div class=\"card player-card\">\n",
|
183 |
+
" <div class=\"player-photo\" alt=\"Deebo Samuel Photo\"></div>\n",
|
184 |
+
" <div class=\"card-content\">\n",
|
185 |
+
" <h4>Deebo Samuel – WR</h4>\n",
|
186 |
+
" <ul>\n",
|
187 |
+
" <li>2024 Season Yards: 1,050</li>\n",
|
188 |
+
" <li>Touchdowns: 8</li>\n",
|
189 |
+
" <li>Best Game: 150 yds vs Rams</li>\n",
|
190 |
+
" </ul>\n",
|
191 |
+
" </div>\n",
|
192 |
+
"</div>\n",
|
193 |
+
"\"\"\"\n",
|
194 |
+
"\n",
|
195 |
+
"# Sample HTML for a game recap layout:\n",
|
196 |
+
"game_html = \"\"\"\n",
|
197 |
+
"<div class=\"game-recap\">\n",
|
198 |
+
" <div class=\"team-block away\">\n",
|
199 |
+
" <img src=\"\" class=\"team-logo\" alt=\"Eagles Logo\" />\n",
|
200 |
+
" <div class=\"team-name\">Eagles</div>\n",
|
201 |
+
" <div class=\"score\">24</div>\n",
|
202 |
+
" </div>\n",
|
203 |
+
" <div class=\"team-block home winner\">\n",
|
204 |
+
" <img src=\"\" class=\"team-logo\" alt=\"49ers Logo\" />\n",
|
205 |
+
" <div class=\"team-name\">49ers</div>\n",
|
206 |
+
" <div class=\"score\">30</div>\n",
|
207 |
+
" </div>\n",
|
208 |
+
" <p class=\"summary\">The 49ers rallied in the 4th quarter to win at home. \n",
|
209 |
+
" <b>Player of the Game:</b> Deebo Samuel with 2 TDs.</p>\n",
|
210 |
+
"</div>\n",
|
211 |
+
"\"\"\"\n",
|
212 |
+
"\n",
|
213 |
+
"rendered_html = f\"<style>{css}</style>{player_html}{game_html}\"\n",
|
214 |
+
"disp.HTML(rendered_html)\n"
|
215 |
+
]
|
216 |
+
},
|
217 |
+
{
|
218 |
+
"cell_type": "code",
|
219 |
+
"execution_count": null,
|
220 |
+
"metadata": {},
|
221 |
+
"outputs": [],
|
222 |
+
"source": []
|
223 |
+
}
|
224 |
+
],
|
225 |
+
"metadata": {
|
226 |
+
"kernelspec": {
|
227 |
+
"display_name": "base",
|
228 |
+
"language": "python",
|
229 |
+
"name": "python3"
|
230 |
+
},
|
231 |
+
"language_info": {
|
232 |
+
"codemirror_mode": {
|
233 |
+
"name": "ipython",
|
234 |
+
"version": 3
|
235 |
+
},
|
236 |
+
"file_extension": ".py",
|
237 |
+
"mimetype": "text/x-python",
|
238 |
+
"name": "python",
|
239 |
+
"nbconvert_exporter": "python",
|
240 |
+
"pygments_lexer": "ipython3",
|
241 |
+
"version": "3.12.7"
|
242 |
+
}
|
243 |
+
},
|
244 |
+
"nbformat": 4,
|
245 |
+
"nbformat_minor": 2
|
246 |
+
}
|
tools/player_search.py
ADDED
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Player Search - LangChain tool for retrieving player information from Neo4j
|
3 |
+
|
4 |
+
This module provides functions to:
|
5 |
+
1. Search for players in Neo4j based on natural language queries.
|
6 |
+
2. Generate text summaries about players.
|
7 |
+
3. Return both text summaries and structured data for UI components.
|
8 |
+
"""
|
9 |
+
|
10 |
+
# Import Gradio-specific modules directly
|
11 |
+
import sys
|
12 |
+
import os
|
13 |
+
# Add parent directory to path to access gradio modules
|
14 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
15 |
+
from gradio_llm import llm
|
16 |
+
from gradio_graph import graph
|
17 |
+
from langchain_neo4j import GraphCypherQAChain
|
18 |
+
from langchain_core.prompts import PromptTemplate
|
19 |
+
|
20 |
+
# Create a global variable to store the last retrieved player data
|
21 |
+
# Workaround for LangChain dropping structured data
|
22 |
+
LAST_PLAYER_DATA = None
|
23 |
+
|
24 |
+
# Function to get the cached player data
|
25 |
+
def get_last_player_data():
|
26 |
+
global LAST_PLAYER_DATA
|
27 |
+
print(f"GETTING PLAYER DATA FROM CACHE: {LAST_PLAYER_DATA}")
|
28 |
+
return LAST_PLAYER_DATA
|
29 |
+
|
30 |
+
# Function to set the cached player data
|
31 |
+
def set_last_player_data(player_data):
|
32 |
+
global LAST_PLAYER_DATA
|
33 |
+
LAST_PLAYER_DATA = player_data
|
34 |
+
print(f"STORED PLAYER DATA IN CACHE: {player_data}")
|
35 |
+
|
36 |
+
# Clear the cache initially
|
37 |
+
set_last_player_data(None)
|
38 |
+
|
39 |
+
# Create the Cypher generation prompt for player search
|
40 |
+
PLAYER_SEARCH_TEMPLATE = """
|
41 |
+
You are an expert Neo4j Developer translating user questions about NFL players into Cypher queries.
|
42 |
+
Your goal is to find a specific player or group of players in the database based on the user's description.
|
43 |
+
|
44 |
+
Convert the user's question based on the schema provided.
|
45 |
+
|
46 |
+
IMPORTANT NOTES:
|
47 |
+
1. Always return the FULL player node with ALL its relevant properties for display.
|
48 |
+
Specifically include: `player_id`, `Name`, `Position`, `Jersey_number`, `College`, `Height`, `Weight`, `Years_in_nfl`, `headshot_url`, `instagram_url`, `highlight_video_url`.
|
49 |
+
2. Always use case-insensitive comparisons using `toLower()` for string properties like Name, Position, College.
|
50 |
+
3. If searching by name, use CONTAINS for flexibility (e.g., `toLower(p.Name) CONTAINS toLower("bosa")`).
|
51 |
+
4. If searching by number, ensure the number property (`p.Jersey_number`) is matched correctly (it's likely stored as an integer or string, check schema).
|
52 |
+
5. NEVER use the embedding property.
|
53 |
+
6. Limit results to 1 if the user asks for a specific player, but allow multiple for general queries (e.g., "list all QBs"). Default to LIMIT 5 if multiple results are possible and no limit is specified.
|
54 |
+
|
55 |
+
Example Questions and Queries:
|
56 |
+
|
57 |
+
1. "Who is Nick Bosa?"
|
58 |
+
```
|
59 |
+
MATCH (p:Player)
|
60 |
+
WHERE toLower(p.Name) CONTAINS toLower("Nick Bosa")
|
61 |
+
RETURN p.player_id, p.Name, p.Position, p.Jersey_number, p.College, p.Height, p.Weight, p.Years_in_nfl, p.headshot_url, p.instagram_url, p.highlight_video_url
|
62 |
+
LIMIT 1
|
63 |
+
```
|
64 |
+
|
65 |
+
2. "Tell me about player number 13"
|
66 |
+
```
|
67 |
+
MATCH (p:Player)
|
68 |
+
WHERE p.Jersey_number = 13 OR p.Jersey_number = "13" // Adapt based on schema type
|
69 |
+
RETURN p.player_id, p.Name, p.Position, p.Jersey_number, p.College, p.Height, p.Weight, p.Years_in_nfl, p.headshot_url, p.instagram_url, p.highlight_video_url
|
70 |
+
LIMIT 1
|
71 |
+
```
|
72 |
+
|
73 |
+
3. "List all quarterbacks"
|
74 |
+
```
|
75 |
+
MATCH (p:Player)
|
76 |
+
WHERE toLower(p.Position) = toLower("QB")
|
77 |
+
RETURN p.player_id, p.Name, p.Position, p.Jersey_number, p.College, p.Height, p.Weight, p.Years_in_nfl, p.headshot_url, p.instagram_url, p.highlight_video_url
|
78 |
+
ORDER BY p.Name
|
79 |
+
LIMIT 5
|
80 |
+
```
|
81 |
+
|
82 |
+
4. "Find players from Central Florida"
|
83 |
+
```
|
84 |
+
MATCH (p:Player)
|
85 |
+
WHERE toLower(p.College) CONTAINS toLower("Central Florida")
|
86 |
+
RETURN p.player_id, p.Name, p.Position, p.Jersey_number, p.College, p.Height, p.Weight, p.Years_in_nfl, p.headshot_url, p.instagram_url, p.highlight_video_url
|
87 |
+
ORDER BY p.Name
|
88 |
+
LIMIT 5
|
89 |
+
```
|
90 |
+
|
91 |
+
Schema:
|
92 |
+
{schema}
|
93 |
+
|
94 |
+
Question:
|
95 |
+
{question}
|
96 |
+
"""
|
97 |
+
|
98 |
+
player_search_prompt = PromptTemplate.from_template(PLAYER_SEARCH_TEMPLATE)
|
99 |
+
|
100 |
+
# Create the player summary generation prompt
|
101 |
+
PLAYER_SUMMARY_TEMPLATE = """
|
102 |
+
You are a helpful AI assistant providing information about an NFL player.
|
103 |
+
Based on the following data, write a concise 1-2 sentence summary.
|
104 |
+
Focus on their name, position, and maybe college or experience.
|
105 |
+
|
106 |
+
Data:
|
107 |
+
- Name: {Name}
|
108 |
+
- Position: {Position}
|
109 |
+
- Number: {Jersey_number}
|
110 |
+
- College: {College}
|
111 |
+
- Experience (Years): {Years_in_nfl}
|
112 |
+
|
113 |
+
Write the summary:
|
114 |
+
"""
|
115 |
+
|
116 |
+
player_summary_prompt = PromptTemplate.from_template(PLAYER_SUMMARY_TEMPLATE)
|
117 |
+
|
118 |
+
# Create the Cypher QA chain for player search
|
119 |
+
player_search_chain = GraphCypherQAChain.from_llm(
|
120 |
+
llm,
|
121 |
+
graph=graph,
|
122 |
+
verbose=True,
|
123 |
+
cypher_prompt=player_search_prompt,
|
124 |
+
return_direct=True, # Return raw results
|
125 |
+
allow_dangerous_requests=True
|
126 |
+
)
|
127 |
+
|
128 |
+
# Function to parse player data from Cypher result
|
129 |
+
def parse_player_data(result):
|
130 |
+
"""Parse the player data from the Cypher result into a structured dictionary."""
|
131 |
+
if not result or not isinstance(result, list) or len(result) == 0:
|
132 |
+
print("Parsing player data: No result found.")
|
133 |
+
return None
|
134 |
+
|
135 |
+
# Assuming the query returns one player row or we take the first if multiple
|
136 |
+
player = result[0]
|
137 |
+
print(f"Parsing player data: Raw result item: {player}")
|
138 |
+
|
139 |
+
# Extract properties using the defined map, checking ONLY for the prefixed keys
|
140 |
+
parsed_data = {}
|
141 |
+
# Corrected key map to use lowercase property names matching Cypher output
|
142 |
+
key_map = {
|
143 |
+
# Key from Cypher result : Key for output dictionary
|
144 |
+
'p.player_id': 'player_id',
|
145 |
+
'p.name': 'Name', # Corrected case
|
146 |
+
'p.position': 'Position', # Corrected case
|
147 |
+
'p.jersey_number': 'Jersey_number', # Corrected case
|
148 |
+
'p.college': 'College', # Corrected case
|
149 |
+
'p.height': 'Height', # Corrected case
|
150 |
+
'p.weight': 'Weight', # Corrected case
|
151 |
+
'p.years_in_nfl': 'Years_in_nfl', # Corrected case
|
152 |
+
'p.headshot_url': 'headshot_url',
|
153 |
+
'p.instagram_url': 'instagram_url',
|
154 |
+
'p.highlight_video_url': 'highlight_video_url'
|
155 |
+
}
|
156 |
+
|
157 |
+
for cypher_key, dict_key in key_map.items():
|
158 |
+
if cypher_key in player:
|
159 |
+
parsed_data[dict_key] = player[cypher_key]
|
160 |
+
# else: # Optional: Log if a specific key wasn't found
|
161 |
+
# print(f"Parsing player data: Key '{cypher_key}' not found in result.")
|
162 |
+
|
163 |
+
# Ensure essential keys were successfully mapped
|
164 |
+
if 'Name' not in parsed_data or 'player_id' not in parsed_data:
|
165 |
+
print("Parsing player data: Essential keys ('Name', 'player_id') were not successfully mapped from result.")
|
166 |
+
print(f"Available keys in result: {list(player.keys())}")
|
167 |
+
return None
|
168 |
+
|
169 |
+
print(f"Parsing player data: Parsed dictionary: {parsed_data}")
|
170 |
+
return parsed_data
|
171 |
+
|
172 |
+
# Function to generate a player summary using LLM
|
173 |
+
def generate_player_summary(player_data):
|
174 |
+
"""Generate a natural language summary of the player using the LLM."""
|
175 |
+
if not player_data:
|
176 |
+
return "I couldn't retrieve enough information to summarize the player."
|
177 |
+
|
178 |
+
try:
|
179 |
+
# Format the prompt with player data, providing defaults
|
180 |
+
formatted_prompt = player_summary_prompt.format(
|
181 |
+
Name=player_data.get('Name', 'N/A'),
|
182 |
+
Position=player_data.get('Position', 'N/A'),
|
183 |
+
Jersey_number=player_data.get('Jersey_number', 'N/A'),
|
184 |
+
College=player_data.get('College', 'N/A'),
|
185 |
+
Years_in_nfl=player_data.get('Years_in_nfl', 'N/A')
|
186 |
+
)
|
187 |
+
|
188 |
+
# Generate the summary using the LLM
|
189 |
+
summary = llm.invoke(formatted_prompt)
|
190 |
+
summary_content = summary.content if hasattr(summary, 'content') else str(summary)
|
191 |
+
print(f"Generated Player Summary: {summary_content}")
|
192 |
+
return summary_content
|
193 |
+
except Exception as e:
|
194 |
+
print(f"Error generating player summary: {str(e)}")
|
195 |
+
return f"Summary for {player_data.get('Name', 'this player')}."
|
196 |
+
|
197 |
+
# Main function to search for a player and generate output
|
198 |
+
def player_search_qa(input_text: str) -> dict:
|
199 |
+
"""
|
200 |
+
Searches for a player based on input text, generates a summary, and returns data.
|
201 |
+
|
202 |
+
Args:
|
203 |
+
input_text (str): Natural language query about a player.
|
204 |
+
|
205 |
+
Returns:
|
206 |
+
dict: Response containing text summary and structured player data.
|
207 |
+
"""
|
208 |
+
global LAST_PLAYER_DATA
|
209 |
+
set_last_player_data(None) # Clear cache at the start of each call
|
210 |
+
|
211 |
+
try:
|
212 |
+
# Log the incoming query
|
213 |
+
print(f"--- Processing Player Search Query: {input_text} ---")
|
214 |
+
|
215 |
+
# Search for the player using the Cypher chain
|
216 |
+
search_result = player_search_chain.invoke({"query": input_text})
|
217 |
+
print(f"Raw search result from chain: {search_result}")
|
218 |
+
|
219 |
+
# Check if we have a result and it's not empty
|
220 |
+
if not search_result or not search_result.get('result') or not isinstance(search_result['result'], list) or len(search_result['result']) == 0:
|
221 |
+
print("Player Search: No results found in Neo4j.")
|
222 |
+
return {
|
223 |
+
"output": "I couldn't find information about that player. Could you be more specific or try a different name/number?",
|
224 |
+
"player_data": None
|
225 |
+
}
|
226 |
+
|
227 |
+
# Parse the player data from the first result
|
228 |
+
player_data = parse_player_data(search_result['result'])
|
229 |
+
|
230 |
+
if not player_data:
|
231 |
+
print("Player Search: Failed to parse data from Neo4j result.")
|
232 |
+
return {
|
233 |
+
"output": "I found some information, but couldn't process the player details correctly.",
|
234 |
+
"player_data": None
|
235 |
+
}
|
236 |
+
|
237 |
+
# Generate the text summary
|
238 |
+
summary_text = generate_player_summary(player_data)
|
239 |
+
|
240 |
+
# Store the structured data in the cache for the UI component
|
241 |
+
set_last_player_data(player_data)
|
242 |
+
|
243 |
+
# Return both the text summary and the structured data
|
244 |
+
final_output = {
|
245 |
+
"output": summary_text,
|
246 |
+
"player_data": player_data # Include for potential direct use if caching fails
|
247 |
+
}
|
248 |
+
print(f"Final player_search_qa output: {final_output}")
|
249 |
+
return final_output
|
250 |
+
|
251 |
+
except Exception as e:
|
252 |
+
print(f"Error in player_search_qa: {str(e)}")
|
253 |
+
import traceback
|
254 |
+
traceback.print_exc()
|
255 |
+
set_last_player_data(None) # Clear cache on error
|
256 |
+
return {
|
257 |
+
"output": "I encountered an error while searching for the player. Please try again.",
|
258 |
+
"player_data": None
|
259 |
+
}
|