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"¤t_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}." }