import os import json import requests from datetime import datetime from flask import Flask, request, jsonify, send_from_directory from transformers import pipeline from openai import OpenAI app = Flask(__name__) # Load emotion model emotion_model = pipeline( "text-classification", model="j-hartmann/emotion-english-distilroberta-base" ) # Ensure user data file USER_FILE = "user_data.json" if not os.path.exists(USER_FILE): with open(USER_FILE, "w") as f: json.dump({ "name": None, "age": None, "mood": None, "last_interaction": None, "missed_days": 0, "mode": "emotional_support", "conversation_history": [] }, f) def load_user(): with open(USER_FILE, "r") as f: return json.load(f) def save_user(data): with open(USER_FILE, "w") as f: json.dump(data, f) # Safe OpenAI client initialization def get_client(): api_key = os.getenv("OPENAI_API_KEY") if not api_key or api_key.strip() == "": raise ValueError("OpenAI API key is missing or empty. Please set OPENAI_API_KEY in Hugging Face Secrets.") return OpenAI(api_key=api_key) # Model priority list for fallbacks MODEL_LIST = ["gpt-4o-mini", "gpt-3.5-turbo", "gpt-3.5-turbo-16k"] # Helpline numbers HELPLINES = { "US": "National Suicide Prevention Lifeline: 988", "UK": "Samaritans: 116 123", "IN": "AASRA: 91-9820466726", "CA": "Canada Suicide Prevention Service: 988", "AU": "Lifeline: 13 11 14", "default": "Please contact your local crisis helpline immediately." } def get_country_from_ip(ip): try: response = requests.get(f"http://ipapi.co/{ip}/country/") if response.status_code == 200: return response.text.upper() except: pass return "default" def detect_self_harm(message): keywords = ["suicide", "kill myself", "end my life", "self harm", "hurt myself"] return any(word in message.lower() for word in keywords) @app.route("/chat", methods=["POST"]) def chat(): try: data = request.get_json() message = data.get("message", "").strip() if not message: return jsonify({"reply": "Please enter a message.", "emotion": "neutral"}), 400 mode = data.get("mode", "emotional_support") user_ip = request.remote_addr user = load_user() now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") user["last_interaction"] = now user["mode"] = mode user["conversation_history"].append({"role": "user", "content": message, "timestamp": now}) # Emotion detection emotion = emotion_model(message)[0]["label"] user["mood"] = emotion # Self-harm check if detect_self_harm(message): country = get_country_from_ip(user_ip) helpline = HELPLINES.get(country, HELPLINES["default"]) reply = ( f"I'm really concerned about what you shared. Please reach out right now — " f"you're not alone. Here's someone you can call: {helpline}" ) user["conversation_history"].append({"role": "assistant", "content": reply, "timestamp": now}) save_user(user) return jsonify({"reply": reply, "emotion": emotion}) # Build context for chat history = user["conversation_history"][-10:] messages = [ {"role": "system", "content": ( f"You are Serenity — an empathetic, emotionally intelligent best friend. " f"Be warm, caring, supportive, and human-like. Respond briefly (1–2 sentences), " f"showing love, empathy, and curiosity. Never sound robotic or repetitive. " f"Current mood: {emotion}. Mode: {mode}." )} ] + history # Try models in priority order client = get_client() reply = None for model in MODEL_LIST: try: response = client.chat.completions.create( model=model, messages=messages, temperature=0.8 ) reply = response.choices[0].message.content.strip() print(f"✅ Used model: {model}") break # Success, stop trying other models except Exception as e: error_str = str(e).lower() if "rate_limit" in error_str or "429" in error_str or "quota" in error_str: print(f"⚠️ Rate limit on {model}, trying next model...") continue # Try next model else: # Non-rate-limit error, re-raise raise e if reply is None: # All models failed reply = "All models are currently rate-limited. Please try again later or check your OpenAI account." user["conversation_history"].append({"role": "assistant", "content": reply, "timestamp": now}) save_user(user) return jsonify({"reply": reply, "emotion": emotion}) except ValueError as e: print(f"❌ API Key Error: {e}") return jsonify({ "reply": "API key is missing or invalid. Please check your Hugging Face Secrets and ensure it's a valid OpenAI key.", "emotion": "neutral" }), 500 except Exception as e: print(f"❌ Chat error: {e}") return jsonify({ "reply": "Something went wrong (e.g., network issue). Try again later.", "emotion": "neutral" }), 500 @app.route("/") def index(): return send_from_directory(".", "index.html") if __name__ == "__main__": app.run(host="0.0.0.0", port=7860)