import json from smolagents import CodeAgent, tool, LiteLLMModel import requests import yaml import os from collections import defaultdict from tools.final_answer import FinalAnswerTool import gradio as gr @tool def get_pokedex_entry(pokemon_name: str) -> str: """ Fetches the English Pokédex entry (flavor text) for the given Pokémon. Args: pokemon_name: Name or ID of the Pokémon (e.g., 'pikachu'). Returns: A string containing the Pokémon's English Pokédex entry or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon-species/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() flavor_texts = [ entry["flavor_text"] for entry in data.get("flavor_text_entries", []) if entry["language"]["name"] == "en" ] if not flavor_texts: return f"No English Pokédex entry available for '{pokemon_name}'." entry = flavor_texts[0].replace("\n", " ").replace("\f", " ") title = pokemon_name.strip().title() return f"{title}: {entry}" @tool def get_pokemon_basic_info(pokemon_name: str) -> str: """ Fetches basic information about a Pokémon, such as its ID, height, weight, and types. Args: pokemon_name: The name or ID of the Pokémon (e.g., 'pikachu'). Returns: A string summarizing the Pokémon's ID, height, weight, and types, or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() types = ', '.join([t['type']['name'].title() for t in data.get('types', [])]) return ( f"{data['name'].title()} (ID: {data['id']}):\n" f"Height: {data['height']} Weight: {data['weight']}\n" f"Types: {types}" ) @tool def get_pokemon_abilities(pokemon_name: str) -> str: """ Retrieves a list of abilities for a given Pokémon, distinguishing between normal and hidden abilities. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string listing the Pokémon's abilities, labeled as Normal or Hidden, or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() abilities = [] for ability_info in data.get('abilities', []): ability_name = ability_info['ability']['name'].replace('-', ' ').title() label = "Hidden" if ability_info.get('is_hidden', False) else "Normal" abilities.append(f"{ability_name} ({label})") if not abilities: return f"No abilities found for '{pokemon_name}'." return f"{pokemon_name.title()}'s abilities:\n" + "\n".join(abilities) @tool def get_pokemon_base_stats(pokemon_name: str) -> str: """ Fetches the base stats (HP, Attack, Defense, etc.) of a Pokémon. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string summarizing the Pokémon's base stats or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() stats = {s['stat']['name'].replace('-', ' ').title(): s['base_stat'] for s in data.get('stats', [])} if not stats: return f"No base stats found for '{pokemon_name}'." stats_str = ', '.join([f"{k}: {v}" for k, v in stats.items()]) return f"{pokemon_name.title()}'s base stats: {stats_str}" @tool def get_pokemon_evolutions(pokemon_name: str) -> str: """ Retrieves the evolution chain for a given Pokémon. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string listing the evolution chain or an error message. """ species_url = f"https://pokeapi.co/api/v2/pokemon-species/{pokemon_name.strip().lower()}/" species_response = requests.get(species_url, timeout=5) if species_response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {species_response.status_code})." species_data = species_response.json() evo_chain_url = species_data.get('evolution_chain', {}).get('url') if not evo_chain_url: return f"No evolution chain found for '{pokemon_name}'." evo_response = requests.get(evo_chain_url, timeout=5) if evo_response.status_code != 200: return f"Error fetching evolution chain (HTTP {evo_response.status_code})." evo_data = evo_response.json() def extract_chain(chain): names = [chain['species']['name'].title()] for evo in chain.get('evolves_to', []): names.extend(extract_chain(evo)) return names evo_names = extract_chain(evo_data['chain']) return f"Evolution chain: {' → '.join(evo_names)}" @tool def get_pokemon_moves_grouped(pokemon_name: str) -> str: """ Lists all moves a Pokémon can learn, grouped by move name, showing the minimum level, all learning methods, and all game versions. Egg moves are listed separately. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string listing the Pokémon's moves, grouped by name, with levels, methods, and versions. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() moves_dict = defaultdict(lambda: {'level': None, 'versions': set(), 'methods': set()}) egg_moves_dict = defaultdict(set) for move in data.get('moves', []): move_name = move['move']['name'].replace('-', ' ').title() for detail in move.get('version_group_details', []): method = detail['move_learn_method']['name'].replace('-', ' ').title() version = detail['version_group']['name'].replace('-', ' ').title() level = detail['level_learned_at'] if method == "Egg": egg_moves_dict[move_name].add(version) else: moves_dict[move_name]['methods'].add(method) moves_dict[move_name]['versions'].add(version) if method == "Level Up" and ( moves_dict[move_name]['level'] is None or level < moves_dict[move_name]['level']): moves_dict[move_name]['level'] = level moves_lines = [] for move_name, info in sorted(moves_dict.items()): versions_str = ', '.join(sorted(info['versions'])) methods_str = ', '.join(sorted(info['methods'])) if info['level'] is not None: moves_lines.append(f"{move_name} (Level {info['level']}, Versions: {versions_str})") else: moves_lines.append(f"{move_name} (Methods: {methods_str}, Versions: {versions_str})") egg_lines = [f"{move_name} (Egg Move in: {', '.join(sorted(versions))})" for move_name, versions in sorted(egg_moves_dict.items())] moves_preview = '\n'.join(moves_lines[:20]) more_moves = f"\n...and {len(moves_lines) - 20} more moves." if len(moves_lines) > 20 else "" egg_moves_output = '\n'.join(egg_lines) if egg_lines else 'None' return ( f"{pokemon_name.title()} can learn these moves grouped by name with versions and levels:\n" f"{moves_preview}{more_moves}\n\n" f"Egg moves:\n{egg_moves_output}" ) @tool def get_pokemon_sprite(pokemon_name: str) -> str: """ Fetches the default front sprite URL for a Pokémon. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string containing the sprite URL or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() sprite_url = data.get('sprites', {}).get('front_default') if not sprite_url: return f"No sprite found for '{pokemon_name}'." return f"{pokemon_name.title()} sprite: {sprite_url}" @tool def get_pokemon_held_items(pokemon_name: str) -> str: """ Fetches the items a Pokémon can hold in the wild, including rarity and game versions. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string listing the Pokémon's held items, their rarity, and versions, or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() held_items = data.get('held_items', []) if not held_items: return f"No held items found for '{pokemon_name}'." item_lines = [] for item in held_items: item_name = item['item']['name'].replace('-', ' ').title() versions = ', '.join([v['version']['name'].title() for v in item['version_details']]) rarity = item['version_details'][0]['rarity'] item_lines.append(f"{item_name} (Rarity: {rarity}%, Versions: {versions})") return f"{pokemon_name.title()}'s held items:\n" + "\n".join(item_lines) @tool def get_pokemon_encounter_locations(pokemon_name: str) -> str: """ Fetches the locations where a Pokémon can be encountered in different game versions. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string listing the Pokémon's encounter locations by game version, or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() encounters_url = data.get('location_area_encounters') if not encounters_url: return f"No encounter data found for '{pokemon_name}'." response = requests.get(encounters_url, timeout=5) if response.status_code != 200: return f"Error fetching encounter data (HTTP {response.status_code})." encounters = response.json() location_dict = defaultdict(list) for encounter in encounters: location = encounter['location_area']['name'].replace('-', ' ').title() for version_detail in encounter['version_details']: version = version_detail['version']['name'].title() location_dict[version].append(location) output = [] for version, locations in sorted(location_dict.items()): locations_str = ', '.join(sorted(set(locations))) output.append(f"{version}: {locations_str}") if not output: return f"No encounter locations found for '{pokemon_name}'." return f"{pokemon_name.title()}'s encounter locations:\n" + "\n".join(output) @tool def get_pokemon_type_effectiveness(pokemon_name: str) -> str: """ Fetches the type effectiveness (weaknesses, resistances, immunities) for a Pokémon's types. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string summarizing the Pokémon's type effectiveness, or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() types = [t['type']['url'] for t in data.get('types', [])] effectiveness = {'2x': set(), '0.5x': set(), '0x': set()} for type_url in types: response = requests.get(type_url, timeout=5) if response.status_code != 200: continue type_data = response.json() damage_relations = type_data['damage_relations'] for t in damage_relations['double_damage_from']: effectiveness['2x'].add(t['name'].title()) for t in damage_relations['half_damage_from']: effectiveness['0.5x'].add(t['name'].title()) for t in damage_relations['no_damage_from']: effectiveness['0x'].add(t['name'].title()) output = [] for key, types in effectiveness.items(): if types: output.append(f"{key}: {', '.join(sorted(types))}") if not output: return f"No type effectiveness data found for '{pokemon_name}'." return f"{pokemon_name.title()}'s type effectiveness:\n" + "\n".join(output) @tool def get_pokemon_ability_description(pokemon_name: str) -> str: """ Fetches the descriptions of a Pokémon's abilities from the ability endpoint. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string listing each ability and its English description, or an error message. """ name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon/{name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Pokémon '{pokemon_name}' not found (HTTP {response.status_code})." data = response.json() abilities = data.get('abilities', []) output = [] for ability_info in abilities: ability_name = ability_info['ability']['name'].replace('-', ' ').title() label = "Hidden" if ability_info['is_hidden'] else "Normal" ability_url = ability_info['ability']['url'] response = requests.get(ability_url, timeout=5) if response.status_code != 200: continue ability_data = response.json() effect = next((e['effect'] for e in ability_data['effect_entries'] if e['language']['name'] == 'en'), 'No description available.') output.append(f"{ability_name} ({label}): {effect}") if not output: return f"No ability descriptions found for '{pokemon_name}'." return f"{pokemon_name.title()}'s ability descriptions:\n" + "\n".join(output) @tool def list_pokemon_by_type(type_name: str, limit: int = 10) -> str: """ Lists Pokémon that have a specific type, up to a specified limit. Args: type_name: The name of the type (e.g., 'normal'). limit: Maximum number of Pokémon to list (default: 10). Returns: A string listing Pokémon with the specified type, or an error message. """ type_name = type_name.strip().lower() url = f"https://pokeapi.co/api/v2/type/{type_name}/" response = requests.get(url, timeout=5) if response.status_code != 200: return f"Error: Type '{type_name}' not found (HTTP {response.status_code})." data = response.json() pokemon_list = [p['pokemon']['name'].title() for p in data.get('pokemon', [])][:limit] if not pokemon_list: return f"No Pokémon found with type '{type_name}'." return f"Pokémon with {type_name.title()} type (up to {limit}):\n" + "\n".join(pokemon_list) @tool def get_pokemon_full_profile(pokemon_name: str) -> str: """ Fetches a complete profile for a Pokémon, including all available data. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string with a comprehensive Pokémon profile. """ name = pokemon_name.strip().lower() profile = [ get_pokedex_entry(pokemon_name=name), get_pokemon_basic_info(pokemon_name=name), get_pokemon_abilities(pokemon_name=name), get_pokemon_base_stats(pokemon_name=name), get_pokemon_evolutions(pokemon_name=name), get_pokemon_moves_grouped(pokemon_name=name), get_pokemon_held_items(pokemon_name=name), get_pokemon_encounter_locations(pokemon_name=name), get_pokemon_type_effectiveness(pokemon_name=name), get_pokemon_ability_description(pokemon_name=name), get_pokemon_sprite(pokemon_name=name) ] return f"{name.title()} Profile:\n\n" + "\n\n".join(profile) @tool def get_pokemon_evolution_visual(pokemon_name: str) -> str: """ Fetches the evolution chain for a Pokémon and generates an HTML visualization with sprites and basic info. This tool should be called directly without any code generation. Args: pokemon_name: The name or ID of the Pokémon. Returns: A string with the evolution chain and instructions to view the HTML visualization. """ def generate_evolution_html(pokemon_name: str, evolution_data: list) -> str: html_template = """
{name}
{info}
Evolution HTML file not found.
" except Exception as e: evolution_html = f"Error reading evolution HTML: {str(e)}
" # Read chart JSON and create chart HTML chart_filename = f"{pokemon_name.lower()}_evolution_stats.json" chart_html = "" try: if os.path.exists(chart_filename): with open(chart_filename, "r") as f: chart_config = json.load(f) # Create proper chart HTML chart_html = f"""Chart JSON file not found.
" except Exception as e: chart_html = f"Error creating chart HTML: {str(e)}
" return text_output, evolution_html, chart_html except Exception as e: return f"Error processing {pokemon_name}: {str(e)}", "", "" # Also fix the Gradio interface if __name__ == "__main__": iface = gr.Interface( fn=pokemon_ui, inputs=gr.Textbox(label="Pokemon Name", placeholder="Enter Pokemon name (e.g., pikachu)"), outputs=[ gr.Textbox(label="Results", lines=6), # Changed from HTML to Textbox for the text output gr.HTML(label="Evolution Visualization"), gr.HTML(label="Evolution Stats Chart"), ], title="Pokemon Evolution Visualizer", description="Enter a Pokemon name to see its evolution chain and stats comparison" ) iface.launch(share=True)