BirthdayM / app.py
ayush2917's picture
Update app.py
2dcee35 verified
import os
from datetime import datetime
from flask import Flask, render_template, request, jsonify, escape
from dotenv import load_dotenv
from chatbot import get_krishna_response
from image_api import generate_krishna_image, generate_comic_strip
from countdown import get_countdown
from messages import daily_blessings, auto_generate_birthday_message
from ayush_messages import ayush_surprises
import logging
import requests
import random
import backoff
from functools import lru_cache
# Configure logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
# Initialize Firebase if available
try:
from firebase_config import save_chat_message, get_chat_history
firebase_enabled = True
logger.debug("Firebase integration enabled")
except ImportError:
firebase_enabled = False
logger.debug("Firebase disabled: firebase_config.py not found")
# Load environment variables
load_dotenv()
HUGGINGFACE_API_TOKEN = os.getenv("HUGGINGFACE_API_TOKEN")
if not HUGGINGFACE_API_TOKEN:
logger.error("HUGGINGFACE_API_TOKEN not found in environment variables")
raise ValueError("HUGGINGFACE_API_TOKEN is required")
app = Flask(__name__)
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
# Daily messages with Bhagavad Gita verses
DAILY_MESSAGES = {
0: {"message": "On Sundays, shine like the sun for all!", "verse": "Bhagavad Gita 9.29"},
1: {"message": "Monday: Do your duty without attachment.", "verse": "Bhagavad Gita 2.47"},
2: {"message": "Tuesday: The soul is eternal, beyond death.", "verse": "Bhagavad Gita 2.20"},
3: {"message": "Wednesday: Krishna brings victory!", "verse": "Bhagavad Gita 18.78"},
4: {"message": "Thursday: Surrender to the divine will.", "verse": "Bhagavad Gita 18.66"},
5: {"message": "Friday: Cultivate compassion.", "verse": "Bhagavad Gita 16.1-3"},
6: {"message": "Saturday: Find joy in spiritual practice.", "verse": "Bhagavad Gita 6.17"}
}
# Festival messages
FESTIVAL_MESSAGES = {
(1, 26): {"message": "Happy Republic Day! Let Dharma guide our nation.", "verse": "Bhagavad Gita 4.7", "special": True},
(8, 19): {"message": "Happy Krishna Janmashtami! Celebrate the divine birth.", "verse": "Bhagavad Gita 4.9", "special": True},
(4, 19): {"message": ayush_surprises["birthday"], "verse": "Bhagavad Gita 2.22", "special": True}
}
def parse_huggingface_response(response):
"""Parse Hugging Face API response."""
try:
result = response.json()
if isinstance(result, list) and result and "generated_text" in result[0]:
return result[0]["generated_text"].strip()
elif isinstance(result, dict) and "generated_text" in result:
return result["generated_text"].strip()
elif isinstance(result, str):
return result.strip()
logger.error("Unexpected API response format")
return None
except Exception as e:
logger.error(f"Error parsing API response: {str(e)}")
return None
def get_daily_message_and_blessing():
"""Retrieve daily message and blessing."""
try:
today = datetime.now()
day_of_week = (today.weekday() + 1) % 7
month_day = (today.month, today.day)
if month_day in FESTIVAL_MESSAGES:
daily_message = FESTIVAL_MESSAGES[month_day]
else:
daily_message = DAILY_MESSAGES.get(day_of_week,
{"message": "Seek the divine today.", "verse": "Bhagavad Gita 6.30"})
daily_message['special'] = False
daily_blessing_index = (today.day - 1) % len(daily_blessings)
daily_blessing = daily_blessings[daily_blessing_index]
return {
"message": daily_message["message"],
"verse": daily_message["verse"],
"is_special": daily_message.get("special", False),
"blessing": daily_blessing
}
except Exception as e:
logger.error(f"Error in get_daily_message_and_blessing: {str(e)}")
return {
"message": "Seek the divine today.",
"verse": "Bhagavad Gita 6.30",
"is_special": False,
"blessing": "Hare Manavi! May Vrindavan’s joy fill your heart!"
}
@backoff.on_exception(backoff.expo, (requests.exceptions.RequestException, requests.exceptions.HTTPError), max_tries=2, max_time=60)
def make_api_request(url, headers, payload, timeout=30):
"""Make API requests with exponential backoff."""
try:
response = requests.post(url, headers=headers, json=payload, timeout=timeout)
if response.status_code == 200:
return response
logger.error(f"API error: {response.status_code} - {response.text}")
raise requests.exceptions.HTTPError(f"API returned status {response.status_code}")
except requests.exceptions.RequestException as e:
logger.error(f"API request failed: {str(e)}")
raise
@lru_cache(maxsize=10)
def generate_birthday_message():
"""Generate a birthday message for Manavi."""
try:
today = datetime.now()
if today.month == 4 and today.day == 19:
return ayush_surprises["birthday"]
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
prompt = (
"You are Little Krishna, speaking to Manavi on her birthday, April 19, 2025. "
"Ayush created this chatbot as a surprise. Write a short, heartfelt birthday message (1-2 sentences) starting with 'Happy Birthday, Manavi!', using Vrindavan imagery. "
"Example: 'Happy Birthday, Manavi! I’ve brought Vrindavan’s sweetest butter and a flute melody to make your heart dance!'"
)
payload = {
"inputs": prompt,
"parameters": {
"max_length": 60,
"temperature": 0.9,
"top_p": 0.9,
"top_k": 50
}
}
response = make_api_request(
"https://api-inference.huggingface.co/models/mistralai/Mixtral-8x7B-Instruct-v0.1",
headers=headers,
payload=payload
)
result = parse_huggingface_response(response)
if result and result.startswith("Happy Birthday, Manavi!"):
return result
logger.warning("Invalid API response; using fallback")
return auto_generate_birthday_message(include_tease=True)
except Exception as e:
logger.error(f"Error in generate_birthday_message: {str(e)}")
return auto_generate_birthday_message(include_tease=True)
@lru_cache(maxsize=10)
def generate_gift_suggestions():
"""Generate personalized gift suggestions for Manavi."""
try:
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
prompt = (
"You are Little Krishna, speaking to Manavi on her birthday, April 19, 2025. "
"Suggest three gifts reflecting her interests in art, nature, and spirituality, in a playful Krishna tone. "
"Return as a numbered list (1., 2., 3.). Example: '1. A peacock feather paintbrush for vibrant art!\n2. A lotus journal for serene thoughts!\n3. A flute pendant for spiritual melodies!'"
)
payload = {
"inputs": prompt,
"parameters": {
"max_length": 100,
"temperature": 0.9,
"top_p": 0.9,
"top_k": 50
}
}
response = make_api_request(
"https://api-inference.huggingface.co/models/mistralai/Mixtral-8x7B-Instruct-v0.1",
headers=headers,
payload=payload
)
result = parse_huggingface_response(response)
if result:
suggestions = result.strip().split('\n')
return [s.strip() for s in suggestions if s.strip()]
logger.warning("Using fallback gift suggestions")
return [
"Hare Manavi! A peacock feather paintbrush for vibrant art!",
"Hare Manavi! A lotus journal for serene thoughts!",
"Hare Manavi! A flute pendant for spiritual melodies!"
]
except Exception as e:
logger.error(f"Error in generate_gift_suggestions: {str(e)}")
return [
"Hare Manavi! A peacock feather paintbrush for vibrant art!",
"Hare Manavi! A lotus journal for serene thoughts!",
"Hare Manavi! A flute pendant for spiritual melodies!"
]
@lru_cache(maxsize=10)
def generate_horoscope():
"""Generate a daily horoscope for Manavi."""
try:
today = datetime.now().strftime("%B %d, %Y")
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
prompt = (
f"You are Little Krishna, speaking to Manavi on {today}. "
"Generate a short daily horoscope (1-2 sentences) for Manavi, reflecting her interests in art, nature, and spirituality, in a playful tone with Vrindavan imagery. "
"Example: 'Hare Manavi! Today, the Yamuna inspires your art—paint with Vrindavan’s colors and let your spirit dance!'"
)
payload = {
"inputs": prompt,
"parameters": {
"max_length": 60,
"temperature": 0.9,
"top_p": 0.9,
"top_k": 50
}
}
response = make_api_request(
"https://api-inference.huggingface.co/models/mistralai/Mixtral-8x7B-Instruct-v0.1",
headers=headers,
payload=payload
)
result = parse_huggingface_response(response)
if result:
return result
logger.warning("Using fallback horoscope")
return "Hare Manavi! Today, the Yamuna inspires your art—paint with Vrindavan’s colors and let your spirit dance!"
except Exception as e:
logger.error(f"Error in generate_horoscope: {str(e)}")
return "Hare Manavi! Today, the Yamuna inspires your art—paint with Vrindavan’s colors and let your spirit dance!"
@app.route('/')
def home():
"""Render the home page."""
try:
daily_data = get_daily_message_and_blessing()
countdown_days = get_countdown()
daily_horoscope = generate_horoscope()
return render_template('home.html',
daily_message=daily_data['message'],
verse_reference=daily_data['verse'],
is_special=daily_data['is_special'],
daily_blessing=daily_data['blessing'],
daily_horoscope=daily_horoscope,
countdown=countdown_days)
except Exception as e:
logger.error(f"Error in / route: {str(e)}")
return render_template('error.html', error_message='Failed to load homepage. Please try again.'), 500
@app.route('/chat', methods=['GET', 'POST'])
def chat():
"""Handle chat interactions."""
try:
if request.method == 'POST':
if 'message' not in request.form:
logger.error("Missing 'message' in POST request")
return jsonify({'error': 'Missing message parameter'}), 400
user_input = escape(request.form['message'].strip())
if not user_input:
logger.error("Empty message received")
return jsonify({'error': 'Message cannot be empty'}), 400
if len(user_input) > 500:
logger.error("Message too long")
return jsonify({'error': 'Message too long (max 500 characters)'}), 400
logger.info(f"Received chat input: {user_input}")
reply = get_krishna_response(user_input)
logger.info(f"Generated reply: {reply}")
if firebase_enabled:
try:
save_chat_message(user_input, reply)
logger.debug("Saved chat message to Firebase")
except Exception as e:
logger.error(f"Failed to save chat message: {str(e)}")
return jsonify({'reply': reply})
try:
chat_history = get_chat_history() if firebase_enabled else []
logger.debug(f"Retrieved chat history: {len(chat_history)} messages")
except Exception as e:
logger.error(f"Failed to retrieve chat history: {str(e)}")
chat_history = []
return render_template('chat.html', chat_history=chat_history)
except Exception as e:
logger.error(f"Error in /chat route: {str(e)}")
return render_template('error.html', error_message='Failed to load chat page. Please try again.'), 500
@app.route('/adventure', methods=['GET', 'POST'])
def adventure():
"""Handle the interactive Vrindavan adventure game."""
try:
if request.method == 'POST':
choice = request.form.get('choice', '')
current_scene = request.form.get('current_scene', 'start')
logger.info(f"Adventure POST: current_scene={current_scene}, choice={choice}")
scenes = {
'start': {
'prompt': (
"You are Little Krishna, guiding Manavi on a Vrindavan adventure. "
"Start with a short scene (2-3 sentences) and offer two choices. "
"Example: 'Hare Manavi! By the Yamuna’s sparkling waters, shall we chase peacocks or sneak butter? (Choices: Chase peacocks, Sneak butter)'"
),
'next_scenes': {'Chase peacocks': 'peacocks', 'Sneak butter': 'butter'}
},
'peacocks': {
'prompt': (
"Manavi chose to chase peacocks. Describe a short scene (2-3 sentences) and offer two new choices. "
"Example: 'Hare Manavi! We chased peacocks, their feathers shimmering—shall we climb a kadamba tree or play a flute tune? (Choices: Climb tree, Play flute)'"
),
'next_scenes': {'Climb tree': 'tree', 'Play flute': 'flute'}
},
'butter': {
'prompt': (
"Manavi chose to sneak butter. Describe a short scene (2-3 sentences) and offer two new choices. "
"Example: 'Hare Manavi! We sneaked butter, giggling—shall we hide by the Yamuna or share with calves? (Choices: Hide by Yamuna, Share with calves)'"
),
'next_scenes': {'Hide by Yamuna': 'yamuna', 'Share with calves': 'calves'}
},
'tree': {
'prompt': (
"Manavi chose to climb the kadamba tree. Describe a short scene (2-3 sentences) and end the story. "
"Example: 'Hare Manavi! We climbed the kadamba tree, watching peacocks—what a magical Vrindavan day!'"
),
'next_scenes': {}
},
'flute': {
'prompt': (
"Manavi chose to play the flute. Describe a short scene (2-3 sentences) and end the story. "
"Example: 'Hare Manavi! I played a flute tune, and peacocks danced—what a joyful Vrindavan memory!'"
),
'next_scenes': {}
},
'yamuna': {
'prompt': (
"Manavi chose to hide by the Yamuna. Describe a short scene (2-3 sentences) and end the story. "
"Example: 'Hare Manavi! We hid by the Yamuna, sharing butter—what a mischievous adventure!'"
),
'next_scenes': {}
},
'calves': {
'prompt': (
"Manavi chose to share butter with calves. Describe a short scene (2-3 sentences) and end the story. "
"Example: 'Hare Manavi! We shared butter with calves, who mooed happily—what a sweet moment!'"
),
'next_scenes': {}
}
}
scene = scenes.get(current_scene, scenes['start'])
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
payload = {
"inputs": scene['prompt'],
"parameters": {
"max_length": 80,
"temperature": 0.9,
"top_p": 0.9,
"top_k": 50
}
}
response = make_api_request(
"https://api-inference.huggingface.co/models/mistralai/Mixtral-8x7B-Instruct-v0.1",
headers=headers,
payload=payload
)
if response and response.status_code == 200:
result = parse_huggingface_response(response)
story_text = result or scene['prompt'].split('Example: ')[1]
else:
logger.error("Failed to generate adventure scene")
story_text = scene['prompt'].split('Example: ')[1]
next_scene = scene['next_scenes'].get(choice)
if next_scene:
return jsonify({'scene': story_text, 'current_scene': next_scene, 'choices': list(scene['next_scenes'].keys())})
return jsonify({'scene': story_text, 'current_scene': None, 'choices': []})
return render_template('adventure.html')
except Exception as e:
logger.error(f"Error in /adventure route: {str(e)}")
return render_template('error.html', error_message='Failed to load adventure page. Please try again.'), 500
@app.route('/message')
def message():
"""Render the birthday message page."""
try:
birthday_message = generate_birthday_message()
gift_suggestions = generate_gift_suggestions()
birthday_image_url = "/static/assets/krishna.png"
today = datetime.now()
is_birthday = today.month == 4 and today.day == 19
return render_template('message.html',
birthday_message=birthday_message,
birthday_image_url=birthday_image_url,
gift_suggestions=gift_suggestions,
is_birthday=is_birthday)
except Exception as e:
logger.error(f"Error in /message route: {str(e)}")
return render_template('error.html', error_message='Failed to load birthday message page. Please try again.'), 500
@app.route('/image', methods=['POST'])
def image():
"""Generate a Krishna-themed image."""
try:
prompt = request.json.get('prompt', '')
if not prompt:
return jsonify({'error': 'Missing prompt parameter'}), 400
image_url = generate_krishna_image(prompt)
if image_url:
return jsonify({'image_url': image_url})
return jsonify({'error': 'Failed to generate image'}), 500
except Exception as e:
logger.error(f"Error in /image route: {str(e)}")
return jsonify({'error': 'Internal Server Error. Please try again.'}), 500
@app.route('/comic', methods=['GET'])
def comic():
"""Generate a Krishna-themed comic strip."""
try:
comic_images = generate_comic_strip()
return jsonify({'comic_images': comic_images})
except Exception as e:
logger.error(f"Error in /comic route: {str(e)}")
return jsonify({'error': 'Internal Server Error. Please try again.'}), 500
@app.route('/story', methods=['POST'])
def story():
"""Generate a Krishna story."""
try:
theme = request.json.get('theme', 'Vrindavan')
prompt = (
f"You are Little Krishna, telling a short story (2-3 sentences) to Manavi with the theme '{theme}'. "
f"Keep it fun and Krishna-like. Example: 'Hare Manavi! I hid the gopis’ butter in a peacock’s nest—what a Vrindavan adventure!'"
)
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
payload = {
"inputs": prompt,
"parameters": {
"max_length": 80,
"temperature": 0.9,
"top_p": 0.9,
"top_k": 50
}
}
response = make_api_request(
"https://api-inference.huggingface.co/models/mistralai/Mixtral-8x7B-Instruct-v0.1",
headers=headers,
payload=payload
)
result = parse_huggingface_response(response)
if result:
return jsonify({'story': result})
logger.error("Failed to generate story")
return jsonify({'story': f"Hare Manavi! I hid the gopis’ {theme} in a peacock’s nest—what a Vrindavan adventure!'"})
except Exception as e:
logger.error(f"Error in /story route: {str(e)}")
return jsonify({'error': 'Internal Server Error. Please try again.'}), 500
@app.route('/story_audio', methods=['POST'])
def story_audio():
"""Generate audio narration for a story."""
try:
story_text = request.json.get('story_text', '')
if not story_text:
return jsonify({'error': 'Missing story_text parameter'}), 400
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
payload = {"inputs": story_text}
response = make_api_request(
"https://api-inference.huggingface.co/models/facebook/tts-transformer-en-ljspeech",
headers=headers,
payload=payload
)
if response and response.status_code == 200:
audio_path = "static/audio/story_audio.mp3"
os.makedirs(os.path.dirname(audio_path), exist_ok=True)
with open(audio_path, "wb") as f:
f.write(response.content)
return jsonify({'audio_url': f"/{audio_path}"})
logger.error("Failed to generate story audio")
return jsonify({'error': 'Failed to generate audio'}), 500
except Exception as e:
logger.error(f"Error in /story_audio route: {str(e)}")
return jsonify({'error': 'Internal Server Error. Please try again.'}), 500
@app.route('/riddle', methods=['GET'])
def riddle():
"""Generate a Krishna-themed riddle."""
try:
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
prompt = (
"You are Little Krishna, speaking to Manavi. Generate a short, Krishna-themed riddle (1-2 sentences) with a playful tone, using Vrindavan imagery. "
"Include the answer in parentheses. Example: 'Hare Manavi! I play a tune that makes gopis dance, but I’m not a drum—what am I? (Answer: Flute)'"
)
payload = {
"inputs": prompt,
"parameters": {
"max_length": 60,
"temperature": 0.9,
"top_p": 0.9,
"top_k": 50
}
}
response = make_api_request(
"https://api-inference.huggingface.co/models/mistralai/Mixtral-8x7B-Instruct-v0.1",
headers=headers,
payload=payload
)
result = parse_huggingface_response(response)
if result:
return jsonify({'riddle': result})
logger.error("Failed to generate riddle")
return jsonify({'riddle': "Hare Manavi! I play a tune that makes gopis dance—what am I? (Answer: Flute)"})
except Exception as e:
logger.error(f"Error in /riddle route: {str(e)}")
return jsonify({'error': 'Internal Server Error. Please try again.'}), 500
@app.route('/countdown')
def countdown():
"""Return days until Manavi's birthday."""
try:
days_left = get_countdown()
return jsonify({'days': days_left})
except Exception as e:
logger.error(f"Error in /countdown route: {str(e)}")
return jsonify({'error': 'Internal Server Error. Please try again.'}), 500
if __name__ == '__main__':
port = int(os.environ.get('PORT', 7860))
app.run(host='0.0.0.0', port=port, debug=False)