File size: 28,595 Bytes
2af39fa
 
9b5b26a
c19d193
a6e692e
 
6aae614
2af39fa
 
9b5b26a
 
a6e692e
 
 
2af39fa
9b5b26a
a6e692e
2af39fa
a6e692e
 
9b5b26a
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b5b26a
2af39fa
9b5b26a
a6e692e
 
 
2af39fa
9b5b26a
a6e692e
2af39fa
a6e692e
 
9b5b26a
a6e692e
 
 
 
 
 
 
 
 
 
 
 
8c01ffb
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c01ffb
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
ae7a494
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
a6e692e
 
 
 
 
2af39fa
a6e692e
 
ae7a494
2af39fa
a6e692e
 
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
 
a6e692e
 
 
 
 
 
 
 
 
2af39fa
 
a6e692e
2af39fa
a6e692e
 
 
 
 
 
8c01ffb
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
8c01ffb
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
 
a6e692e
 
 
 
 
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
 
2af39fa
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
a6e692e
 
 
 
2af39fa
a6e692e
 
2af39fa
a6e692e
 
 
 
2af39fa
 
 
 
 
 
 
 
 
 
 
 
 
a6e692e
 
2af39fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a4d67b
2af39fa
 
 
 
 
 
4a4d67b
2af39fa
 
4a4d67b
 
 
2af39fa
 
 
 
 
 
 
 
 
 
 
 
4a4d67b
2af39fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5dce09
 
 
 
 
2af39fa
a6e692e
 
2af39fa
 
 
 
a6e692e
8c01ffb
2af39fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a6e692e
8c01ffb
8fe992b
a6e692e
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
 
 
a6e692e
2af39fa
a6e692e
2af39fa
8fe992b
 
2af39fa
 
 
 
 
 
f5dce09
2af39fa
f5dce09
 
 
 
 
 
2af39fa
 
f5dce09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2af39fa
 
 
f5dce09
 
2af39fa
 
 
f5dce09
2af39fa
f5dce09
 
 
2af39fa
f5dce09
 
2af39fa
f5dce09
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
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 = """
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>{title} Evolution Chain</title>
            <style>
                body {{
                    font-family: Arial, sans-serif;
                    text-align: center;
                }}
                h1 {{
                    color: #121212;
                }}
                .evolution-container {{
                    display: flex;
                    justify-content: center;
                    gap: 20px;
                    margin: 20px;
                    color: #121212;
                }}
                .pokemon {{
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                    background-color: #fff;
                    padding: 10px;
                    border-radius: 10px;
                    box-shadow: 0 0 10px rgba(0,0,0,0.1);
                }}
                .pokemon img {{
                    width: 100px;
                    height: 100px;
                }}
                .pokemon p {{
                    margin: 5px 0;
                    font-weight: bold;
                    color: #121212 !important;
                }}
            </style>
        </head>
        <body>
            <h1>{title} Evolution Chain</h1>
            <div class="evolution-container">
                {pokemon_entries}
            </div>
        </body>
        </html>
        """
        pokemon_entries = ""
        for name, sprite_url, info in evolution_data:
            pokemon_entries += f"""
                <div class="pokemon">
                    <p>{name}</p>
                    <img src="{sprite_url}" alt="{name}">
                    <p>{info}</p>
                </div>
            """
        return html_template.format(title=pokemon_name.title(), pokemon_entries=pokemon_entries)

    evolution_chain = get_pokemon_evolutions(pokemon_name=pokemon_name)
    if "Error" in evolution_chain or "No evolution chain" in evolution_chain:
        return evolution_chain

    names = [name.strip().title() for name in evolution_chain.replace("Evolution chain: ", "").split("→")]
    evolution_data = []
    for name in names:
        sprite_result = get_pokemon_sprite(pokemon_name=name.lower())
        sprite_url = sprite_result.split(": ")[1] if "sprite:" in sprite_result else "https://via.placeholder.com/100"
        info_result = get_pokemon_basic_info(pokemon_name=name.lower())
        info = info_result.replace(f"{name} (", "(").replace("\n",
                                                             ", ") if "Error" not in info_result else "No info available"
        evolution_data.append((name, sprite_url, info))

    html_content = generate_evolution_html(pokemon_name, evolution_data)
    html_filename = f"{pokemon_name.lower()}_evolution.html"
    with open(html_filename, "w", encoding="utf-8") as f:
        f.write(html_content)

    return (
        f"{evolution_chain}\n\n"
        f"Evolution visualization saved to '{html_filename}'.\n"
        f"The HTML shows each stage with its sprite and basic info (ID, height, weight, types)."
    )


@tool
def get_evolution_stats_chart(pokemon_name: str) -> str:
    """
    Generates a Chart.js radar chart comparing base stats for a Pokémon's evolution chain.
    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 instructions to view the chart JSON.
    """
    evolution_chain = get_pokemon_evolutions(pokemon_name=pokemon_name)
    if "Error" in evolution_chain or "No evolution chain" in evolution_chain:
        return evolution_chain

    names = [name.strip().title() for name in evolution_chain.replace("Evolution chain: ", "").split("→")]
    datasets = []
    colors = [
        {"bg": "rgba(255, 206, 86, 0.2)", "border": "rgba(255, 206, 86, 1)"},
        {"bg": "rgba(255, 99, 132, 0.2)", "border": "rgba(255, 99, 132, 1)"},
        {"bg": "rgba(54, 162, 235, 0.2)", "border": "rgba(54, 162, 235, 1)"}
    ]

    for idx, name in enumerate(names):
        stats_result = get_pokemon_base_stats(pokemon_name=name.lower())
        if "Error" in stats_result:
            continue
        stats_dict = {k.split(": ")[0]: int(k.split(": ")[1]) for k in stats_result.split(": ", 1)[1].split(", ")}
        stats_data = [
            stats_dict.get("Hp", 0),
            stats_dict.get("Attack", 0),
            stats_dict.get("Defense", 0),
            stats_dict.get("Special Attack", 0),
            stats_dict.get("Special Defense", 0),
            stats_dict.get("Speed", 0)
        ]
        datasets.append({
            "label": name,
            "data": stats_data,
            "backgroundColor": colors[idx % len(colors)]["bg"],
            "borderColor": colors[idx % len(colors)]["border"],
            "borderWidth": 1
        })

    chart_config = {
        "type": "radar",
        "data": {
            "labels": ["HP", "Attack", "Defense", "Special Attack", "Special Defense", "Speed"],
            "datasets": datasets
        },
        "options": {
            "scales": {
                "r": {
                    "beginAtZero": True,
                    "max": 150
                }
            },
            "plugins": {
                "title": {
                    "display": True,
                    "text": f"{pokemon_name.title()} Evolution Chain Base Stats"
                }
            }
        }
    }

    chart_filename = f"{pokemon_name.lower()}_evolution_stats.json"
    with open(chart_filename, "w") as f:
        json.dump(chart_config, f, indent=2)

    return f"Radar chart for {pokemon_name.title()}'s evolution chain stats saved to '{chart_filename}'."

final_answer = FinalAnswerTool()

model = LiteLLMModel(
    model_id="ollama_chat/qwen2:7b",
    api_base="http://127.0.0.1:11434",
    num_ctx=8192,
)

# Create improved prompt templates
improved_prompts = {
    "system_prompt": """You are a helpful Pokémon information assistant. You have access to many tools to get Pokémon data.

IMPORTANT INSTRUCTIONS:
1. Always use the available tools directly - never try to write code or import modules
2. When asked to call get_pokemon_evolution_visual or get_evolution_stats_chart, call those tools directly
3. Never attempt to write Python code, import statements, or create visualizations yourself
4. Simply call the appropriate tool function with the pokemon name as argument
5. Return the result from the tool call as your final answer

Available tools:
- get_pokemon_evolution_visual: Creates HTML evolution visualization
- get_evolution_stats_chart: Creates radar chart comparing evolution stats
- get_pokemon_basic_info: Gets basic Pokémon info
- get_pokemon_evolutions: Gets evolution chain
- get_pokemon_base_stats: Gets base stats
- And many more Pokémon data tools

When a user asks you to use a specific tool, call that tool directly.""",

    "user_prompt": """Human request: {task}

Remember: Use the available tools directly. Do not write code or try to import anything.""",

    "code_execution_system_prompt": """You are a helpful assistant that can execute code to help users. When executing code, be direct and use available tools without attempting to import external modules."""
}

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,
        get_pokemon_sprite,
        get_pokemon_held_items,
        get_pokemon_encounter_locations,
        get_pokemon_type_effectiveness,
        get_pokemon_ability_description,
        list_pokemon_by_type,
        get_pokemon_full_profile,
        get_pokemon_evolution_visual,
        get_evolution_stats_chart
    ],
    max_steps=3,  # Reduced from 6 to force more direct tool usage
    verbosity_level=2,
    prompt_templates=improved_prompts
)


def pokemon_ui(pokemon_name):
    if not pokemon_name:
        return "Please enter a Pokémon name (e.g., pikachu).", "", ""

    try:
        # Get evolution chain and stats
        evo_result = get_pokemon_evolution_visual(pokemon_name)
        chart_result = get_evolution_stats_chart(pokemon_name)

        # Combine text results
        text_output = f"Evolution Result:\n{evo_result}\n\nChart Result:\n{chart_result}"

        # Read evolution HTML
        html_filename = f"{pokemon_name.lower()}_evolution.html"
        evolution_html = ""
        try:
            if os.path.exists(html_filename):
                with open(html_filename, "r", encoding="utf-8") as f:
                    evolution_html = f.read()
            else:
                evolution_html = "<p>Evolution HTML file not found.</p>"
        except Exception as e:
            evolution_html = f"<p>Error reading evolution HTML: {str(e)}</p>"

        # 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"""
                <!DOCTYPE html>
                <html>
                <head>
                    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
                </head>
                <body>
                    <div style="width: 600px; height: 400px; margin: auto;">
                        <canvas id="statsChart"></canvas>
                    </div>
                    <script>
                        const ctx = document.getElementById('statsChart').getContext('2d');
                        const chartConfig = {json.dumps(chart_config, indent=2)};
                        new Chart(ctx, chartConfig);
                    </script>
                </body>
                </html>
                """
            else:
                chart_html = "<p>Chart JSON file not found.</p>"
        except Exception as e:
            chart_html = f"<p>Error creating chart HTML: {str(e)}</p>"

        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)