import queue from colorama import Fore, Style from langchain_core.callbacks import AsyncCallbackHandler from langchain_core.outputs.llm_result import LLMResult from typing import List from langchain_core.messages import BaseMessage TEAM_LOGO_BASE_URL = "https://huggingface.co/spaces/yamilsteven/ifx-assets/resolve/main/assets/team_logos/" image_base = """
Ht: {height} | Wt: {weight} lbs
Team: {team_name}
Experience: 6 Years
Instagram Profile{team_home}
{team1_score}
vs
{team_away}
{team2_score}
{city_display}
{team_page_cta_html}No goal highlights available.
" team1_logo_filename = GradioEventHandler.get_team_logo_slug(team1_name_str) team2_logo_filename = GradioEventHandler.get_team_logo_slug(team2_name_str) team1_logo_url = f"{TEAM_LOGO_BASE_URL}{team1_logo_filename}" team2_logo_url = f"{TEAM_LOGO_BASE_URL}{team2_logo_filename}" default_logo_url = f"{TEAM_LOGO_BASE_URL}default.png" formatted_game_html = game_card_html.format( game_title=game_title_str, team_home=team1_name_str, team_away=team2_name_str, team1_score=score_team1, team2_score=score_team2, highlights=highlights_html_str, team1_logo_url=team1_logo_url, team2_logo_url=team2_logo_url, default_logo_url=default_logo_url ) self.ots_box(formatted_game_html) return else: print(f"\n{Fore.RED}[TOOL END - SEMI-FINAL CARD] Not enough team data or structure not as expected for {raw_game_name}.{Style.RESET_ALL}") # Attempt to process Generic Game Card (e.g., type 'event') if not a Semi-Final elif output and isinstance(output, list) and len(output) > 0 and isinstance(output[0], object) and hasattr(output[0], 'metadata') and output[0].metadata.get('type') == 'event': game_teams_set = set() game_events_details = [] raw_game_name = "Unknown Game" for i, doc in enumerate(output): if hasattr(doc, 'metadata') and doc.metadata.get('team'): game_teams_set.add(doc.metadata.get('team')) if i == 0: raw_game_name = doc.metadata.get('game_name', raw_game_name) if hasattr(doc, 'metadata') and doc.metadata.get('event') == 'Goal': event_desc = doc.metadata.get('description', 'Goal scored') event_minute = doc.metadata.get('minute', '') event_team = doc.metadata.get('team', '') game_events_details.append(f"({event_minute}') {event_team}: {event_desc}") if len(game_teams_set) >= 1: team_list = list(game_teams_set) team1_name_str = team_list[0] team2_name_str = team_list[1] if len(team_list) > 1 else "(opponent not specified)" score_team1 = 0 score_team2 = 0 for doc in output: if hasattr(doc, 'metadata') and doc.metadata.get('event') == 'Goal': scoring_team = doc.metadata.get('team') if scoring_team == team1_name_str: score_team1 += 1 elif scoring_team == team2_name_str: score_team2 += 1 game_title_str = raw_game_name.replace('_', ' ').title() highlights_html_str = "No goal highlights available.
" team1_logo_filename = GradioEventHandler.get_team_logo_slug(team1_name_str) team2_logo_filename = GradioEventHandler.get_team_logo_slug(team2_name_str) team1_logo_url = f"{TEAM_LOGO_BASE_URL}{team1_logo_filename}" team2_logo_url = f"{TEAM_LOGO_BASE_URL}{team2_logo_filename}" default_logo_url = f"{TEAM_LOGO_BASE_URL}default.png" formatted_game_html = game_card_html.format( game_title=game_title_str, team_home=team1_name_str, team_away=team2_name_str, team1_score=score_team1, team2_score=score_team2, highlights=highlights_html_str, team1_logo_url=team1_logo_url, team2_logo_url=team2_logo_url, default_logo_url=default_logo_url ) self.ots_box(formatted_game_html) return else: print(f"\n{Fore.RED}[TOOL END - GAME CARD] Not enough team data found in events or output structure not as expected.{Style.RESET_ALL}") # Attempt to process Team Card if no Game Card was processed elif output and isinstance(output, list) and len(output) > 0 and isinstance(output[0], object) and hasattr(output[0], 'metadata') and output[0].metadata.get("show_team_card"): doc_for_team_card = output[0] team_name_from_meta = doc_for_team_card.metadata.get("team_name", "Unknown Team") city_raw = doc_for_team_card.metadata.get("city", "N/A") display_team_name = str(team_name_from_meta).replace('-', ' ').replace('_', ' ').title() city_display = city_raw.title() if city_raw != "N/A" else "Location N/A" logo_filename = GradioEventHandler.get_team_logo_slug(team_name_from_meta) team_specific_logo_url = f"{TEAM_LOGO_BASE_URL}{logo_filename}" current_default_logo_url = f"{TEAM_LOGO_BASE_URL}default.png" team_id_slug = doc_for_team_card.metadata.get("team_id", "") team_page_url = f"https://www.team.com/{team_id_slug}" if team_id_slug else "#" team_page_cta_html = f'''Visit Team Page''' formatted_team_card_html = team_info_card_html.format( team_logo_url=team_specific_logo_url, team_display_name=display_team_name, city_display=city_display, default_logo_url=current_default_logo_url, team_page_cta_html=team_page_cta_html ) self.ots_box(formatted_team_card_html) return # Attempt to process Player Card if no Game or Team Card was processed # This iterates through all docs in output, so it's more general elif output and isinstance(output, list): for doc in output: if hasattr(doc, 'metadata') and doc.metadata.get("show_profile_card"): raw_player_name = doc.metadata.get("name", "unknown-player") player_name = "Unknown Player" if raw_player_name != "unknown-player": parts = raw_player_name.split('-') if len(parts) == 2: first_name = parts[0].capitalize() last_name = parts[1].capitalize() player_name = f"{last_name} {first_name}" else: player_name = raw_player_name.replace('-', ' ').title() else: player_name = "Unknown Player" team_slug = doc.metadata.get("team", "unknown-team") formatted_team_name = team_slug.replace('-', ' ').title() if team_slug == "unknown-team": formatted_team_name = "Unknown Team" player_number = doc.metadata.get("number", "N/A") height = doc.metadata.get("height", "175") weight = doc.metadata.get("weight", "150") instagram_url = doc.metadata.get("instagram_url", raw_player_name) img = image_base.format( filename=self.get_image_filename(doc), player_name=player_name, team_name=formatted_team_name, player_number=player_number, height=height, weight=weight, instagram_url=instagram_url ) self.ots_box(img) return # Return after the first player card is found # Fallback if no specific card type was identified print(f"\n{Fore.YELLOW}[TOOL END] No specific card type processed for OTS.{Style.RESET_ALL}") async def on_tool_start(self, input: any, *args, **kwargs): self.info_box(input.get("name", "[TOOL START]")) async def on_workflow_end(self, state, *args, **kwargs): print(f"\n{Fore.CYAN}[WORKFLOW END]{Style.RESET_ALL}") self.queue.put(None) # for msg in state["messages"]: # print(f'{Fore.YELLOW}{msg.content}{Style.RESET_ALL}') @staticmethod def is_chat_stream_end(result: LLMResult) -> bool: try: content = result.generations[0][0].message.content return bool(content and content.strip()) except (IndexError, AttributeError): return False @staticmethod def get_image_filename(doc): return f'{team_image_map.get(doc.metadata.get("team"))}_{doc.metadata.get("number")}.png' @staticmethod def get_team_logo_slug(team_name: str) -> str: if not team_name or team_name == "(opponent not specified)": return "default.png" # Normalize accented for the Yucatán logo normalized_team_name = team_name.lower().replace('á', 'a') slug = normalized_team_name.replace(' ', '-') return slug + ".png"