@tool def get_weather_info(location: str = "New York") -> str: """ Get current weather information for a location. Args: location: Location to get weather for (default: New York) Returns: Current weather information """ try: # Using a free weather API (OpenWeatherMap) api_key = os.getenv('OPENWEATHER_API_KEY') if not api_key: # Fallback to mock data for demo return f""" 🌤️ **Weather for {location}** (Demo Data) Temperature: 22°C (72°F) Condition: Partly Cloudy Humidity: 65% Wind: 8 mph NW *Note: This is demo data. Set OPENWEATHER_API_KEY for real weather data.* """ # Real API call base_url = "https://api.openweathermap.org/data/2.5/weather" params = { 'q': location, 'appid': api_key, 'units': 'metric' } response = requests.get(base_url, params=params, timeout=5) response.raise_for_status() data = response.json() weather_info = f""" 🌤️ **Weather for {data['name']}, {data['sys']['country']}** Temperature: {data['main']['temp']:.1f}°C ({data['main']['temp'] * 9/5 + 32:.1f}°F) Feels like: {data['main']['feels_like']:.1f}°C Condition: {data['weather'][0]['description'].title()} Humidity: {data['main']['humidity']}% Pressure: {data['main']['pressure']} hPa Wind: {data['wind']['speed']} m/s Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} """ return weather_info except requests.exceptions.RequestException as e: logger.error(f"Weather API error: {e}") return f"Unable to fetch weather data for {location}. API error: {str(e)}" except Exception as e: logger.error(f"Weather tool error: {e}") return f"Error getting weather information: {str(e)}" @tool def calculate_math(expression: str) -> str: """ Safely evaluate mathematical expressions. Args: expression: Mathematical expression to evaluate (e.g., "2 + 2 * 3") Returns: Result of the mathematical calculation """ try: # Simple evaluation for basic math operations # In production, you might want to use a more sophisticated math parser # Remove any potentially dangerous characters allowed_chars = "0123456789+-*/()., " cleaned_expression = ''.join(c for c in expression if c in allowed_chars) if cleaned_expression != expression: return f"Expression contains invalid characters. Cleaned: {cleaned_expression}" # Evaluate the expression result = eval(cleaned_expression) return f""" 🧮 **Mathematical Calculation** Expression: {expression} Result: {result} Calculated at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} """ except ZeroDivisionError: return "Error: Division by zero is not allowed." except Exception as e: logger.error(f"Math calculation error: {e}") return f"Error calculating expression '{expression}': {str(e)}" @tool def get_current_time(timezone: str = "UTC") -> str: """ Get current time for a specific timezone. Args: timezone: Timezone (default: UTC) Returns: Current date and time information """ try: from datetime import datetime try: import pytz if timezone == "UTC": current_time = datetime.utcnow() tz_info = "UTC" else: try: tz = pytz.timezone(timezone) current_time = datetime.now(tz) tz_info = timezone except: # Fallback to UTC current_time = datetime.utcnow() tz_info = "UTC (fallback - invalid timezone provided)" except ImportError: # Fallback without pytz current_time = datetime.now() tz_info = "Local System Time" time_info = f""" 🕒 **Current Time Information** Date: {current_time.strftime('%A, %B %d, %Y')} Time: {current_time.strftime('%H:%M:%S')} Timezone: {tz_info} ISO Format: {current_time.isoformat()} """ return time_info except Exception as e: logger.error(f"Time tool error: {e}") return f"Error getting current time: {str(e)}" @tool def text_summarizer(text: str, max_sentences: int = 3) -> str: """ Summarize a given text to specified number of sentences. Args: text: Text to summarize max_sentences: Maximum number of sentences in summary (default: 3) Returns: Summarized text """ try: import re if not text.strip(): return "No text provided to summarize." # Simple extractive summarization # Split into sentences sentences = re.split(r'[.!?]+', text.strip()) sentences = [s.strip() for s in sentences if s.strip()] if len(sentences) <= max_sentences: return f"**Summary:** {text[:500]}..." if len(text) > 500 else text # Simple scoring based on sentence length and position scored_sentences = [] for i, sentence in enumerate(sentences): # Prefer sentences of medium length and earlier position length_score = min(len(sentence.split()) / 15, 1.0) # Normalize to words position_score = 1.0 - (i / len(sentences)) # Earlier sentences get higher score total_score = length_score * 0.7 + position_score * 0.3 scored_sentences.append((sentence, total_score)) # Sort by score and take top sentences scored_sentences.sort(key=lambda x: x[1], reverse=True) summary_sentences = [s[0] for s in scored_sentences[:max_sentences]] # Maintain original order final_summary = [] for sentence in sentences: if sentence in summary_sentences: final_summary.append(sentence) if len(final_summary) == max_sentences: break summary = '. '.join(final_summary) + '.' return f""" 📄 **Text Summary** Original length: {len(text)} characters Summary length: {len(summary)} characters Sentences: {max_sentences} **Summary:** {summary} """ except Exception as e: logger.error(f"Text summarization error: {e}") return f"Error summarizing text: {str(e)}" # List of all available tools for easy import AVAILABLE_TOOLS = [ web_search, get_weather_info, calculate_math, get_current_time, text_summarizer ]