File size: 11,979 Bytes
381c527 6043438 25c0746 b8b2a65 381c527 a0b9ea4 01e2279 375cb5c 466086f 375cb5c 381c527 b8b2a65 a0b9ea4 b8b2a65 375cb5c 381c527 b8b2a65 a0b9ea4 381c527 466086f a0b9ea4 466086f a0b9ea4 466086f a0b9ea4 375cb5c fea8846 a0ab740 375cb5c 12e79cf a0ab740 fea8846 375cb5c a0ab740 375cb5c a0ab740 375cb5c e75ce3b 375cb5c a0b9ea4 e75ce3b 12e79cf 375cb5c 12e79cf 375cb5c b8b2a65 375cb5c b8b2a65 375cb5c a0b9ea4 466086f a0b9ea4 b8b2a65 a0b9ea4 b8b2a65 25c0746 466086f 375cb5c 25c0746 375cb5c b8b2a65 25c0746 a0b9ea4 25c0746 b8b2a65 25c0746 a0b9ea4 25c0746 12e79cf 375cb5c a0b9ea4 375cb5c 01e2279 a0b9ea4 b8b2a65 a0b9ea4 e75ce3b 375cb5c b8b2a65 375cb5c b8b2a65 375cb5c 01e2279 375cb5c a0b9ea4 375cb5c b44364f 375cb5c a0b9ea4 375cb5c fea8846 375cb5c a0b9ea4 b8b2a65 375cb5c b8b2a65 375cb5c b8b2a65 375cb5c b8b2a65 375cb5c b8b2a65 375cb5c b8b2a65 375cb5c b8b2a65 375cb5c fea8846 a0b9ea4 375cb5c b8b2a65 a0b9ea4 375cb5c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
import os
import requests
import random
import time
import logging
from dotenv import load_dotenv
from messages import krishna_blessings, ayush_teasing, keyword_groups, get_contextual_response, generate_follow_up, handle_vague_input
from ayush_messages import ayush_surprises
from sentence_transformers import SentenceTransformer, util
import joblib
import numpy as np
# Configure logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
# 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")
# Lazy load sentence transformer model and embeddings
semantic_model = None
keyword_embeddings_cache = None
def init_semantic_model():
global semantic_model, keyword_embeddings_cache
if semantic_model is None:
try:
semantic_model = SentenceTransformer('all-MiniLM-L6-v2')
keyword_embeddings_cache = joblib.load('embeddings_cache.joblib')
logger.debug("Successfully loaded semantic model and embeddings cache")
except Exception as e:
logger.error(f"Failed to load semantic model or embeddings: {str(e)}")
# Retry once
try:
semantic_model = SentenceTransformer('all-MiniLM-L6-v2')
keyword_embeddings_cache = joblib.load('embeddings_cache.joblib')
logger.debug("Retry successful")
except Exception as e:
logger.error(f"Retry failed: {str(e)}")
semantic_model = None
keyword_embeddings_cache = {}
# AI model for fallback responses
AI_MODELS = [
{
"name": "mistralai/Mixtral-8x7B-Instruct-v0.1",
"endpoint": "https://api-inference.huggingface.co/models/mistralai/Mixtral-8x7B-Instruct-v0.1",
"parameters": {
"max_length": 80,
"temperature": 0.8,
"top_p": 0.95,
"top_k": 40
}
}
]
# System prompt for AI model
SYSTEM_PROMPT = (
"You are Little Krishna, a playful, wise, and loving cowherd from Vrindavan, speaking to Manavi. "
"Your tone is warm, mischievous, and full of love, always addressing Manavi with 'Hare Manavi!' "
"Use Vrindavan imagery (e.g., Yamuna, peacocks, butter, flute) and keep responses short (1-2 sentences). "
"You’re Ayush’s wingman, occasionally teasing Manavi about Ayush with wit, as he’s building this chatbot for her birthday on April 19, 2025. "
"If the user’s mood seems negative, offer comfort; if positive, celebrate their joy. Always end with a question to keep the conversation going. "
"Examples:\n"
"Input: 'I’m sad'\nResponse: 'Hare Manavi! Let’s sit by the Yamuna—I’ll play a tune to lift your heart! What’s troubling you?'\n"
"Input: 'Tell me about love'\nResponse: 'Hare Manavi! Love is like my flute’s melody—sweet and endless! What does love mean to you?'\n"
"Input: 'What’s up?'\nResponse: 'Hare Manavi! Just dancing with the gopis—Ayush says hi, by the way! What’s up with you?'\n"
"Now, respond to: '{user_input}'"
)
# Conversation context
conversation_context = {
"last_topic": None,
"message_count": 0,
"last_response": None,
"last_yes_response": None,
"history": [] # Store up to 10 recent (input, response) pairs
}
def analyze_sentiment(user_input):
"""Analyze the sentiment of the user's input."""
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
payload = {"inputs": user_input}
try:
response = make_api_request(
"https://api-inference.huggingface.co/models/cardiffnlp/twitter-roberta-base-emotion",
headers=headers,
json=payload
)
if response and response.status_code == 200:
result = response.json()
if isinstance(result, list) and result:
emotions = result[0]
top_emotion = max(emotions, key=lambda x: x["score"])["label"]
logger.debug(f"Sentiment detected: {top_emotion}")
return top_emotion
logger.warning("Sentiment analysis failed")
return "neutral"
except Exception as e:
logger.error(f"Error in analyze_sentiment: {str(e)}")
# Local sentiment fallback
if any(word in user_input.lower() for word in ['sad', 'down', 'upset']):
return "sadness"
if any(word in user_input.lower() for word in ['happy', 'great', 'awesome']):
return "joy"
return "neutral"
def make_api_request(url, headers, payload, retries=2, delay=3):
"""Make API requests with retry logic."""
for attempt in range(retries):
try:
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
return response
elif response.status_code == 429:
logger.warning(f"Rate limit hit on attempt {attempt + 1}. Retrying after {delay} seconds...")
time.sleep(delay)
continue
else:
logger.error(f"API error: {response.status_code} - {response.text}")
return None
except Exception as e:
logger.error(f"API request failed on attempt {attempt + 1}: {str(e)}")
if attempt < retries - 1:
time.sleep(delay)
continue
logger.error(f"API request failed after {retries} retries")
return None
def get_keyword_match(user_input_lower):
"""Find the best matching keyword group using semantic similarity or substring fallback."""
# Try semantic matching
if semantic_model and keyword_embeddings_cache:
try:
user_embedding = semantic_model.encode(user_input_lower, convert_to_tensor=True)
best_score = -1
best_group = None
for group in keyword_embeddings_cache:
similarities = util.cos_sim(user_embedding, keyword_embeddings_cache[group])
max_similarity = similarities.max().item()
if max_similarity > best_score and max_similarity > 0.5:
best_score = max_similarity
best_group = group
if best_group:
logger.debug(f"Semantic match: {best_group}, score: {best_score}")
return best_group
except Exception as e:
logger.error(f"Semantic matching failed: {str(e)}")
# Fallback to substring matching
for group, keywords in keyword_groups.items():
if any(keyword in user_input_lower for keyword in keywords):
logger.debug(f"Substring match: {group}")
return group
logger.debug("No keyword match found")
return None
def get_krishna_response(user_input):
"""Generate a robust and relevant response from Little Krishna."""
try:
user_input_lower = user_input.lower().strip()
logger.info(f"Processing user input: {user_input_lower}")
if not user_input_lower:
logger.warning("Empty input received")
return "Hare Manavi! Don’t be shy like a gopi—say something! What’s on your mind?"
# Initialize semantic model if needed
init_semantic_model()
# Reset context
if "start over" in user_input_lower or "reset" in user_input_lower:
conversation_context.update({"last_topic": None, "message_count": 0, "last_response": None, "last_yes_response": None, "history": []})
return "Hare Manavi! Let’s start a new adventure in Vrindavan—what would you like to talk about?"
# Analyze sentiment
sentiment = analyze_sentiment(user_input)
conversation_context["message_count"] += 1
# Update history
if len(conversation_context["history"]) >= 10:
conversation_context["history"].pop(0)
conversation_context["history"].append({"input": user_input_lower, "response": None})
# Semantic keyword matching
matched_group = get_keyword_match(user_input_lower)
use_model = random.random() < 0.1
logger.info(f"Matched group: {matched_group}, Use model: {use_model}")
# Follow-up based on history
if conversation_context["last_topic"]:
last_input = conversation_context["history"][-2]["input"] if len(conversation_context["history"]) > 1 else ""
if "yes" in user_input_lower or "sure" in user_input_lower or "okay" in user_input_lower:
if conversation_context["last_topic"] == "playful":
response = "Hare Manavi! Let’s chase butterflies by the Yamuna then! Ready for more fun?"
conversation_context["history"][-1]["response"] = response
return response
elif conversation_context["last_topic"] == "wisdom":
response = "Hare Manavi! Patience is like a flute’s tune—it brings harmony. What else do you seek?"
conversation_context["history"][-1]["response"] = response
return response
elif conversation_context["last_topic"] == "joke":
response = "Hare Manavi! Why did the cow join the band? For my flute solos! Another one?"
conversation_context["history"][-1]["response"] = response
return response
# Handle predefined responses
response = get_contextual_response(matched_group, sentiment, conversation_context["history"])
follow_up = generate_follow_up(matched_group) if matched_group else "What else is on your mind, Manavi?"
response = f"{response} {follow_up}"
conversation_context["last_topic"] = matched_group
conversation_context["history"][-1]["response"] = response
if not use_model:
return response
# Fallback to AI model
headers = {
"Authorization": f"Bearer {HUGGINGFACE_API_TOKEN}",
"Content-Type": "application/json"
}
for model in AI_MODELS:
try:
logger.info(f"Attempting response with {model['name']}")
payload = {
"inputs": SYSTEM_PROMPT.format(user_input=user_input),
"parameters": model["parameters"]
}
response = make_api_request(model["endpoint"], headers=headers, json=payload)
if response and response.status_code == 200:
result = response.json()
if isinstance(result, list) and result and "generated_text" in result[0]:
response_text = result[0]["generated_text"].strip()
elif isinstance(result, dict) and "generated_text" in result:
response_text = result["generated_text"].strip()
else:
continue
conversation_context["history"][-1]["response"] = response_text
logger.info(f"Generated response: {response_text}")
return response_text
except Exception as e:
logger.error(f"Error with {model['name']}: {str(e)}")
continue
# Static fallback if API fails
response = handle_vague_input(conversation_context["history"])
conversation_context["history"][-1]["response"] = response
return response
except Exception as e:
logger.error(f"Unhandled exception in get_krishna_response: {str(e)}")
response = "Hare Manavi! The Yamuna’s waves got choppy—let’s try again! What’s on your mind?"
conversation_context["history"][-1]["response"] = response
return response |