Pokedex_Agent / app.py
vknt's picture
Update app.py
0df2fc3 verified
Raw
History Blame Contribute Delete
11 kB
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()