Spaces:
No application file
No application file
Liss, Alex (NYC-HUG)
commited on
Commit
·
f3a3878
1
Parent(s):
20ecd50
WIP implementation of team search, have improved component display of game and player search too
Browse files- components/team_story_component.py +63 -0
- gradio_agent.py +10 -2
- gradio_app.py +83 -67
- tools/team_story.py +146 -0
components/team_story_component.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Gradio component for displaying Team Story/News search results.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import gradio as gr
|
6 |
+
|
7 |
+
def create_team_story_component(team_story_data):
|
8 |
+
"""
|
9 |
+
Creates a Gradio HTML component to display formatted team news articles.
|
10 |
+
|
11 |
+
Args:
|
12 |
+
team_story_data (list): A list of dictionaries, where each dictionary
|
13 |
+
represents an article and contains keys like
|
14 |
+
'summary', 'link_to_article', and 'topic'.
|
15 |
+
|
16 |
+
Returns:
|
17 |
+
gr.HTML: A Gradio HTML component containing the formatted news stories.
|
18 |
+
Returns None if the input data is empty or invalid.
|
19 |
+
"""
|
20 |
+
if not team_story_data or not isinstance(team_story_data, list):
|
21 |
+
return None # Return None if no data or invalid data
|
22 |
+
|
23 |
+
html_content = """<div style='padding: 15px; border: 1px solid #e0e0e0; border-radius: 5px; margin-top: 10px;'>
|
24 |
+
<h3 style='margin-top: 0; margin-bottom: 10px;'>Recent Team News</h3>"""
|
25 |
+
|
26 |
+
for story in team_story_data:
|
27 |
+
if isinstance(story, dict):
|
28 |
+
summary = story.get('summary', 'No summary available.')
|
29 |
+
link = story.get('link_to_article', '#')
|
30 |
+
topic = story.get('topic', 'General')
|
31 |
+
|
32 |
+
# Sanitize link to prevent basic injection issues
|
33 |
+
safe_link = link if link.startswith(('http://', 'https://', '#')) else '#'
|
34 |
+
|
35 |
+
# Escape basic HTML characters in text fields
|
36 |
+
def escape_html(text):
|
37 |
+
return text.replace("&", "&").replace("<", "<").replace(">", ">")
|
38 |
+
|
39 |
+
safe_summary = escape_html(summary)
|
40 |
+
safe_topic = escape_html(topic)
|
41 |
+
|
42 |
+
html_content += f"""<div style='margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid #eee;'>
|
43 |
+
<p style='font-size: 0.9em; color: #555; margin-bottom: 5px;'><strong>Topic:</strong> {safe_topic}</p>
|
44 |
+
<p style='margin-bottom: 8px;'>{safe_summary}</p>
|
45 |
+
<a href='{safe_link}' target='_blank' style='font-size: 0.9em;'>Read Full Article</a>
|
46 |
+
</div>"""
|
47 |
+
else:
|
48 |
+
print(f"Warning: Skipping invalid item in team_story_data: {story}")
|
49 |
+
|
50 |
+
# Remove the last border-bottom if content was added
|
51 |
+
if len(team_story_data) > 0:
|
52 |
+
last_border_pos = html_content.rfind("border-bottom: 1px solid #eee;")
|
53 |
+
if last_border_pos != -1:
|
54 |
+
html_content = html_content[:last_border_pos] + html_content[last_border_pos:].replace("border-bottom: 1px solid #eee;", "")
|
55 |
+
|
56 |
+
html_content += "</div>"
|
57 |
+
|
58 |
+
# Return None if only the initial header was created (e.g., all items were invalid)
|
59 |
+
if html_content.strip() == """<div style='padding: 15px; border: 1px solid #e0e0e0; border-radius: 5px; margin-top: 10px;'>
|
60 |
+
<h3 style='margin-top: 0; margin-bottom: 10px;'>Recent Team News</h3></div>""".strip():
|
61 |
+
return None
|
62 |
+
|
63 |
+
return gr.HTML(html_content)
|
gradio_agent.py
CHANGED
@@ -20,8 +20,9 @@ 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 |
-
from tools.player_search import player_search_qa
|
|
|
25 |
|
26 |
# Create a basic chat chain for general football discussion
|
27 |
from langchain_core.prompts import ChatPromptTemplate
|
@@ -91,6 +92,13 @@ Examples: "Tell me about Brock Purdy", "Who is player number 97?", "Show me Nick
|
|
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.
|
|
|
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, get_last_game_data
|
24 |
+
from tools.player_search import player_search_qa, get_last_player_data
|
25 |
+
from tools.team_story import team_story_qa, get_last_team_story_data
|
26 |
|
27 |
# Create a basic chat chain for general football discussion
|
28 |
from langchain_core.prompts import ChatPromptTemplate
|
|
|
92 |
Returns text summary and potentially visual card data.""",
|
93 |
func=player_search_qa
|
94 |
),
|
95 |
+
Tool.from_function(
|
96 |
+
name="Team News Search",
|
97 |
+
description="""Use for questions about recent 49ers news, articles, summaries, or specific topics like 'draft' or 'roster moves'.
|
98 |
+
Examples: 'What's the latest team news?', 'Summarize recent articles about the draft', 'Any news about the offensive line?'
|
99 |
+
Returns text summary and potentially structured article data.""",
|
100 |
+
func=team_story_qa
|
101 |
+
),
|
102 |
Tool.from_function(
|
103 |
name="Game Recap",
|
104 |
description="""Use SPECIFICALLY for detailed game recaps or when users want to see visual information about a particular game identified by opponent or date.
|
gradio_app.py
CHANGED
@@ -11,6 +11,7 @@ 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
|
@@ -19,6 +20,7 @@ from gradio_agent import generate_response
|
|
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 = """
|
@@ -286,20 +288,18 @@ 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 |
-
#
|
290 |
-
debug_textbox = gr.Textbox(label="Debug Player Data", visible=True, interactive=False)
|
291 |
-
#
|
292 |
-
|
293 |
-
|
294 |
-
|
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", #
|
303 |
render_markdown=True
|
304 |
)
|
305 |
|
@@ -314,76 +314,92 @@ with gr.Blocks(title="49ers FanAI Hub", theme=gr.themes.Soft(), css=css) as demo
|
|
314 |
|
315 |
# Define a combined function for user input and bot response
|
316 |
async def process_and_respond(message, history):
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
#
|
339 |
-
|
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("
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
370 |
|
371 |
# Set up event handlers with the combined function
|
372 |
# Ensure outputs list matches the return values of process_and_respond
|
373 |
-
#
|
374 |
-
outputs_list = [msg, chatbot
|
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
|
382 |
def clear_chat():
|
383 |
-
|
|
|
384 |
|
385 |
-
# Update clear outputs
|
386 |
-
clear_btn.click(clear_chat, None, [
|
387 |
|
388 |
# Launch the app
|
389 |
if __name__ == "__main__":
|
|
|
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 |
+
from components.team_story_component import create_team_story_component
|
15 |
|
16 |
# Import the Gradio-compatible agent instead of the original agent
|
17 |
import gradio_agent
|
|
|
20 |
# Import cache getter functions
|
21 |
from tools.game_recap import get_last_game_data
|
22 |
from tools.player_search import get_last_player_data
|
23 |
+
from tools.team_story import get_last_team_story_data
|
24 |
|
25 |
# Define CSS directly
|
26 |
css = """
|
|
|
288 |
gr.Markdown("# 🏈 49ers FanAI Hub")
|
289 |
|
290 |
# --- Component Display Area --- #
|
291 |
+
# REMOVED Unused/Redundant Component Placeholders:
|
292 |
+
# debug_textbox = gr.Textbox(label="Debug Player Data", visible=True, interactive=False)
|
293 |
+
# player_card_display = gr.HTML(visible=False)
|
294 |
+
# game_recap_display = gr.HTML(visible=False)
|
295 |
+
|
296 |
+
# Chat interface - Components will be added directly here
|
|
|
|
|
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", # Using default tuple format as components are added
|
303 |
render_markdown=True
|
304 |
)
|
305 |
|
|
|
314 |
|
315 |
# Define a combined function for user input and bot response
|
316 |
async def process_and_respond(message, history):
|
317 |
+
"""Process user input, get agent response, check for components, and update history."""
|
318 |
+
|
319 |
+
print(f"process_and_respond: Received message: {message}")
|
320 |
+
# history.append((message, None)) # Add user message placeholder
|
321 |
+
# yield "", history # Show user message immediately
|
322 |
+
|
323 |
+
# Call the agent to get the response (text output + potentially populates cached data)
|
324 |
+
agent_response = generate_response(message, state.session_id)
|
325 |
+
text_output = agent_response.get("output", "Sorry, something went wrong.")
|
326 |
+
metadata = agent_response.get("metadata", {})
|
327 |
+
tools_used = metadata.get("tools_used", ["None"])
|
328 |
+
|
329 |
+
print(f"process_and_respond: Agent text output: {text_output}")
|
330 |
+
print(f"process_and_respond: Tools used: {tools_used}")
|
331 |
+
|
332 |
+
# Initialize response list with the text output
|
333 |
+
response_list = [(message, text_output)]
|
334 |
+
|
335 |
+
# Check for specific component data based on tools used or cached data
|
336 |
+
# Important: Call the getter functions *after* generate_response has run
|
337 |
+
|
338 |
+
# Check for Player Card
|
339 |
+
player_data = get_last_player_data()
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
if player_data:
|
341 |
+
print(f"process_and_respond: Found player data: {player_data}")
|
342 |
+
player_card_component = create_player_card_component(player_data)
|
343 |
+
if player_card_component:
|
344 |
+
response_list.append((None, player_card_component))
|
345 |
+
print("process_and_respond: Added player card component.")
|
346 |
+
else:
|
347 |
+
print("process_and_respond: Player data found but component creation failed.")
|
348 |
+
|
349 |
+
# Check for Game Recap
|
350 |
+
game_data = get_last_game_data()
|
351 |
+
if game_data:
|
352 |
+
print(f"process_and_respond: Found game data: {game_data}")
|
353 |
+
game_recap_comp = create_game_recap_component(game_data)
|
354 |
+
if game_recap_comp:
|
355 |
+
response_list.append((None, game_recap_comp))
|
356 |
+
print("process_and_respond: Added game recap component.")
|
357 |
+
else:
|
358 |
+
print("process_and_respond: Game data found but component creation failed.")
|
359 |
+
|
360 |
+
# Check for Team Story --- NEW ---
|
361 |
+
team_story_data = get_last_team_story_data()
|
362 |
+
if team_story_data:
|
363 |
+
print(f"process_and_respond: Found team story data: {team_story_data}")
|
364 |
+
team_story_comp = create_team_story_component(team_story_data)
|
365 |
+
if team_story_comp:
|
366 |
+
response_list.append((None, team_story_comp))
|
367 |
+
print("process_and_respond: Added team story component.")
|
368 |
+
else:
|
369 |
+
print("process_and_respond: Team story data found but component creation failed.")
|
370 |
+
|
371 |
+
# Update history with all parts of the response (text + components)
|
372 |
+
# Gradio's Chatbot handles lists of (user, assistant) tuples,
|
373 |
+
# where assistant can be text or a Gradio component.
|
374 |
+
# We replace the last entry (user, None) with the actual response items.
|
375 |
+
|
376 |
+
# Gradio manages history display; we just return the latest exchange.
|
377 |
+
# The actual history state is managed elsewhere (e.g., Zep, Neo4j history)
|
378 |
+
|
379 |
+
# Return the combined response list to update the chatbot UI
|
380 |
+
# The first element is user message + assistant text response
|
381 |
+
# Subsequent elements are None + UI component
|
382 |
+
print(f"process_and_respond: Final response list for UI: {response_list}")
|
383 |
+
# Return values suitable for outputs: [msg, chatbot]
|
384 |
+
return "", response_list # Return empty string for msg, list for chatbot
|
385 |
|
386 |
# Set up event handlers with the combined function
|
387 |
# Ensure outputs list matches the return values of process_and_respond
|
388 |
+
# REMOVED redundant components from outputs_list
|
389 |
+
outputs_list = [msg, chatbot]
|
390 |
msg.submit(process_and_respond, [msg, chatbot], outputs_list)
|
391 |
submit_btn.click(process_and_respond, [msg, chatbot], outputs_list)
|
392 |
|
393 |
# Add a clear button
|
394 |
clear_btn = gr.Button("Clear Conversation")
|
395 |
|
396 |
+
# Clear function - now only needs to clear msg and chatbot
|
397 |
def clear_chat():
|
398 |
+
# Return empty values for msg and chatbot
|
399 |
+
return "", []
|
400 |
|
401 |
+
# Update clear outputs - only need msg and chatbot
|
402 |
+
clear_btn.click(clear_chat, None, [msg, chatbot])
|
403 |
|
404 |
# Launch the app
|
405 |
if __name__ == "__main__":
|
tools/team_story.py
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Tool for querying Neo4j about recent team news stories.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import os
|
6 |
+
import sys
|
7 |
+
from dotenv import load_dotenv
|
8 |
+
from langchain_core.prompts import PromptTemplate
|
9 |
+
from langchain_openai import ChatOpenAI
|
10 |
+
from langchain_community.chains.graph_qa.cypher import GraphCypherQAChain
|
11 |
+
|
12 |
+
# Adjust path to import graph object and LLM from the parent directory
|
13 |
+
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
14 |
+
if parent_dir not in sys.path:
|
15 |
+
sys.path.append(parent_dir)
|
16 |
+
|
17 |
+
try:
|
18 |
+
from gradio_graph import graph # Import the configured graph instance
|
19 |
+
from gradio_llm import llm # Import the configured LLM instance
|
20 |
+
except ImportError as e:
|
21 |
+
print(f"Error importing graph or llm: {e}")
|
22 |
+
print("Please ensure gradio_graph.py and gradio_llm.py exist and are configured correctly.")
|
23 |
+
sys.exit(1)
|
24 |
+
|
25 |
+
# Load environment variables if needed (though graph/llm should be configured)
|
26 |
+
load_dotenv()
|
27 |
+
|
28 |
+
# Define the prompt for translating NL query to Cypher for Team Story
|
29 |
+
CYPHER_TEAM_STORY_GENERATION_TEMPLATE = """
|
30 |
+
Task: Generate Cypher query to query a graph database.
|
31 |
+
Instructions:
|
32 |
+
Use only the provided relationship types and properties in the schema.
|
33 |
+
Do not use any other relationship types or properties that are not provided.
|
34 |
+
Schema:
|
35 |
+
{schema}
|
36 |
+
Note: Do not include any explanations or apologies in your responses.
|
37 |
+
Do not respond to any questions that might ask anything else than for you to construct a Cypher query.
|
38 |
+
Do not include any text except the generated Cypher query.
|
39 |
+
|
40 |
+
The question is:
|
41 |
+
{query}
|
42 |
+
|
43 |
+
Cypher Query:
|
44 |
+
"""
|
45 |
+
|
46 |
+
CYPHER_TEAM_STORY_GENERATION_PROMPT = PromptTemplate(
|
47 |
+
input_variables=["schema", "query"], template=CYPHER_TEAM_STORY_GENERATION_TEMPLATE
|
48 |
+
)
|
49 |
+
|
50 |
+
# Placeholder for structured data caching (similar to player_search/game_recap)
|
51 |
+
LAST_TEAM_STORY_DATA = []
|
52 |
+
|
53 |
+
def get_last_team_story_data():
|
54 |
+
"""Returns the structured data from the last team story query."""
|
55 |
+
return LAST_TEAM_STORY_DATA
|
56 |
+
|
57 |
+
def team_story_qa(query: str) -> dict:
|
58 |
+
"""
|
59 |
+
Queries the Neo4j database for team news stories based on the user query.
|
60 |
+
|
61 |
+
Args:
|
62 |
+
query: The natural language query from the user.
|
63 |
+
|
64 |
+
Returns:
|
65 |
+
A dictionary containing the 'output' text and potentially structured 'team_story_data'.
|
66 |
+
"""
|
67 |
+
global LAST_TEAM_STORY_DATA
|
68 |
+
LAST_TEAM_STORY_DATA = [] # Clear previous results
|
69 |
+
|
70 |
+
print(f"--- Running Team Story QA for query: {query} ---")
|
71 |
+
|
72 |
+
try:
|
73 |
+
# Initialize the QA chain for Team Story
|
74 |
+
team_story_chain = GraphCypherQAChain.from_llm(
|
75 |
+
llm=llm, # Use the pre-configured LLM
|
76 |
+
graph=graph, # Use the pre-configured graph connection
|
77 |
+
cypher_prompt=CYPHER_TEAM_STORY_GENERATION_PROMPT,
|
78 |
+
verbose=True, # Set to True for debugging Cypher generation
|
79 |
+
return_intermediate_steps=True, # Useful for debugging
|
80 |
+
return_direct=False # Return the final answer directly? Set to False for now
|
81 |
+
)
|
82 |
+
|
83 |
+
# Use the GraphCypherQAChain to get results
|
84 |
+
# The chain handles NL -> Cypher -> Execution -> LLM response generation
|
85 |
+
result = team_story_chain.invoke({"query": query})
|
86 |
+
|
87 |
+
print(f"Raw result from team_story_chain: {result}")
|
88 |
+
|
89 |
+
# Extract relevant info (summaries, links) from the result['intermediate_steps']
|
90 |
+
# This structure depends on GraphCypherQAChain's output
|
91 |
+
# Typically intermediate_steps contains context (list of dicts)
|
92 |
+
structured_results = []
|
93 |
+
if 'intermediate_steps' in result and isinstance(result['intermediate_steps'], list):
|
94 |
+
for step in result['intermediate_steps']:
|
95 |
+
if 'context' in step and isinstance(step['context'], list):
|
96 |
+
for item in step['context']:
|
97 |
+
# Assuming item is a dict representing a :Team_Story node
|
98 |
+
if isinstance(item, dict):
|
99 |
+
story_data = {
|
100 |
+
'summary': item.get('s.summary', 'Summary not available'),
|
101 |
+
'link_to_article': item.get('s.link_to_article', '#'),
|
102 |
+
'topic': item.get('s.topic', 'Topic not available')
|
103 |
+
}
|
104 |
+
structured_results.append(story_data)
|
105 |
+
|
106 |
+
LAST_TEAM_STORY_DATA = structured_results
|
107 |
+
|
108 |
+
# Format the text output
|
109 |
+
if not structured_results:
|
110 |
+
output_text = result.get('result', "I couldn't find any specific news articles matching your query, but I can try a broader search.")
|
111 |
+
else:
|
112 |
+
# Simple text formatting for now
|
113 |
+
output_text = result.get('result', "Here's what I found:") + "\n\n"
|
114 |
+
for i, story in enumerate(structured_results[:3]): # Show top 3
|
115 |
+
output_text += f"{i+1}. {story['summary']}\n[Link: {story['link_to_article']}]\n\n"
|
116 |
+
if len(structured_results) > 3:
|
117 |
+
output_text += f"... found {len(structured_results)} relevant articles in total."
|
118 |
+
|
119 |
+
except Exception as e:
|
120 |
+
print(f"Error during team_story_qa: {e}")
|
121 |
+
output_text = "Sorry, I encountered an error trying to find team news."
|
122 |
+
LAST_TEAM_STORY_DATA = []
|
123 |
+
|
124 |
+
print(f"--- Team Story QA output: {output_text} ---")
|
125 |
+
print(f"--- Team Story QA structured data: {LAST_TEAM_STORY_DATA} ---")
|
126 |
+
|
127 |
+
# Return both text output and structured data (though structured data isn't used by agent yet)
|
128 |
+
return {"output": output_text, "team_story_data": LAST_TEAM_STORY_DATA}
|
129 |
+
|
130 |
+
# Example usage (for testing)
|
131 |
+
if __name__ == '__main__':
|
132 |
+
test_query = "What is the latest news about the 49ers draft?"
|
133 |
+
print(f"Testing team_story_qa with query: {test_query}")
|
134 |
+
response = team_story_qa(test_query)
|
135 |
+
print("\nResponse:")
|
136 |
+
print(response.get("output"))
|
137 |
+
print("\nStructured Data:")
|
138 |
+
print(response.get("team_story_data"))
|
139 |
+
|
140 |
+
test_query_2 = "Any updates on the roster?"
|
141 |
+
print(f"\nTesting team_story_qa with query: {test_query_2}")
|
142 |
+
response_2 = team_story_qa(test_query_2)
|
143 |
+
print("\nResponse:")
|
144 |
+
print(response_2.get("output"))
|
145 |
+
print("\nStructured Data:")
|
146 |
+
print(response_2.get("team_story_data"))
|