File size: 7,509 Bytes
9b5b26a
 
 
 
c19d193
6aae614
8fe992b
9b5b26a
 
ed2abd9
 
 
 
 
 
 
 
 
 
5df72d6
9b5b26a
3d1237b
9b5b26a
 
 
 
 
 
 
 
deed080
 
ed2abd9
0b4544a
87aa658
0b4544a
cda4a6f
 
deed080
ed2abd9
 
 
 
 
87aa658
deed080
ed2abd9
 
87aa658
ed2abd9
 
 
 
 
 
 
87aa658
ed2abd9
87aa658
ed2abd9
87aa658
deed080
ed2abd9
deed080
 
ed2abd9
0b4544a
665c11e
cda4a6f
 
 
deed080
ed2abd9
 
665c11e
 
 
deed080
 
ed2abd9
 
deed080
665c11e
ed2abd9
deed080
ed2abd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
deed080
ed2abd9
deed080
0b4544a
665c11e
ed2abd9
0b4544a
665c11e
0b4544a
cda4a6f
0b4544a
deed080
 
 
ed2abd9
 
deed080
ed2abd9
deed080
 
 
ed2abd9
 
 
 
 
 
 
 
 
 
deed080
ed2abd9
deed080
9b5b26a
 
 
 
 
 
 
 
 
 
 
 
 
 
8c01ffb
 
6aae614
cfa3668
 
 
ae7a494
 
 
 
e121372
bf6d34c
 
3989ad3
fe328e0
13d500a
8c01ffb
 
9b5b26a
 
8c01ffb
861422e
 
ed2abd9
 
 
164d007
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b5b26a
8c01ffb
8fe992b
deed080
8c01ffb
 
 
 
 
 
861422e
8fe992b
 
9b5b26a
8c01ffb
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
from smolagents import CodeAgent,DuckDuckGoSearchTool, HfApiModel,load_tool,tool
import datetime
import requests
import pytz
import yaml
from tools.final_answer import FinalAnswerTool

from Gradio_UI import GradioUI

#Utility function to help refine the search and use of our api calls
def normalize_input(text: str) -> str:
    return text.strip().lower().replace("'", "").replace(",", "")

def best_match(name: str, options: list) -> str:
    matches = get_close_matches(normalize_input(name), 
                               [normalize_input(o) for o in options], 
                               n=1, cutoff=0.8)
    return options[matches[0]] if matches else None

# 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 asoiaf_book(query: str) -> str:
    """Handle book queries by ID or name. Returns JSON with {title, authors, pages, characters}

    Args:
        query: The book ID (1-12) or name (e.g., 'A Game of Thrones')
    """
    try:
        if query.isdigit():
            book_id = int(query)
            response = requests.get(f"https://anapioficeandfire.com/api/books/{book_id}")
        else:
            response = requests.get("https://anapioficeandfire.com/api/books")

        if response.status_code == 200:
            books = response.json() if isinstance(response.json(), list) else [response.json()]
            matched = next((b for b in books if normalize_input(b["name"]) == normalize_input(query)), None)

            if matched:
                return json.dumps({
                    "title": matched["name"],
                    "authors": matched["authors"],
                    "pages": matched["numberOfPages"],
                    "characters": len(matched["characters"])
                })

            return json.dumps({"error": f"No book matching '{query}' found"})

        return json.dumps({"error": "Book API unavailable"})

    except Exception as e:
        return json.dumps({"error": f"Book search failed: {str(e)}"})

@tool
def asoiaf_character(query: str) -> str:
    """Resolve questions about characters. Returns JSON with: {name, aliases, titles, actor, house, error}

    Args:
        query: The character name or description (e.g., 'Mother of Dragons')
    """
    try:
        clean_query = normalize_input(query)
        name_keywords = ["who is", "plays", "actor for", "information about"]
        name = next((clean_query.split(kw)[-1].strip() for kw in name_keywords
                     if kw in clean_query), clean_query)

        response = requests.get(
            "https://anapioficeandfire.com/api/characters",
            params={"name": name},
            timeout=5
        )

        
        if response.status_code == 200:
            chars = response.json()
            if not chars:
                all_chars = requests.get("https://anapioficeandfire.com/api/characters").json()
                aliases = [alias for c in all_chars for alias in c["aliases"]]
                matched = best_match(name, aliases) or best_match(name, [c["name"] for c in all_chars])
                if matched:
                    return asoiaf_character(matched)
                return json.dumps({"error": f"No match for '{query}'. Try full names like 'Daenerys Targaryen'"})

            char = chars[0]
            result = {
                "name": char.get("name"),
                "aliases": char.get("aliases", []),
                "titles": char.get("titles", []),
                "actor": char.get("playedBy", ["Unknown"])[0],
                "house": char.get("allegiances", ["Unknown"])[0].split("/")[-1],
                "source": "book"
            }
            return json.dumps(result, ensure_ascii=False)
            
        return json.dumps({"error": "API unavailable"})
    
    except Exception as e:
        return json.dumps({"error": f"Search failed: {str(e)}"})


@tool
def asoiaf_house(query: str) -> str:
    """Resolve house-related queries. Returns JSON with {name, region, words, members}

    Args:
        query: The region or house name (e.g., 'The North', 'Stark')
    """
    try:
        response = requests.get(
            "https://anapioficeandfire.com/api/houses",
            params={"region": query},
            timeout=5
        )
        
        if response.status_code == 200:
            houses = response.json()
            if houses:
                return json.dumps({
                    "name": houses[0]["name"],
                    "region": houses[0]["region"],
                    "words": houses[0]["words"],
                    "members": len(houses[0]["swornMembers"])
                })
            return json.dumps({"error": f"No houses found matching '{query}'"})
            
        return json.dumps({"error": "House API unavailable"})
    
    except Exception as e:
        return json.dumps({"error": f"House search failed: {str(e)}"})

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """A tool that fetches the current local time in a specified timezone.
    Args:
        timezone: A string representing a valid timezone (e.g., 'America/New_York').
    """
    try:
        # Create timezone object
        tz = pytz.timezone(timezone)
        # Get current time in that timezone
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"The current local time in {timezone} is: {local_time}"
    except Exception as e:
        return f"Error fetching time for timezone '{timezone}': {str(e)}"


final_answer = FinalAnswerTool()
house = asoiaf_house
book = asoiaf_book
character = asoiaf_character

# 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 = HfApiModel(
max_tokens=2096,
temperature=0.5,
model_id='meta-llama/Llama-3.2-3B-Instruct',# it is possible that this model may be overloaded
custom_role_conversions=None,
)


# Import tool from Hub
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)

with open("prompts.yaml", 'r') as stream:
    prompt_templates = yaml.safe_load(stream)


# Add JSON parsing instructions to the agent prompt
if 'system' in prompt_templates:
    prompt_templates['system'] += """
    When using API tools: 
    1. Always parse JSON responses first 
    2. Check for "error" keys before proceeding 
    3. Use get() for optional fields 
    4. Handle empty values gracefully 
    """
else:
    prompt_templates['system'] = """
    When using API tools: 
    1. Always parse JSON responses first 
    2. Check for "error" keys before proceeding 
    3. Use get() for optional fields 
    4. Handle empty values gracefully 
    """
    
agent = CodeAgent(
    model=model,
    tools=[final_answer, book, character, house], ## add your tools here (don't remove final answer)
    max_steps=6,
    verbosity_level=1,
    grammar=None,
    planning_interval=None,
    name=None,
    description=None,
    prompt_templates=prompt_templates
)


GradioUI(agent).launch()