File size: 10,335 Bytes
01d262c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
#!/usr/bin/env python3
"""
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 CONFIGURATION ===
MODEL_ID = "TheBloke/Mistral-7B-Instruct-v0.2-AWQ"

# Load main chat model
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!")

# Add pad token if needed
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token

# Load sentiment analysis model
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 ===
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."""

# === EMOTION DETECTION ===
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]
        # Convert to our emotion system
        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

# === EMOJI SELECTION ===
def get_appropriate_emoji(emotion, confidence):
    """Select appropriate emoji based on detected emotion"""
    if confidence < 0.6:
        return "😊"  # Default friendly
    
    if emotion == "positive":
        return random.choice(["😊", "πŸ˜„", "πŸŽ‰", "πŸ‘", "✨"])
    elif emotion == "negative":
        return random.choice(["πŸ˜”", "πŸ’™", "πŸ«‚", "😞", "πŸ’—"])
    else:
        return random.choice(["😊", "πŸ‘‹", "πŸ€”", "πŸ’­"])

# === RESPONSE TONE ADJUSTMENT ===
def adjust_response_tone(response, emotion, confidence):
    """Adjust response tone based on detected emotion"""
    if confidence < 0.6:
        return response  # Keep original tone for unclear emotions
    
    if emotion == "negative":
        # Add gentle, understanding tone
        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":
        # Add enthusiastic tone
        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

# === MAIN RESPONSE FUNCTION ===
def generate_response(message, history):
    """Generate a simple, emotion-aware response"""
    try:
        # 1. Detect user emotion
        emotion, confidence = detect_emotion(message)
        print(f"Detected emotion: {emotion} (confidence: {confidence:.2f})")
        
        # 2. Prepare conversation for model
        messages = [
            {"role": "system", "content": SIMPLE_SYSTEM_PROMPT},
            {"role": "user", "content": message}
        ]
        
        # Add recent history (last 2 exchanges only)
        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
        
        # 3. Generate response using model
        if "mistral" in MODEL_ID.lower():
            # Use Mistral chat template
            conversation = tokenizer.apply_chat_template(
                messages, 
                tokenize=False, 
                add_generation_prompt=True
            )
        else:
            # Simple format for DialoGPT
            conversation = f"{message}{tokenizer.eos_token}"
        
        # Tokenize
        inputs = tokenizer(
            conversation,
            return_tensors="pt",
            truncation=True,
            max_length=1024,
            padding=True
        )
        
        # Generate with optimized parameters
        with torch.no_grad():
            outputs = model.generate(
                inputs['input_ids'].to(model.device),
                attention_mask=inputs.get('attention_mask', None),
                max_new_tokens=80,  # Short, concise responses
                temperature=0.7,     # Balanced creativity
                top_p=0.9,          # Focused responses
                top_k=50,           # Good variety
                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
            )
        
        # Decode response
        raw_response = tokenizer.decode(
            outputs[:, inputs['input_ids'].shape[-1]:][0],
            skip_special_tokens=True
        ).strip()
        
        # 4. Clean up response
        response = raw_response.replace("Human:", "").replace("Assistant:", "").strip()
        
        # Remove any leftover conversation markers
        response = re.sub(r'^(User|Bot|AI|Assistant):\s*', '', response)
        
        # 5. Adjust tone based on emotion
        response = adjust_response_tone(response, emotion, confidence)
        
        # 6. Add appropriate emoji
        emoji = get_appropriate_emoji(emotion, confidence)
        
        # 7. Ensure response isn't too long or repetitive
        if len(response.split()) > 50:
            sentences = response.split('.')
            response = '. '.join(sentences[:2]) + '.'
        
        # 8. Final formatting
        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}")
        # Simple fallback based on emotion
        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}"

# === GRADIO INTERFACE ===
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

# Create Gradio interface
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"  # Modern Gradio format
    )
    
    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")
    
    # Settings (simplified)
    with gr.Accordion("βš™οΈ Settings", open=False):
        gr.Markdown("*The chatbot is optimized for speed and quality by default.*")
    
    # Event handlers
    msg.submit(
        lambda m, h: ("", chatbot_response(m, h)),
        [msg, chatbot],
        [msg, chatbot]
    )
    
    clear.click(lambda: [], None, chatbot)
    
    # Example conversations
    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!")