from smolagents import CodeAgent,DuckDuckGoSearchTool, HfApiModel,load_tool,tool, OpenAIServerModel import datetime import requests import pytz import yaml import os from collections import defaultdict from tools.final_answer import FinalAnswerTool from tools.web_search import DuckDuckGoSearchTool from tools.visit_webpage import VisitWebpageTool from Gradio_UI import GradioUI # Below is an example of a tool that does nothing. Amaze us with your creativity ! #@tool #def my_custom_tool(arg1:str, arg2:int)-> str: #it's import to specify the return type # #Keep this format for the description / args / args description but feel free to modify the tool # """A tool that does nothing yet # Args: # arg1: the first argument # arg2: the second argument # """ # return "What magic will you build ?" @tool def get_pokedex_entry(pokemon_name: str) -> str: """ Fetches the English Pokédex entry (flavor text) or dex entry 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. """ # Normalize name/ID and construct URL name = pokemon_name.strip().lower() url = f"https://pokeapi.co/api/v2/pokemon-species/{name}/" # Request data from PokéAPI 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() # Filter for English flavor-text entries 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}'." # Clean up the first entry by replacing newlines/form feeds with spaces entry = flavor_texts[0].replace("\n", " ").replace("\f", " ") # Capitalize the Pokémon name for presentation 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. """ # Step 1: Get species info to find evolution chain URL 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}'." # Step 2: Get evolution chain data 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() # Recursive function to extract evolution names 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 (if applicable), all learning methods, and all game versions. Egg moves are listed separately with their versions. 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, 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() 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) # For level-up moves, keep the minimum level learned if method == "Level Up": if moves_dict[move_name]['level'] is None or level < moves_dict[move_name]['level']: moves_dict[move_name]['level'] = level # Prepare moves output 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})") # Prepare egg moves output egg_lines = [] for move_name, versions in sorted(egg_moves_dict.items()): versions_str = ', '.join(sorted(versions)) egg_lines.append(f"{move_name} (Egg Move in: {versions_str})") # Limit output for readability 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}" ) final_answer = FinalAnswerTool() #search_tool = DuckDuckGoSearchTool() #webpage_tool = VisitWebpageTool() # If the agent does not answer, the model is overloaded, please use another model or the following Hugging Face Endpoint that also contains qwen2.5 coder: # model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud' #model = OpenAIServerModel( # model_id="deepseek/deepseek-r1-0528-qwen3-8b:free", # api_base="https://openrouter.ai/api/v1", # Leave this blank to query OpenAI servers. # api_key=os.environ["OPENROUTER_API_KEY"], # Switch to the API key for the server you're targeting. # #) model = OpenAIServerModel( model_id="meta-llama/Llama-3.3-70B-Instruct-Turbo-Free", #model_id="meta-llama/Llama-Vision-Free", api_base="https://api.together.xyz/v1/", # Leave this blank to query OpenAI servers. api_key=os.environ["TOGETHER_API_KEY"], # Switch to the API key for the server you're targeting. ) with open("prompts.yaml", 'r') as stream: prompt_templates = yaml.safe_load(stream) agent = CodeAgent( model=model, tools=[final_answer,get_pokedex_entry,get_pokemon_basic_info,get_pokemon_abilities,get_pokemon_base_stats,get_pokemon_evolutions,get_pokemon_moves_grouped], ## add your tools here (don't remove final answer) max_steps=6, verbosity_level=2, grammar=None, planning_interval=None, name=None, description=None, prompt_templates=prompt_templates ) GradioUI(agent).launch()