First_agent / app.py
YenLai's picture
Update app.py
ca69d52 verified
from smolagents import CodeAgent,DuckDuckGoSearchTool, HfApiModel,load_tool,tool
import datetime
import requests
import pytz
import yaml
from tools.final_answer import FinalAnswerTool
from Gradio_UI import GradioUI
# Below is an example of a tool that does nothing. Amaze us with your creativity !
@tool
def get_current_time_in_timezone(timezone: str) -> str:
"""Get the current time in a specified timezone.
Args:
timezone: A valid timezone string (e.g., 'America/New_York', 'Asia/Tokyo').
"""
try:
# Validate timezone
if timezone not in pytz.all_timezones:
return json.dumps({
"status": "error",
"message": f"Invalid timezone: '{timezone}'. Please provide a valid timezone like 'America/New_York'.",
"data_source": "none"
})
# Create timezone object
tz = pytz.timezone(timezone)
# Get current time in that timezone
local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
return json.dumps({
"status": "success",
"message": f"The current time in {timezone} is: {local_time}",
"data": {
"timezone": timezone,
"current_time": local_time
},
"data_source": "system_time"
})
except Exception as e:
return json.dumps({
"status": "error",
"message": f"Error fetching time for timezone '{timezone}': {str(e)}",
"data_source": "none"
})
@tool
def get_sunrise_time(location: str, date: str = "today") -> str:
"""Get sunrise time and photography tips for a specified location.
Args:
location: Location name or coordinates (e.g., "London" or "51.5074,-0.1278")
date: Date string (e.g., "2023-12-25" or "today" for current date)
"""
try:
# Validate date format if not "today"
if date != "today":
try:
datetime.datetime.strptime(date, "%Y-%m-%d")
date_param = f"&date={date}"
except ValueError:
return json.dumps({
"status": "error",
"message": f"Invalid date format: '{date}'. Please use YYYY-MM-DD format or 'today'.",
"data_source": "none"
})
else:
date_param = ""
# Check if location is in coordinate format
if "," in location and all(part.replace('.', '', 1).replace('-', '', 1).isdigit() for part in location.split(',')):
lat, lng = location.split(',')
location_source = "user_coordinates"
else:
# Use Nominatim API for geocoding (convert location name to coordinates)
try:
geo_response = requests.get(
f"https://nominatim.openstreetmap.org/search?q={location}&format=json&limit=1",
headers={"User-Agent": "SunrisePhotographyAssistant/1.0"}
)
geo_data = geo_response.json()
if not geo_data:
return json.dumps({
"status": "error",
"message": f"Could not find location: {location}. Please try using coordinates or check the spelling.",
"data_source": "none"
})
lat = geo_data[0]['lat']
lng = geo_data[0]['lon']
location_source = "openstreetmap_geocoding"
# Get the properly formatted name from OSM
location_name = geo_data[0].get('display_name', location)
except Exception as e:
return json.dumps({
"status": "error",
"message": f"Error geocoding location '{location}': {str(e)}",
"data_source": "none"
})
# Use coordinates to get sunrise data
try:
response = requests.get(f"https://api.sunrise-sunset.org/json?lat={lat}&lng={lng}{date_param}&formatted=0")
data = response.json()
if data.get('status') != 'OK':
return json.dumps({
"status": "error",
"message": f"Error getting sunrise data: {data.get('status', 'Unknown error')}",
"data_source": "none"
})
# Validate required fields
required_fields = ['sunrise', 'civil_twilight_begin']
for field in required_fields:
if field not in data.get('results', {}):
return json.dumps({
"status": "error",
"message": f"Missing required data field: {field}",
"data_source": "none"
})
except Exception as e:
return json.dumps({
"status": "error",
"message": f"Error fetching sunrise data: {str(e)}",
"data_source": "none"
})
# Process sunrise times (convert from UTC to local time)
sunrise_utc = datetime.datetime.fromisoformat(data['results']['sunrise'].replace('Z', '+00:00'))
civil_twilight_utc = datetime.datetime.fromisoformat(data['results']['civil_twilight_begin'].replace('Z', '+00:00'))
# Get timezone for the location (using timezonefinder library)
try:
from timezonefinder import TimezoneFinder
tf = TimezoneFinder()
timezone_str = tf.certain_timezone_at(lat=float(lat), lng=float(lng))
if not timezone_str:
timezone = pytz.UTC
timezone_name = "UTC"
timezone_confidence = "low"
timezone_note = "(Note: Could not determine exact local timezone, using UTC instead)"
else:
timezone = pytz.timezone(timezone_str)
timezone_name = timezone_str
timezone_confidence = "high"
timezone_note = ""
except Exception as e:
# Fallback to UTC if timezone lookup fails
timezone = pytz.UTC
timezone_name = "UTC"
timezone_confidence = "fallback"
timezone_note = f"(Note: Error determining timezone: {str(e)}. Using UTC instead)"
# Convert to local time
sunrise_local = sunrise_utc.astimezone(timezone)
civil_twilight_local = civil_twilight_utc.astimezone(timezone)
# Calculate golden hour (about 30 minutes after sunrise)
golden_hour_end = sunrise_local + datetime.timedelta(minutes=30)
# Format times
sunrise_time = sunrise_local.strftime("%H:%M:%S")
civil_twilight_time = civil_twilight_local.strftime("%H:%M:%S")
golden_hour_end_time = golden_hour_end.strftime("%H:%M:%S")
# Provide season-specific advice
month = sunrise_local.month
season = ""
season_tip = ""
if 3 <= month <= 5: # Spring
season = "Spring"
season_tip = "Spring sunrises often feature mist and soft light. Try to capture flowers in the foreground."
elif 6 <= month <= 8: # Summer
season = "Summer"
season_tip = "Summer sunrise comes early and temperatures rise quickly. Bring insect repellent and watch for lens fog due to humidity."
elif 9 <= month <= 11: # Fall/Autumn
season = "Fall/Autumn"
season_tip = "Fall sunrises often have fog and interesting cloud formations. Try using fallen leaves as foreground elements."
else: # Winter
season = "Winter"
season_tip = "Winter sunrises come later with cooler light. Stay warm and bring extra batteries as cold temperatures drain them faster."
# Generate factual data section
factual_data = [
f"Sunrise time: {sunrise_time} {timezone_note}",
f"Civil twilight begins: {civil_twilight_time}",
f"Golden light ends approximately: {golden_hour_end_time}",
f"Current season: {season}"
]
# Generate photography recommendations
photography_recommendations = [
f"Recommended arrival time: Civil twilight ({civil_twilight_time})",
f"{season} photography consideration: {season_tip}",
"Suggested gear: Tripod (essential), Variable ND filter, Remote shutter, Extra batteries",
"Composition tip: Look for interesting foreground elements, don't just focus on the sky",
"For best exposure, consider bracketing (HDR) or graduated filters"
]
# Add a humorous tip
humor_tips = [
"Remember to bring coffee, because your eyes might still be dreaming at dawn!",
"If you see other photographers, don't stand in front of them... unless you want to be the 'silhouette art' in their photos.",
"If your memory card fills up before the sunrise begins, that's why you should always bring two!",
"First rule of sunrise photography: Don't oversleep. Second rule: Seriously, don't oversleep!",
"Remember, the best tripod is the one you forgot at home...",
"The ultimate test for your sunrise photo: If your mom says 'Wow!' instead of just politely nodding, you've succeeded.",
"While waiting for sunrise in the frigid morning, remember that your bed misses you too.",
"If your photos come out blurry, just tell everyone it's an 'artistic style'.",
"The secret formula for the perfect sunrise shot: Weather forecast accuracy + Luck × Preparation ÷ Snooze button index"
]
# Build final output
result = f"== Sunrise Photography Guide for {location} ==\n\n"
result += "FACTUAL DATA (from sunrise-sunset.org):\n"
result += "\n".join([f"• {item}" for item in factual_data])
result += "\n\n"
result += "PHOTOGRAPHY RECOMMENDATIONS:\n"
result += "\n".join([f"• {item}" for item in photography_recommendations])
result += "\n\n"
result += "HUMOR TIP:\n"
result += f"• {random.choice(humor_tips)}\n\n"
result += "NOTE: Weather conditions can significantly affect photography opportunities and are not predicted by this tool."
# Prepare data for structured output
output_data = {
"status": "success",
"message": result,
"data": {
"location": location,
"date": date if date != "today" else datetime.datetime.now().strftime("%Y-%m-%d"),
"sunrise_time": sunrise_time,
"civil_twilight_time": civil_twilight_time,
"golden_hour_end_time": golden_hour_end_time,
"timezone": timezone_name,
"season": season
},
"data_sources": {
"location": location_source,
"astronomy": "sunrise-sunset.org",
"timezone": f"timezonefinder ({timezone_confidence} confidence)"
}
}
return json.dumps(output_data)
except Exception as e:
return json.dumps({
"status": "error",
"message": f"Error processing request: {str(e)}",
"data_source": "none"
})
@tool
def get_photography_tip() -> str:
"""Get a random sunrise photography tip."""
tips = [
"Arrive 45 minutes before sunrise to set up your equipment and have time to adjust your composition.",
"Don't just shoot at the moment of sunrise - the light changes before and after are equally worth capturing.",
"Use a tripod and remote shutter for sharp photos in low light conditions.",
"Try using foreground elements (trees, rocks, lake reflections) to add depth to your photo.",
"Use smartphone apps to predict the sunrise direction at your location and plan your composition in advance.",
"Cloudy days can produce spectacular sunrise photos - don't abandon your shoot just because there's no clear sky.",
"Light changes rapidly during sunrise - use exposure bracketing (HDR) to ensure you capture enough dynamic range.",
"Consider using a telephoto lens for sunrise shots to magnify the sun and highlight atmospheric effects near the horizon.",
"Bring graduated ND filters to balance the bright sky with a darker foreground.",
"Research your location before traveling, understanding the sun's direction and terrain to find the best shooting spots.",
"Try shooting panoramas to capture the full range of colors in the sky during sunrise.",
"Shoot in RAW format to preserve more possibilities for post-processing.",
"When shooting sunrise at the beach, check tide schedules to plan your best shooting position.",
"Winter sunrises often have the most dramatic colors and longest golden hour periods.",
"City sunrises can be spectacular - look for elevated positions that show the city skyline.",
"In windy weather, add weight to your tripod to prevent camera shake during sunrise shoots.",
"Use interval shooting (time-lapse) to document the entire sunrise process.",
"Having the sky occupy 2/3 of your composition often creates more dramatic sunrise photos.",
"During sunrise, sunlight has a reddish-yellow tone. Try setting your white balance to 'cloudy' to preserve these colors.",
"If you want to capture star trails and sunrise in one composition, you'll need a star tracker and multiple exposure techniques."
]
# Add some humorous "rescue" tips
rescue_tips = [
"If you overslept and missed the sunrise, don't worry! Claim you're practicing 'minimalist photography' - not shooting is the ultimate art form.",
"Lens fogged up? Tell everyone you invented the 'dream filter effect' - it might be trending next week.",
"If you took 100 photos and none are satisfactory, convert them to black and white - photographers call this 'artistic redemption'.",
"Forgot your memory card? Congratulations on experiencing 'mindful photography' - recording the moment with your eyes and heart.",
"Sun hiding behind clouds and not coming out? Claim you were actually there to shoot 'cloud portraits' all along.",
"Forgot to adjust ISO and got noisy photos? No problem, call it 'digital grain' and claim it's your signature style.",
"If your sunrise photos look bland, try this pro tip: Add filters until it looks like an alien sunset, then post on social media with #NoFilter.",
"Tripod collapsed? That's the universe telling you it's time to try 'stream of consciousness photography'."
]
# 20% chance to return a "rescue" tip
if random.random() < 0.2:
tip = random.choice(rescue_tips)
tip_type = "humorous_rescue"
else:
tip = random.choice(tips)
tip_type = "practical"
return json.dumps({
"status": "success",
"message": tip,
"data": {
"tip": tip,
"tip_type": tip_type
},
"data_source": "curated_collection"
})
@tool
def parse_response(response_json: str) -> str:
"""Helper tool to parse JSON responses and format them for user display.
Args:
response_json: JSON string from one of the other tools
"""
try:
data = json.loads(response_json)
# Simply return the message for successful responses
if data.get("status") == "success":
return data.get("message", "Operation completed successfully, but no message was provided.")
else:
# For error responses, provide a clear error message
return f"ERROR: {data.get('message', 'An unknown error occurred.')}"
except Exception as e:
return f"Error parsing response: {str(e)}"
final_answer = FinalAnswerTool()
# Model setup
model = HfApiModel(
max_tokens=2096,
temperature=0.5,
model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
custom_role_conversions=None,
)
# Import image generation tool from Hub
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
with open("prompts.yaml", 'r') as stream:
prompt_templates = yaml.safe_load(stream)
agent = CodeAgent(
model=model,
tools=[
get_current_time_in_timezone,
get_sunrise_time,
get_photography_tip,
parse_response, # Add helper tool for parsing
image_generation_tool,
final_answer
],
max_steps=6,
verbosity_level=1,
grammar=None,
planning_interval=None,
name="Sunrise Photography Assistant",
description="An intelligent assistant that helps photographers find the best times for sunrise photography and provides professional advice",
prompt_templates=prompt_templates
)
GradioUI(agent).launch()