Spaces:
Sleeping
Sleeping
| import math | |
| import requests | |
| import ast | |
| import operator | |
| from typing import Union, Dict, Any | |
| # ============================== | |
| # MATH TOOL | |
| # ============================== | |
| def calculate_expression(expression: str) -> Union[float, str]: | |
| """ | |
| Safely evaluate a mathematical expression. | |
| Supported operators: +, -, *, /, **, %, ^ (as power), sqrt, abs, round, sin, cos, tan, log, pi, e | |
| """ | |
| # Safe operators map | |
| operators = { | |
| ast.Add: operator.add, | |
| ast.Sub: operator.sub, | |
| ast.Mult: operator.mul, | |
| ast.Div: operator.truediv, | |
| ast.Pow: operator.pow, | |
| ast.Mod: operator.mod, | |
| ast.USub: operator.neg, | |
| ast.UAdd: operator.pos, | |
| } | |
| # Safe functions map | |
| functions = { | |
| "sqrt": math.sqrt, | |
| "abs": abs, | |
| "round": round, | |
| "sin": math.sin, | |
| "cos": math.cos, | |
| "tan": math.tan, | |
| "log": math.log, | |
| "max": max, | |
| "min": min, | |
| "ceil": math.ceil, | |
| "floor": math.floor, | |
| "degrees": math.degrees, | |
| "radians": math.radians, | |
| } | |
| # Safe constants | |
| constants = { | |
| "pi": math.pi, | |
| "e": math.e, | |
| "tau": math.tau, | |
| } | |
| def eval_node(node): | |
| if isinstance(node, ast.Num): # < 3.8 | |
| return node.n | |
| elif isinstance(node, ast.Constant): # >= 3.8 | |
| if isinstance(node.value, (int, float)): | |
| return node.value | |
| raise ValueError(f"Unsupported constant type: {type(node.value)}") | |
| elif isinstance(node, ast.BinOp): # <left> <operator> <right> | |
| op = type(node.op) | |
| if op not in operators: | |
| raise ValueError(f"Unsupported operator: {op}") | |
| return operators[op](eval_node(node.left), eval_node(node.right)) | |
| elif isinstance(node, ast.UnaryOp): # <operator> <operand> (e.g., -1) | |
| op = type(node.op) | |
| if op not in operators: | |
| raise ValueError(f"Unsupported unary operator: {op}") | |
| return operators[op](eval_node(node.operand)) | |
| elif isinstance(node, ast.Call): # Function calls like sqrt(4) | |
| if not isinstance(node.func, ast.Name): | |
| raise ValueError("Only named functions are supported") | |
| if node.func.id not in functions: | |
| raise ValueError(f"Unsupported function: {node.func.id}") | |
| args = [eval_node(arg) for arg in node.args] | |
| return functions[node.func.id](*args) | |
| elif isinstance(node, ast.Name): # Variables/Constants | |
| if node.id in constants: | |
| return constants[node.id] | |
| raise ValueError(f"Unsupported name: {node.id}") | |
| else: | |
| raise TypeError(f"Unsupported expression node: {type(node)}") | |
| try: | |
| # Pre-process: replace ^ with ** for power | |
| expression = expression.replace("^", "**") | |
| node = ast.parse(expression, mode='eval') | |
| result = eval_node(node.body) | |
| return float(result) | |
| except Exception as e: | |
| return f"Error calculating '{expression}': {str(e)}" | |
| # ============================== | |
| # WEATHER TOOL | |
| # ============================== | |
| def get_current_weather(location: str) -> Dict[str, Any]: | |
| """ | |
| Get current weather for a specific city using Open-Meteo API. | |
| Returns temperature (C), humidity, wind speed, etc. | |
| """ | |
| try: | |
| # 1. Geocoding | |
| geo_url = "https://geocoding-api.open-meteo.com/v1/search" | |
| geo_params = {"name": location, "count": 1, "language": "en", "format": "json"} | |
| geo_res = requests.get(geo_url, params=geo_params, timeout=5) | |
| geo_data = geo_res.json() | |
| if not geo_data.get("results"): | |
| return {"error": f"City '{location}' not found."} | |
| location = geo_data["results"][0] | |
| lat = location["latitude"] | |
| lon = location["longitude"] | |
| city_name = location["name"] | |
| country = location.get("country", "") | |
| # 2. Weather Data | |
| weather_url = "https://api.open-meteo.com/v1/forecast" | |
| weather_params = { | |
| "latitude": lat, | |
| "longitude": lon, | |
| "current": "temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,rain,showers,snowfall,weather_code,cloud_cover,wind_speed_10m", | |
| "timezone": "auto" | |
| } | |
| w_res = requests.get(weather_url, params=weather_params, timeout=5) | |
| w_data = w_res.json() | |
| if "current" not in w_data: | |
| return {"error": "{location} Weather data not available."} | |
| current = w_data["current"] | |
| current_units = w_data["current_units"] | |
| # Decode WMO Weather Code | |
| # source: https://open-meteo.com/en/docs | |
| wmo_code = current["weather_code"] | |
| condition = "Unknown" | |
| if wmo_code == 0: condition = "Clear sky" | |
| elif 1 <= wmo_code <= 3: condition = "Mainly clear, partly cloudy, and overcast" | |
| elif 45 <= wmo_code <= 48: condition = "Fog and depositing rime fog" | |
| elif 51 <= wmo_code <= 55: condition = "Drizzle: Light, moderate, and dense intensity" | |
| elif 56 <= wmo_code <= 57: condition = "Freezing Drizzle: Light and dense intensity" | |
| elif 61 <= wmo_code <= 65: condition = "Rain: Slight, moderate and heavy intensity" | |
| elif 66 <= wmo_code <= 67: condition = "Freezing Rain: Light and heavy intensity" | |
| elif 71 <= wmo_code <= 75: condition = "Snow fall: Slight, moderate, and heavy intensity" | |
| elif 77: condition = "Snow grains" | |
| elif 80 <= wmo_code <= 82: condition = "Rain showers: Slight, moderate, and violent" | |
| elif 85 <= wmo_code <= 86: condition = "Snow showers slight and heavy" | |
| elif 95: condition = "Thunderstorm: Slight or moderate" | |
| elif 96 <= wmo_code <= 99: condition = "Thunderstorm with slight and heavy hail" | |
| return { | |
| "location": f"{city_name}, {country}", | |
| "temperature": f"{current['temperature_2m']} {current_units['temperature_2m']}", | |
| "feels_like": f"{current['apparent_temperature']} {current_units['apparent_temperature']}", | |
| "humidity": f"{current['relative_humidity_2m']} {current_units['relative_humidity_2m']}", | |
| "wind_speed": f"{current['wind_speed_10m']} {current_units['wind_speed_10m']}", | |
| "condition": condition, | |
| "cloud_cover": f"{current['cloud_cover']} {current_units['cloud_cover']}", | |
| "timestamp": current["time"] | |
| } | |
| except Exception as e: | |
| return {"error": f"Failed to fetch weather: {str(e)}"} | |