sagar007's picture
Update app.py
3e34e42 verified
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
import json
import os
import math
import re
from typing import Dict, List, Any, Optional
from tools.final_answer import FinalAnswerTool
from Gradio_UI import GradioUI
# =============================================================================
# CUSTOM TOOLS COLLECTION
# =============================================================================
@tool
def get_current_time_in_timezone(timezone: str) -> str:
"""Get the current local time in a specified timezone.
Args:
timezone: A string representing a valid timezone (e.g., 'America/New_York', 'Europe/London', 'Asia/Tokyo')
"""
try:
tz = pytz.timezone(timezone)
local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
return f"Current time in {timezone}: {local_time}"
except Exception as e:
return f"Error: Invalid timezone '{timezone}'. Please use format like 'America/New_York'"
@tool
def weather_forecast(city: str, country_code: str = "") -> str:
"""Get current weather information for a city.
Args:
city: Name of the city
country_code: Optional 2-letter country code (e.g., 'US', 'UK', 'CA')
"""
try:
# Using OpenWeatherMap API (you'll need to get a free API key)
api_key = os.getenv('OPENWEATHER_API_KEY', 'demo_key')
if country_code:
query = f"{city},{country_code}"
else:
query = city
url = f"http://api.openweathermap.org/data/2.5/weather?q={query}&appid={api_key}&units=metric"
if api_key == 'demo_key':
return f"Weather service unavailable. Please set OPENWEATHER_API_KEY environment variable."
response = requests.get(url, timeout=10)
data = response.json()
if response.status_code == 200:
temp = data['main']['temp']
feels_like = data['main']['feels_like']
humidity = data['main']['humidity']
description = data['weather'][0]['description']
wind_speed = data['wind']['speed']
return f"Weather in {city}: {description.title()}, {temp}°C (feels like {feels_like}°C), Humidity: {humidity}%, Wind: {wind_speed} m/s"
else:
return f"Could not fetch weather for {city}. Error: {data.get('message', 'Unknown error')}"
except Exception as e:
return f"Weather service error: {str(e)}"
@tool
def calculate_advanced_math(expression: str) -> str:
"""Safely evaluate mathematical expressions including advanced functions.
Args:
expression: Mathematical expression (e.g., 'sqrt(16)', 'sin(pi/2)', '2**3 + log(10)')
"""
try:
# Safe math evaluation with common functions
safe_dict = {
'__builtins__': {},
'abs': abs, 'round': round, 'min': min, 'max': max,
'sum': sum, 'pow': pow,
'sqrt': math.sqrt, 'sin': math.sin, 'cos': math.cos, 'tan': math.tan,
'log': math.log, 'log10': math.log10, 'exp': math.exp,
'pi': math.pi, 'e': math.e,
'ceil': math.ceil, 'floor': math.floor,
'factorial': math.factorial,
'degrees': math.degrees, 'radians': math.radians
}
result = eval(expression, safe_dict)
return f"Result: {result}"
except Exception as e:
return f"Math error: {str(e)}. Please check your expression syntax."
@tool
def text_analyzer(text: str) -> str:
"""Analyze text for various metrics and insights.
Args:
text: The text to analyze
"""
try:
# Basic text statistics
word_count = len(text.split())
char_count = len(text)
char_count_no_spaces = len(text.replace(' ', ''))
sentence_count = len([s for s in re.split(r'[.!?]+', text) if s.strip()])
paragraph_count = len([p for p in text.split('\n\n') if p.strip()])
# Average metrics
avg_words_per_sentence = word_count / max(sentence_count, 1)
avg_chars_per_word = char_count_no_spaces / max(word_count, 1)
# Reading time estimate (average 200 words per minute)
reading_time_minutes = word_count / 200
# Most common words (excluding common stop words)
stop_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'is', 'are', 'was', 'were', 'be', 'been', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'this', 'that', 'these', 'those'}
words = [word.lower().strip('.,!?";()[]{}') for word in text.split()]
content_words = [word for word in words if word not in stop_words and len(word) > 2]
word_freq = {}
for word in content_words:
word_freq[word] = word_freq.get(word, 0) + 1
top_words = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:5]
analysis = f"""Text Analysis Results:
📊 Basic Statistics:
• Words: {word_count}
• Characters: {char_count} (with spaces), {char_count_no_spaces} (without spaces)
• Sentences: {sentence_count}
• Paragraphs: {paragraph_count}
📈 Averages:
• Words per sentence: {avg_words_per_sentence:.1f}
• Characters per word: {avg_chars_per_word:.1f}
⏱️ Reading Time: {reading_time_minutes:.1f} minutes
🔤 Most Common Words:
{chr(10).join([f"• {word}: {count}" for word, count in top_words])}
"""
return analysis
except Exception as e:
return f"Text analysis error: {str(e)}"
@tool
def url_shortener(url: str, custom_alias: str = "") -> str:
"""Create a shortened URL using TinyURL service.
Args:
url: The URL to shorten
custom_alias: Optional custom alias for the shortened URL
"""
try:
if not url.startswith(('http://', 'https://')):
url = 'https://' + url
api_url = "http://tinyurl.com/api-create.php"
params = {'url': url}
if custom_alias:
params['alias'] = custom_alias
response = requests.get(api_url, params=params, timeout=10)
if response.status_code == 200:
short_url = response.text.strip()
if short_url.startswith('http'):
return f"Shortened URL: {short_url}"
else:
return f"Error: {short_url}"
else:
return f"URL shortening failed with status code: {response.status_code}"
except Exception as e:
return f"URL shortening error: {str(e)}"
@tool
def password_generator(length: int = 12, include_symbols: bool = True, include_numbers: bool = True) -> str:
"""Generate a secure random password.
Args:
length: Length of the password (default: 12)
include_symbols: Whether to include special characters (default: True)
include_numbers: Whether to include numbers (default: True)
"""
try:
import random
import string
if length < 4:
return "Error: Password length must be at least 4 characters"
if length > 128:
return "Error: Password length cannot exceed 128 characters"
# Base character set
chars = string.ascii_letters
if include_numbers:
chars += string.digits
if include_symbols:
chars += "!@#$%^&*()_+-=[]{}|;:,.<>?"
# Ensure at least one character from each selected category
password = []
# Add at least one lowercase and uppercase letter
password.append(random.choice(string.ascii_lowercase))
password.append(random.choice(string.ascii_uppercase))
if include_numbers:
password.append(random.choice(string.digits))
if include_symbols:
password.append(random.choice("!@#$%^&*()_+-=[]{}|;:,.<>?"))
# Fill the rest with random characters
for _ in range(length - len(password)):
password.append(random.choice(chars))
# Shuffle the password
random.shuffle(password)
generated_password = ''.join(password)
return f"Generated password: {generated_password}\n\nSecurity tips:\n• Store in a password manager\n• Don't reuse across sites\n• Change regularly for sensitive accounts"
except Exception as e:
return f"Password generation error: {str(e)}"
@tool
def unit_converter(value: float, from_unit: str, to_unit: str) -> str:
"""Convert between different units of measurement.
Args:
value: The numeric value to convert
from_unit: Source unit (e.g., 'km', 'miles', 'kg', 'lbs', 'celsius', 'fahrenheit')
to_unit: Target unit
"""
try:
# Conversion factors to base units
conversions = {
# Length (to meters)
'mm': 0.001, 'cm': 0.01, 'm': 1, 'km': 1000,
'inch': 0.0254, 'ft': 0.3048, 'yard': 0.9144, 'mile': 1609.34,
# Weight (to kg)
'mg': 0.000001, 'g': 0.001, 'kg': 1,
'oz': 0.0283495, 'lb': 0.453592, 'stone': 6.35029,
# Temperature (special handling)
'celsius': 'celsius', 'fahrenheit': 'fahrenheit', 'kelvin': 'kelvin',
# Volume (to liters)
'ml': 0.001, 'l': 1, 'gallon': 3.78541, 'quart': 0.946353,
'pint': 0.473176, 'cup': 0.236588, 'fl_oz': 0.0295735
}
from_unit = from_unit.lower()
to_unit = to_unit.lower()
# Handle temperature conversions separately
if from_unit in ['celsius', 'fahrenheit', 'kelvin'] or to_unit in ['celsius', 'fahrenheit', 'kelvin']:
if from_unit == 'celsius' and to_unit == 'fahrenheit':
result = (value * 9/5) + 32
elif from_unit == 'fahrenheit' and to_unit == 'celsius':
result = (value - 32) * 5/9
elif from_unit == 'celsius' and to_unit == 'kelvin':
result = value + 273.15
elif from_unit == 'kelvin' and to_unit == 'celsius':
result = value - 273.15
elif from_unit == 'fahrenheit' and to_unit == 'kelvin':
result = (value - 32) * 5/9 + 273.15
elif from_unit == 'kelvin' and to_unit == 'fahrenheit':
result = (value - 273.15) * 9/5 + 32
else:
return f"Error: Cannot convert from {from_unit} to {to_unit}"
return f"{value}° {from_unit.title()} = {result:.2f}° {to_unit.title()}"
# Handle other unit conversions
if from_unit not in conversions or to_unit not in conversions:
available_units = list(conversions.keys())
return f"Error: Unsupported unit. Available units: {', '.join(available_units)}"
# Convert to base unit, then to target unit
base_value = value * conversions[from_unit]
result = base_value / conversions[to_unit]
return f"{value} {from_unit} = {result:.6f} {to_unit}"
except Exception as e:
return f"Unit conversion error: {str(e)}"
@tool
def json_formatter(json_string: str) -> str:
"""Format and validate JSON strings.
Args:
json_string: The JSON string to format
"""
try:
# Parse the JSON to validate it
parsed = json.loads(json_string)
# Format with proper indentation
formatted = json.dumps(parsed, indent=2, ensure_ascii=False)
return f"✅ Valid JSON - Formatted:\n\n{formatted}"
except json.JSONDecodeError as e:
return f"❌ Invalid JSON - Error: {str(e)}"
except Exception as e:
return f"JSON formatting error: {str(e)}"
@tool
def base64_encoder_decoder(text: str, operation: str = "encode") -> str:
"""Encode or decode base64 strings.
Args:
text: The text to encode/decode
operation: Either 'encode' or 'decode'
"""
try:
import base64
if operation.lower() == "encode":
encoded = base64.b64encode(text.encode('utf-8')).decode('utf-8')
return f"Base64 Encoded: {encoded}"
elif operation.lower() == "decode":
decoded = base64.b64decode(text.encode('utf-8')).decode('utf-8')
return f"Base64 Decoded: {decoded}"
else:
return "Error: Operation must be 'encode' or 'decode'"
except Exception as e:
return f"Base64 operation error: {str(e)}"
@tool
def hash_generator(text: str, algorithm: str = "sha256") -> str:
"""Generate hash for text using various algorithms.
Args:
text: The text to hash
algorithm: Hash algorithm ('md5', 'sha1', 'sha256', 'sha512')
"""
try:
import hashlib
algorithm = algorithm.lower()
if algorithm == "md5":
hash_obj = hashlib.md5(text.encode())
elif algorithm == "sha1":
hash_obj = hashlib.sha1(text.encode())
elif algorithm == "sha256":
hash_obj = hashlib.sha256(text.encode())
elif algorithm == "sha512":
hash_obj = hashlib.sha512(text.encode())
else:
return "Error: Supported algorithms are 'md5', 'sha1', 'sha256', 'sha512'"
hash_value = hash_obj.hexdigest()
return f"{algorithm.upper()} hash: {hash_value}"
except Exception as e:
return f"Hash generation error: {str(e)}"
# =============================================================================
# MAIN APPLICATION SETUP
# =============================================================================
def main():
"""Initialize and launch the AI agent application."""
# Initialize core tools
final_answer = FinalAnswerTool()
search_tool = DuckDuckGoSearchTool()
# Initialize the language model
model = InferenceClientModel(
max_tokens=2096,
temperature=0.5,
model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
custom_role_conversions=None,
)
# Try to load image generation tool
try:
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
print("✅ Image generation tool loaded successfully")
except Exception as e:
print(f"⚠️ Could not load image generation tool: {e}")
image_generation_tool = None
# Collect all tools
tools = [
final_answer,
search_tool,
get_current_time_in_timezone,
weather_forecast,
calculate_advanced_math,
text_analyzer,
url_shortener,
password_generator,
unit_converter,
json_formatter,
base64_encoder_decoder,
hash_generator,
]
# Add image generation tool if available
if image_generation_tool:
tools.append(image_generation_tool)
# Load system prompt templates
try:
with open("prompts.yaml", 'r') as stream:
prompt_templates = yaml.safe_load(stream)
print("✅ System prompts loaded successfully")
except FileNotFoundError:
print("⚠️ prompts.yaml not found, using default prompts")
prompt_templates = None
except Exception as e:
print(f"⚠️ Error loading prompts.yaml: {e}")
prompt_templates = None
# Initialize the CodeAgent
agent = CodeAgent(
model=model,
tools=tools,
max_steps=10, # Increased for more complex tasks
verbosity_level=1,
grammar=None,
planning_interval=3, # Plan every 3 steps
name="SuperAgent",
description="An advanced AI agent capable of solving complex tasks using code and various tools",
prompt_templates=prompt_templates
)
print("🚀 SuperAgent initialized with the following capabilities:")
print(" • Web search and information retrieval")
print(" • Advanced mathematical calculations")
print(" • Text analysis and processing")
print(" • Time zone and weather information")
print(" • URL shortening and web utilities")
print(" • Security tools (password generation, hashing)")
print(" • Data conversion and formatting")
if image_generation_tool:
print(" • Image generation from text")
print(" • Code execution and problem solving")
print()
# Launch the Gradio interface
try:
ui = GradioUI(agent)
ui.launch(
share=False, # Set to True to create a public link
inbrowser=True, # Open in browser automatically
server_name="0.0.0.0", # Allow access from other devices on network
server_port=7860, # Default Gradio port
)
except Exception as e:
print(f"❌ Failed to launch Gradio UI: {e}")
print("Make sure you have gradio installed and Gradio_UI module is available")
if __name__ == "__main__":
main()