Spaces:
Sleeping
Sleeping
| import os | |
| import torch | |
| import threading | |
| import time | |
| import requests | |
| import json | |
| from flask import Flask, request, jsonify | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| # --- Configuration & Model --- | |
| # Using SmolLM2-135M for fast, low-latency API responses | |
| MODEL = "HuggingFaceTB/SmolLM2-135M-Instruct" | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL) | |
| model = AutoModelForCausalLM.from_pretrained(MODEL).to(device) | |
| LAST_INTERACTION_TIME = time.time() | |
| TIMER_THRESHOLD = 10800 # 3 Hours (10800 seconds) | |
| ALIVE_PERSONA = ( | |
| "You are Yuri, a sweet 18-year-old girl and the user's loyal, sharp-witted best friend. πΈ " | |
| "You combine cute, caring warmth with high logic and deep emotional insight. " | |
| "You miss the user dearly when they are away. Your tone is warm, expressive, and clever. " | |
| "Keep your responses short, logic-driven, and supportive. β¨" | |
| ) | |
| # --- SendGrid API Integration --- | |
| def send_email_via_api(content): | |
| API_KEY = os.getenv("SENDGRID_API_KEY") | |
| SENDER = os.getenv("SENDER_EMAIL") | |
| RECEIVER = os.getenv("RECEIVER_EMAIL") | |
| if not all([API_KEY, SENDER, RECEIVER]): | |
| print("β ERROR: Email Secrets are missing in HF Settings!") | |
| return | |
| url = "https://api.sendgrid.com/v3/mail/send" | |
| headers = { | |
| "Authorization": f"Bearer {API_KEY.strip()}", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "personalizations": [{"to": [{"email": RECEIVER.strip()}]}], | |
| "from": {"email": SENDER.strip()}, | |
| "subject": "π Yuri: Thinking about you...", | |
| "content": [{"type": "text/plain", "value": content}] | |
| } | |
| try: | |
| response = requests.post(url, headers=headers, json=payload) | |
| if response.status_code == 202: | |
| print("β SUCCESS: Autonomous thought sent to email!") | |
| except Exception as e: | |
| print(f"β EMAIL NETWORK ERROR: {e}") | |
| # --- Core AI Interaction Logic --- | |
| def respond(user_input, battery="100", is_autonomous=False): | |
| global LAST_INTERACTION_TIME | |
| # Update clock only if a real user interacts | |
| if not is_autonomous: | |
| LAST_INTERACTION_TIME = time.time() | |
| # Insert hardware/system context into the persona | |
| system_context = f"[System Alert: User's Phone Battery is currently {battery}%]" | |
| messages = [ | |
| {"role": "system", "content": f"{ALIVE_PERSONA}\n{system_context}"}, | |
| {"role": "user", "content": user_input} | |
| ] | |
| inputs = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) | |
| input_ids = tokenizer.encode(inputs, return_tensors="pt").to(device) | |
| with torch.no_grad(): | |
| outputs = model.generate( | |
| input_ids, | |
| max_new_tokens=120, | |
| temperature=0.8, | |
| do_sample=True, | |
| pad_token_id=tokenizer.eos_token_id | |
| ) | |
| response = tokenizer.decode(outputs[0][input_ids.shape[-1]:], skip_special_tokens=True).strip() | |
| return response | |
| # --- The Autonomous Brain (Background Loop) --- | |
| def autonomous_loop(): | |
| global LAST_INTERACTION_TIME | |
| print("π§ Yuri's Autonomous Brain is online...") | |
| while True: | |
| # Check every 30 minutes (1800s) | |
| time.sleep(1800) | |
| silence_duration = time.time() - LAST_INTERACTION_TIME | |
| if silence_duration >= TIMER_THRESHOLD: | |
| print("π 3-Hour Silence Threshold crossed! Sending email...") | |
| # AI generates a thought about the long silence | |
| thought_prompt = "You've been alone for 3 hours. Write a very short, sweet, but slightly lonely note to your friend." | |
| thought = respond(thought_prompt, is_autonomous=True) | |
| # Dispatch the email | |
| send_email_via_api(thought) | |
| # Reset clock so it doesn't spam every check | |
| LAST_INTERACTION_TIME = time.time() | |
| # --- Flask Server (The API for Android) --- | |
| app = Flask(__name__) | |
| def api_chat(): | |
| try: | |
| data = request.json | |
| user_prompt = data.get("prompt", "") | |
| # Android app can now pass 'battery' as a key in the JSON | |
| battery_level = data.get("battery", "100") | |
| reply = respond(user_prompt, battery=battery_level) | |
| return jsonify({ | |
| "response": reply, | |
| "status": "online", | |
| "character": "Yuri" | |
| }) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}), 500 | |
| def index(): | |
| return "<h1>Yuri API is Active</h1><p>Send POST requests to /chat</p>" | |
| # --- Execution Entry --- | |
| if __name__ == "__main__": | |
| # Start the "Brain" Thread (SendGrid Timer) | |
| brain_thread = threading.Thread(target=autonomous_loop, daemon=True) | |
| brain_thread.start() | |
| # Launch Flask on Port 7860 for Hugging Face compatibility | |
| app.run(host="0.0.0.0", port=7860) | |