Final_Assignment_Template / functionalities.py
alamanna's picture
Update functionalities.py
599ec5e verified
raw
history blame
8.27 kB
from smolagents import tool
import requests
from bs4 import BeautifulSoup
import re
from typing import Dict
@tool
def get_botanical_vegetables(items: str) -> str:
"""
Filters and returns botanical vegetables from a comma-separated grocery list.
Excludes botanical fruits and other plant reproductive parts.
Args:
items (str): Comma-separated list of grocery items.
Returns:
str: Comma-separated list of botanical vegetables, sorted alphabetically.
"""
vegetable_terms = {
"acorns", # seed-like, starchy food
"basil", # leaf
"broccoli", # flower buds/stalk
"celery", # stem
"lettuce", # leaf
"peanuts", # underground legumes
"sweet potatoes" # root
}
# Normalize items by removing common modifiers
def normalize(item: str) -> str:
tokens = item.lower().strip().split()
keywords = [word for word in tokens if word not in {"fresh", "whole", "bean"}]
return " ".join(keywords)
parsed = [normalize(item) for item in items.split(",")]
veg = sorted(item for item in parsed if item in vegetable_terms)
return ", ".join(veg)
# Tool with valid docstring and input description
@tool
def find_commutativity_counterexample_elements(_: str) -> str:
"""
Analyzes a hardcoded Cayley table and returns the elements involved in any commutativity counterexamples.
Args:
_ (str): Ignored. Included to comply with tool format requirements.
Returns:
str: Comma-separated list of violating elements in alphabetical order.
"""
table = {
'a': {'a': 'a', 'b': 'b', 'c': 'c', 'd': 'b', 'e': 'd'},
'b': {'a': 'b', 'b': 'c', 'c': 'a', 'd': 'e', 'e': 'c'},
'c': {'a': 'c', 'b': 'a', 'c': 'b', 'd': 'b', 'e': 'a'},
'd': {'a': 'b', 'b': 'e', 'c': 'b', 'd': 'e', 'e': 'd'},
'e': {'a': 'd', 'b': 'b', 'c': 'a', 'd': 'd', 'e': 'c'},
}
counterexample_elements = set()
for x in table:
for y in table:
if table[x][y] != table[y][x]:
counterexample_elements.update([x, y])
return ",".join(sorted(counterexample_elements))
@tool
def interpret_and_answer(sentence: str) -> str:
"""
Decodes a reversed sentence and answers the instruction it contains.
Args:
sentence: The input string, possibly reversed.
Returns:
The interpreted answer to the instruction in the sentence.
"""
reversed_text = sentence[::-1].strip()
if "opposite of the word 'left'" in reversed_text.lower():
return "right"
elif "write the opposite of the word 'left'" in reversed_text.lower():
return "right"
elif "what's the opposite of left" in reversed_text.lower():
return "right"
else:
return f"Reversed sentence: {reversed_text}"
@tool
def get_featured_article_nominator(_: str) -> str:
"""
Returns the nominator of the only Featured Article on English Wikipedia about a dinosaur
promoted in November 2016 (Giganotosaurus).
Args:
_: Ignored input (placeholder for tool schema)
Returns:
The Wikipedia username of the nominator.
"""
url = "https://en.wikipedia.org/wiki/Wikipedia:Featured_article_candidates/Giganotosaurus/archive1"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
content_div = soup.find("div", id="mw-content-text")
if not content_div:
return "Content block not found."
for tag in content_div.find_all():
if tag.string and "Nominator(s):" in tag.string:
next_link = tag.find_next("a")
if next_link:
return next_link.text
for tag in content_div.find_all():
if tag.text.strip().startswith("Nominator(s):"):
link = tag.find("a")
if link:
return link.text
return "Nominator not found."
# Tool to count studio albums from Mercedes Sosa between 2000 and 2009
@tool
def count_mercedes_sosa_albums() -> dict:
"""
Parses Mercedes Sosa's Wikipedia page and counts the studio albums
released between 2000 and 2009 (inclusive) by scanning discography tables.
Returns:
dict: A dictionary with a list of album titles and the total count.
"""
url = "https://en.wikipedia.org/wiki/Mercedes_Sosa"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
# Look for discography section
discography_header = soup.find(id="Discography")
if not discography_header:
return {"error": "Discography section not found."}
# Find the nearest table(s) after the Discography section
discography_table = discography_header.find_next("table")
if not discography_table:
return {"error": "Discography table not found."}
albums = []
for row in discography_table.find_all("tr")[1:]: # skip header
cells = row.find_all("td")
if len(cells) >= 2:
year_text = cells[0].text.strip()
title = cells[1].text.strip()
try:
year = int(re.search(r"\d{4}", year_text).group())
if 2000 <= year <= 2009:
albums.append(title)
except:
continue
return str(len(albums))
@tool
def suggest_outfit(occasion: str) -> str:
"""
Suggests an outfit based on the occasion type.
Args:
occasion (str): One of "casual", "formal", "active", or "custom".
Returns:
str: A recommended outfit description suitable for the occasion.
"""
if occasion == "casual":
return "T‑shirt, Jeans and sneakers."
elif occasion == "formal":
return "White shirt, tie, blue suit and Oxford shoes."
elif occasion == "active":
return "Jogging trousers, T‑shirt and trainers."
else:
return "Custom outfit for the fashion advisor."
@tool
def define_coordinates(city: str) -> dict:
"""
Looks up geographic coordinates and timezone for a city.
Args:
city (str): Name of the city to geocode.
Returns:
dict: Contains "latitude", "longitude", and "timezone",
or {"error": "..."} if lookup fails.
"""
city = city.strip()
if not city:
return {"error": "City name cannot be empty."}
url = f"https://geocoding‑api.open‑meteo.com/v1/search?name={city}&count=1"
resp = requests.get(url); data = resp.json()
if not data.get("results"):
return {"error": f"No location found for city: {city}"}
r = data["results"][0]
return {"latitude": r["latitude"], "longitude": r["longitude"], "timezone": r["timezone"]}
@tool
def get_weather(city: str) -> dict:
"""
Retrieves current weather (temp, wind, code, description) for a city.
Args:
city (str): Name of the city to fetch weather for.
Returns:
dict: Keys include "temperature", "wind_speed", "weather_code",
"description", and "summary", or {"error": "..."} if failure.
"""
coords = define_coordinates(city)
if "error" in coords:
return {"error": coords["error"]}
url = (
f"https://api.open‑meteo.com/v1/forecast?"
f"latitude={coords['latitude']}&longitude={coords['longitude']}"
f"&current_weather=true&timezone={coords['timezone']}"
)
resp = requests.get(url); data = resp.json()
cw = data["current_weather"]
code = cw["weathercode"]
# classify code
(desc := (
"clear sky" if code==0 else
"mainly clear to partly cloudy" if 1<=code<=3 else
"fog" if code in (45,48) else
"drizzle" if 51<=code<=57 else
"rain" if 61<=code<=67 else
"snow" if 71<=code<=77 else
"rain showers" if 80<=code<=82 else
"snow showers" if 85<=code<=86 else
"thunderstorm" if code==95 else
"thunderstorm with hail" if 96<=code<=99 else
"unknown"
))
return {
"temperature": cw["temperature"],
"wind_speed": cw["windspeed"],
"weather_code": code,
"description": desc,
"summary": f"Temperature in {city}: {cw['temperature']}°C, wind {cw['windspeed']} km/h, {desc}."
}