|
|
|
|
|
""" |
|
|
Simple Emotion-Aware Chatbot |
|
|
|
|
|
This chatbot: |
|
|
1. Gives direct, helpful answers to questions |
|
|
2. Detects user emotions using sentiment analysis |
|
|
3. Responds with appropriate tone and emojis |
|
|
4. Uses Mistral-7B-AWQ for high-quality responses |
|
|
5. No therapy-style conversations - just helpful assistance |
|
|
""" |
|
|
|
|
|
import gradio as gr |
|
|
import torch |
|
|
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline |
|
|
import re |
|
|
import random |
|
|
|
|
|
print("π€ Loading Simple Emotion-Aware Chatbot...") |
|
|
|
|
|
|
|
|
MODEL_ID = "TheBloke/Mistral-7B-Instruct-v0.2-AWQ" |
|
|
|
|
|
|
|
|
try: |
|
|
print("π Loading Mistral-7B-AWQ model...") |
|
|
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) |
|
|
model = AutoModelForCausalLM.from_pretrained( |
|
|
MODEL_ID, |
|
|
device_map="auto", |
|
|
torch_dtype=torch.float16, |
|
|
low_cpu_mem_usage=True, |
|
|
trust_remote_code=True |
|
|
) |
|
|
print("β
Mistral model loaded successfully!") |
|
|
except Exception as e: |
|
|
print(f"β οΈ Mistral AWQ failed: {e}") |
|
|
print("π¦ Falling back to DialoGPT...") |
|
|
MODEL_ID = "microsoft/DialoGPT-medium" |
|
|
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) |
|
|
model = AutoModelForCausalLM.from_pretrained( |
|
|
MODEL_ID, |
|
|
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32, |
|
|
low_cpu_mem_usage=True |
|
|
) |
|
|
print("β
DialoGPT fallback loaded!") |
|
|
|
|
|
|
|
|
if tokenizer.pad_token is None: |
|
|
tokenizer.pad_token = tokenizer.eos_token |
|
|
|
|
|
|
|
|
try: |
|
|
print("π Loading sentiment analysis model...") |
|
|
sentiment_analyzer = pipeline( |
|
|
"sentiment-analysis", |
|
|
model="distilbert-base-uncased-finetuned-sst-2-english", |
|
|
return_all_scores=True |
|
|
) |
|
|
print("β
Sentiment analyzer loaded!") |
|
|
except Exception as e: |
|
|
print(f"β οΈ Sentiment analyzer failed: {e}") |
|
|
sentiment_analyzer = None |
|
|
|
|
|
|
|
|
SIMPLE_SYSTEM_PROMPT = """You are a helpful AI assistant. Answer questions directly and clearly. Be friendly and concise. If someone seems upset, be understanding. If they seem happy, match their energy. Keep responses to 1-2 sentences unless more detail is needed.""" |
|
|
|
|
|
|
|
|
def detect_emotion(text): |
|
|
"""Detect user's emotion from their message""" |
|
|
if not sentiment_analyzer: |
|
|
return "neutral", 0.5 |
|
|
|
|
|
try: |
|
|
results = sentiment_analyzer(text)[0] |
|
|
|
|
|
for result in results: |
|
|
if result['label'] == 'POSITIVE': |
|
|
return "positive", result['score'] |
|
|
elif result['label'] == 'NEGATIVE': |
|
|
return "negative", result['score'] |
|
|
return "neutral", 0.5 |
|
|
except: |
|
|
return "neutral", 0.5 |
|
|
|
|
|
|
|
|
def get_appropriate_emoji(emotion, confidence): |
|
|
"""Select appropriate emoji based on detected emotion""" |
|
|
if confidence < 0.6: |
|
|
return "π" |
|
|
|
|
|
if emotion == "positive": |
|
|
return random.choice(["π", "π", "π", "π", "β¨"]) |
|
|
elif emotion == "negative": |
|
|
return random.choice(["π", "π", "π«", "π", "π"]) |
|
|
else: |
|
|
return random.choice(["π", "π", "π€", "π"]) |
|
|
|
|
|
|
|
|
def adjust_response_tone(response, emotion, confidence): |
|
|
"""Adjust response tone based on detected emotion""" |
|
|
if confidence < 0.6: |
|
|
return response |
|
|
|
|
|
if emotion == "negative": |
|
|
|
|
|
supportive_starters = [ |
|
|
"I understand that's tough. ", |
|
|
"That sounds challenging. ", |
|
|
"I hear you. ", |
|
|
"I can see why that would be difficult. " |
|
|
] |
|
|
if not any(starter.lower() in response.lower()[:20] for starter in supportive_starters): |
|
|
return f"{random.choice(supportive_starters)}{response}" |
|
|
|
|
|
elif emotion == "positive": |
|
|
|
|
|
positive_starters = [ |
|
|
"That's great! ", |
|
|
"Wonderful! ", |
|
|
"That sounds amazing! ", |
|
|
"How exciting! " |
|
|
] |
|
|
if "great" not in response.lower() and "wonderful" not in response.lower(): |
|
|
return f"{random.choice(positive_starters)}{response}" |
|
|
|
|
|
return response |
|
|
|
|
|
|
|
|
def generate_response(message, history): |
|
|
"""Generate a simple, emotion-aware response""" |
|
|
try: |
|
|
|
|
|
emotion, confidence = detect_emotion(message) |
|
|
print(f"Detected emotion: {emotion} (confidence: {confidence:.2f})") |
|
|
|
|
|
|
|
|
messages = [ |
|
|
{"role": "system", "content": SIMPLE_SYSTEM_PROMPT}, |
|
|
{"role": "user", "content": message} |
|
|
] |
|
|
|
|
|
|
|
|
if history: |
|
|
recent_history = history[-2:] |
|
|
full_messages = [{"role": "system", "content": SIMPLE_SYSTEM_PROMPT}] |
|
|
for user_msg, bot_msg in recent_history: |
|
|
full_messages.append({"role": "user", "content": user_msg}) |
|
|
if bot_msg: |
|
|
full_messages.append({"role": "assistant", "content": bot_msg}) |
|
|
full_messages.append({"role": "user", "content": message}) |
|
|
messages = full_messages |
|
|
|
|
|
|
|
|
if "mistral" in MODEL_ID.lower(): |
|
|
|
|
|
conversation = tokenizer.apply_chat_template( |
|
|
messages, |
|
|
tokenize=False, |
|
|
add_generation_prompt=True |
|
|
) |
|
|
else: |
|
|
|
|
|
conversation = f"{message}{tokenizer.eos_token}" |
|
|
|
|
|
|
|
|
inputs = tokenizer( |
|
|
conversation, |
|
|
return_tensors="pt", |
|
|
truncation=True, |
|
|
max_length=1024, |
|
|
padding=True |
|
|
) |
|
|
|
|
|
|
|
|
with torch.no_grad(): |
|
|
outputs = model.generate( |
|
|
inputs['input_ids'].to(model.device), |
|
|
attention_mask=inputs.get('attention_mask', None), |
|
|
max_new_tokens=80, |
|
|
temperature=0.7, |
|
|
top_p=0.9, |
|
|
top_k=50, |
|
|
repetition_penalty=1.1, |
|
|
do_sample=True, |
|
|
pad_token_id=tokenizer.pad_token_id or tokenizer.eos_token_id, |
|
|
eos_token_id=tokenizer.eos_token_id |
|
|
) |
|
|
|
|
|
|
|
|
raw_response = tokenizer.decode( |
|
|
outputs[:, inputs['input_ids'].shape[-1]:][0], |
|
|
skip_special_tokens=True |
|
|
).strip() |
|
|
|
|
|
|
|
|
response = raw_response.replace("Human:", "").replace("Assistant:", "").strip() |
|
|
|
|
|
|
|
|
response = re.sub(r'^(User|Bot|AI|Assistant):\s*', '', response) |
|
|
|
|
|
|
|
|
response = adjust_response_tone(response, emotion, confidence) |
|
|
|
|
|
|
|
|
emoji = get_appropriate_emoji(emotion, confidence) |
|
|
|
|
|
|
|
|
if len(response.split()) > 50: |
|
|
sentences = response.split('.') |
|
|
response = '. '.join(sentences[:2]) + '.' |
|
|
|
|
|
|
|
|
if response and not response.endswith(('!', '?', '.')): |
|
|
response += '.' |
|
|
|
|
|
final_response = f"{response} {emoji}" |
|
|
|
|
|
return final_response |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Error generating response: {e}") |
|
|
|
|
|
emotion, confidence = detect_emotion(message) |
|
|
emoji = get_appropriate_emoji(emotion, confidence) |
|
|
|
|
|
if emotion == "negative": |
|
|
return f"I understand you're going through something difficult. How can I help? {emoji}" |
|
|
elif emotion == "positive": |
|
|
return f"That's wonderful to hear! What would you like to know? {emoji}" |
|
|
else: |
|
|
return f"I'm here to help! What can I assist you with? {emoji}" |
|
|
|
|
|
|
|
|
def chatbot_response(message, history): |
|
|
"""Wrapper for Gradio interface""" |
|
|
if not message.strip(): |
|
|
return history |
|
|
|
|
|
response = generate_response(message, history) |
|
|
history.append([message, response]) |
|
|
return history |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Simple AI Assistant") as demo: |
|
|
gr.Markdown("# π€ Simple AI Assistant") |
|
|
gr.Markdown(""" |
|
|
**A helpful AI assistant that:** |
|
|
- Answers your questions directly and clearly |
|
|
- Detects your emotions and responds appropriately |
|
|
- Uses emojis to match the conversation tone |
|
|
- Keeps responses concise and useful |
|
|
""") |
|
|
|
|
|
chatbot = gr.Chatbot( |
|
|
value=[], |
|
|
height=500, |
|
|
type="messages" |
|
|
) |
|
|
|
|
|
msg = gr.Textbox( |
|
|
placeholder="Ask me anything! I'll help you out π", |
|
|
container=False, |
|
|
scale=7 |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
clear = gr.Button("Clear Chat", variant="secondary") |
|
|
|
|
|
|
|
|
with gr.Accordion("βοΈ Settings", open=False): |
|
|
gr.Markdown("*The chatbot is optimized for speed and quality by default.*") |
|
|
|
|
|
|
|
|
msg.submit( |
|
|
lambda m, h: ("", chatbot_response(m, h)), |
|
|
[msg, chatbot], |
|
|
[msg, chatbot] |
|
|
) |
|
|
|
|
|
clear.click(lambda: [], None, chatbot) |
|
|
|
|
|
|
|
|
gr.Examples( |
|
|
examples=[ |
|
|
"What's the weather like today?", |
|
|
"I'm feeling stressed about work", |
|
|
"Can you help me with Python code?", |
|
|
"I just got a promotion!", |
|
|
"How do I make pasta?" |
|
|
], |
|
|
inputs=msg, |
|
|
label="Try these examples:" |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
print("π Starting Simple AI Assistant...") |
|
|
demo.launch(share=True) |
|
|
print("π Chatbot is running!") |
|
|
|