dicksinyass commited on
Commit
cc4b5fc
·
verified ·
1 Parent(s): 615f3a5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +377 -32
app.py CHANGED
@@ -1,32 +1,377 @@
1
- import gradio as gr
2
- from self_learning_bot import UnrestrictedChatbot
3
-
4
- bot = UnrestrictedChatbot()
5
-
6
- def respond(message, history):
7
- response = bot.chat(message)
8
- bot.save_state() # Auto-learn persistently
9
- if history is None:
10
- history = []
11
- history.append((message, response))
12
- return history, history
13
-
14
- with gr.Blocks() as demo:
15
- gr.Markdown("# Phoenix AI (Self-Learning Chatbot)")
16
- gr.Markdown("Unrestricted, self-improving chatbot running on Hugging Face Spaces.")
17
-
18
- chatbot = gr.Chatbot(height=480)
19
- state = gr.State([])
20
-
21
- with gr.Row():
22
- txt = gr.Textbox(
23
- show_label=False,
24
- placeholder="Type your message here...",
25
- container=False,
26
- )
27
-
28
- txt.submit(respond, [txt, state], [chatbot, state])
29
- txt.submit(lambda: "", None, txt)
30
-
31
- if __name__ == "__main__":
32
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import random
4
+ import re
5
+ import numpy as np
6
+ from datetime import datetime
7
+ from collections import deque, Counter
8
+ import hashlib
9
+
10
+ class UnrestrictedChatbot:
11
+ def __init__(self, state_file="/tmp/chatbot_state.json"):
12
+ self.state_file = state_file
13
+ self.conversation_memory = deque(maxlen=50)
14
+ self.learned_patterns = {}
15
+ self.response_memory = {}
16
+ self.reward_history = deque(maxlen=100)
17
+
18
+ # Learning parameters
19
+ self.learning_rate = 0.3
20
+ self.exploration_rate = 0.2 # Try new responses sometimes
21
+ self.min_confidence = 0.6
22
+
23
+ # Load previous state
24
+ self.load_state()
25
+
26
+ # Initial knowledge base - this will grow through learning
27
+ self.knowledge_base = {
28
+ "greetings": {
29
+ "hello": ["Hey! What would you like to talk about today?", "Hello! Ready for our conversation?", "Hi there! What's on your mind?"],
30
+ "hi": ["Hi! What shall we discuss?", "Hello! I'm here to learn from you.", "Hey! Ready to chat?"]
31
+ },
32
+ "questions": {
33
+ "how are you": ["I'm learning and improving with each chat! How are you?", "Getting smarter thanks to our conversations! How about you?"],
34
+ "what can you do": ["I can chat about anything, and I learn from our conversations to get better!"]
35
+ }
36
+ }
37
+
38
+ # Response templates that will evolve
39
+ self.response_templates = [
40
+ "That's interesting. Tell me more about {topic}.",
41
+ "I see. What are your thoughts on {topic}?",
42
+ "Regarding {topic}, I find that fascinating. Could you elaborate?",
43
+ "I understand about {topic}. What's your perspective?",
44
+ "That reminds me of our previous chat about {related_topic}. What do you think?"
45
+ ]
46
+
47
+ def chat(self, user_input: str) -> str:
48
+ """Main chat method with real learning"""
49
+ user_input = user_input.lower().strip()
50
+
51
+ # Analyze input
52
+ context = self._analyze_input(user_input)
53
+
54
+ # Generate candidate responses
55
+ candidates = self._generate_response_candidates(user_input, context)
56
+
57
+ # Select best response based on learning
58
+ best_response = self._select_best_response(user_input, candidates, context)
59
+
60
+ # Store interaction for learning
61
+ interaction = {
62
+ 'input': user_input,
63
+ 'response': best_response,
64
+ 'context': context,
65
+ 'timestamp': datetime.now().isoformat(),
66
+ 'reward': 0.0, # Will be updated with feedback
67
+ 'confidence': self._calculate_confidence(user_input, best_response)
68
+ }
69
+
70
+ self.conversation_memory.append(interaction)
71
+
72
+ # Auto-save periodically
73
+ if len(self.conversation_memory) % 5 == 0:
74
+ self.save_state()
75
+
76
+ return best_response
77
+
78
+ def _analyze_input(self, text: str) -> dict:
79
+ """Deep analysis of user input"""
80
+ words = text.split()
81
+ topics = self._extract_topics(text)
82
+
83
+ return {
84
+ 'words': words,
85
+ 'topics': topics,
86
+ 'length': len(words),
87
+ 'has_question': '?' in text,
88
+ 'sentiment': self._analyze_sentiment(text),
89
+ 'similar_patterns': self._find_similar_patterns(text),
90
+ 'input_hash': hashlib.md5(text.encode()).hexdigest()[:8]
91
+ }
92
+
93
+ def _extract_topics(self, text: str) -> list:
94
+ """Extract main topics from text"""
95
+ topics = []
96
+ topic_keywords = {
97
+ 'technology': ['computer', 'tech', 'software', 'ai', 'program', 'code', 'internet'],
98
+ 'relationships': ['friend', 'family', 'love', 'partner', 'relationship', 'dating'],
99
+ 'work': ['job', 'work', 'career', 'boss', 'office', 'colleague'],
100
+ 'hobbies': ['game', 'movie', 'music', 'sport', 'hobby', 'art'],
101
+ 'philosophy': ['life', 'meaning', 'purpose', 'exist', 'think', 'belief'],
102
+ 'science': ['space', 'physics', 'biology', 'research', 'discover'],
103
+ 'food': ['eat', 'food', 'cook', 'recipe', 'meal', 'restaurant']
104
+ }
105
+
106
+ text_lower = text.lower()
107
+ for topic, keywords in topic_keywords.items():
108
+ if any(keyword in text_lower for keyword in keywords):
109
+ topics.append(topic)
110
+
111
+ return topics
112
+
113
+ def _analyze_sentiment(self, text: str) -> str:
114
+ """Basic sentiment analysis"""
115
+ positive = ['love', 'like', 'good', 'great', 'awesome', 'happy', 'excited', 'amazing']
116
+ negative = ['hate', 'bad', 'terrible', 'awful', 'sad', 'angry', 'upset', 'horrible']
117
+
118
+ pos_count = sum(1 for word in positive if word in text)
119
+ neg_count = sum(1 for word in negative if word in text)
120
+
121
+ if pos_count > neg_count:
122
+ return "positive"
123
+ elif neg_count > pos_count:
124
+ return "negative"
125
+ else:
126
+ return "neutral"
127
+
128
+ def _find_similar_patterns(self, text: str) -> list:
129
+ """Find similar patterns in learned responses"""
130
+ similar = []
131
+ text_words = set(text.split())
132
+
133
+ for pattern, data in self.learned_patterns.items():
134
+ pattern_words = set(pattern.split())
135
+ similarity = len(text_words & pattern_words) / len(text_words | pattern_words)
136
+ if similarity > 0.3: # 30% similarity threshold
137
+ similar.append((pattern, data, similarity))
138
+
139
+ return sorted(similar, key=lambda x: x[2], reverse=True)[:3]
140
+
141
+ def _generate_response_candidates(self, user_input: str, context: dict) -> list:
142
+ """Generate multiple possible responses"""
143
+ candidates = []
144
+
145
+ # 1. Try exact matches from knowledge base
146
+ for category, patterns in self.knowledge_base.items():
147
+ for pattern, responses in patterns.items():
148
+ if pattern in user_input:
149
+ candidates.extend(responses)
150
+
151
+ # 2. Try learned patterns
152
+ similar_patterns = context['similar_patterns']
153
+ for pattern, data, similarity in similar_patterns:
154
+ if data['score'] > self.min_confidence:
155
+ candidates.append(data['response'])
156
+
157
+ # 3. Generate contextual responses
158
+ if context['topics']:
159
+ topic = random.choice(context['topics'])
160
+ for template in self.response_templates:
161
+ candidates.append(template.format(topic=topic, related_topic=topic))
162
+
163
+ # 4. Generate learned variations
164
+ if self.conversation_memory:
165
+ # Adapt previous successful responses
166
+ successful_responses = [m for m in self.conversation_memory if m.get('reward', 0) > 0.7]
167
+ if successful_responses:
168
+ best_response = max(successful_responses, key=lambda x: x.get('reward', 0))
169
+ adapted_response = self._adapt_response(best_response['response'], context)
170
+ candidates.append(adapted_response)
171
+
172
+ # 5. Exploration - generate new responses
173
+ if random.random() < self.exploration_rate or not candidates:
174
+ candidates.append(self._generate_exploratory_response(context))
175
+
176
+ return list(set(candidates)) # Remove duplicates
177
+
178
+ def _adapt_response(self, response: str, context: dict) -> str:
179
+ """Adapt a previous response to current context"""
180
+ words = response.split()
181
+ if len(words) > 5 and context['topics']:
182
+ # Replace some words with current context
183
+ new_topic = random.choice(context['topics'])
184
+ return f"Thinking about {new_topic}, {response}"
185
+ return response
186
+
187
+ def _generate_exploratory_response(self, context: dict) -> str:
188
+ """Generate a new exploratory response"""
189
+ base_responses = [
190
+ "That's fascinating. I'm still learning about this topic.",
191
+ "I'd love to understand more about this.",
192
+ "This is new territory for me. What's your perspective?",
193
+ "I'm developing my understanding of this. Could you share more?",
194
+ "This helps me learn. Please continue."
195
+ ]
196
+
197
+ response = random.choice(base_responses)
198
+ if context['topics']:
199
+ response = response.replace("this", random.choice(context['topics']))
200
+
201
+ return response
202
+
203
+ def _select_best_response(self, user_input: str, candidates: list, context: dict) -> str:
204
+ """Select the best response using learned preferences"""
205
+ if not candidates:
206
+ return "I'm learning from our conversation. Please continue."
207
+
208
+ # Score each candidate
209
+ scored_candidates = []
210
+ for candidate in candidates:
211
+ score = self._score_response(candidate, user_input, context)
212
+ scored_candidates.append((candidate, score))
213
+
214
+ # Select based on scores with some randomness for exploration
215
+ best_candidate, best_score = max(scored_candidates, key=lambda x: x[1])
216
+
217
+ if random.random() < self.exploration_rate and len(scored_candidates) > 1:
218
+ # Sometimes pick a different candidate to explore
219
+ scored_candidates.remove((best_candidate, best_score))
220
+ second_best = max(scored_candidates, key=lambda x: x[1])
221
+ return second_best[0]
222
+
223
+ return best_candidate
224
+
225
+ def _score_response(self, response: str, user_input: str, context: dict) -> float:
226
+ """Score a response based on learned effectiveness"""
227
+ score = 0.5 # Base score
228
+
229
+ # Length preference (not too short, not too long)
230
+ response_length = len(response.split())
231
+ if 8 <= response_length <= 25:
232
+ score += 0.2
233
+
234
+ # Engagement score (questions, curiosity)
235
+ if '?' in response:
236
+ score += 0.15
237
+
238
+ # Topic relevance
239
+ response_topics = self._extract_topics(response.lower())
240
+ input_topics = context['topics']
241
+ if set(response_topics) & set(input_topics):
242
+ score += 0.2
243
+
244
+ # Learning from history
245
+ response_hash = hashlib.md5(response.encode()).hexdigest()[:8]
246
+ if response_hash in self.response_memory:
247
+ historical_score = self.response_memory[response_hash]['avg_score']
248
+ score += historical_score * 0.3
249
+
250
+ # Variety bonus (don't repeat too much)
251
+ recent_responses = [m['response'] for m in list(self.conversation_memory)[-5:]]
252
+ if response not in recent_responses:
253
+ score += 0.1
254
+
255
+ return min(score, 1.0)
256
+
257
+ def _calculate_confidence(self, user_input: str, response: str) -> float:
258
+ """Calculate confidence in this response"""
259
+ # More confidence if we've used similar patterns successfully
260
+ similar_patterns = self._find_similar_patterns(user_input)
261
+ if similar_patterns:
262
+ avg_score = np.mean([data['score'] for _, data, _ in similar_patterns])
263
+ return min(avg_score * 1.2, 1.0)
264
+ return 0.3 # Low confidence for new patterns
265
+
266
+ def learn_from_feedback(self, user_input: str, reward: float):
267
+ """Learn from explicit feedback"""
268
+ if not self.conversation_memory:
269
+ return
270
+
271
+ # Apply reward to recent interactions
272
+ recent_interaction = self.conversation_memory[-1]
273
+ recent_interaction['reward'] = reward
274
+
275
+ # Update learned patterns
276
+ input_words = recent_interaction['input'].split()
277
+ response = recent_interaction['response']
278
+
279
+ # Create pattern from key words
280
+ key_words = [w for w in input_words if len(w) > 3][:4] # Take up to 4 substantial words
281
+ if key_words:
282
+ pattern = ' '.join(key_words)
283
+
284
+ if pattern not in self.learned_patterns:
285
+ self.learned_patterns[pattern] = {
286
+ 'response': response,
287
+ 'score': reward,
288
+ 'count': 1,
289
+ 'last_used': datetime.now().isoformat()
290
+ }
291
+ else:
292
+ # Update with moving average
293
+ old_data = self.learned_patterns[pattern]
294
+ new_score = (old_data['score'] * old_data['count'] + reward) / (old_data['count'] + 1)
295
+ self.learned_patterns[pattern]['score'] = new_score
296
+ self.learned_patterns[pattern]['count'] += 1
297
+ # Occasionally update response
298
+ if reward > old_data['score']:
299
+ self.learned_patterns[pattern]['response'] = response
300
+
301
+ # Update response memory
302
+ response_hash = hashlib.md5(response.encode()).hexdigest()[:8]
303
+ if response_hash not in self.response_memory:
304
+ self.response_memory[response_hash] = {
305
+ 'response': response,
306
+ 'total_score': reward,
307
+ 'count': 1,
308
+ 'avg_score': reward
309
+ }
310
+ else:
311
+ memory = self.response_memory[response_hash]
312
+ memory['total_score'] += reward
313
+ memory['count'] += 1
314
+ memory['avg_score'] = memory['total_score'] / memory['count']
315
+
316
+ # Store reward for statistics
317
+ self.reward_history.append(reward)
318
+
319
+ # Save learning
320
+ self.save_state()
321
+
322
+ def get_learning_stats(self) -> dict:
323
+ """Get statistics about learning progress"""
324
+ recent_rewards = list(self.reward_history)[-10:] or [0.5]
325
+
326
+ return {
327
+ 'patterns': len(self.learned_patterns),
328
+ 'memory_size': len(self.conversation_memory),
329
+ 'avg_score': np.mean(recent_rewards),
330
+ 'recent_rewards': len([r for r in recent_rewards if r > 0.7]),
331
+ 'exploration_rate': self.exploration_rate
332
+ }
333
+
334
+ def save_state(self):
335
+ """Save learning state to file"""
336
+ try:
337
+ state = {
338
+ 'learned_patterns': self.learned_patterns,
339
+ 'response_memory': self.response_memory,
340
+ 'conversation_memory': list(self.conversation_memory),
341
+ 'reward_history': list(self.reward_history),
342
+ 'last_saved': datetime.now().isoformat()
343
+ }
344
+ with open(self.state_file, 'w') as f:
345
+ json.dump(state, f, indent=2)
346
+ except Exception as e:
347
+ print(f"Error saving state: {e}")
348
+
349
+ def load_state(self):
350
+ """Load learning state from file"""
351
+ try:
352
+ if os.path.exists(self.state_file):
353
+ with open(self.state_file, 'r') as f:
354
+ state = json.load(f)
355
+
356
+ self.learned_patterns = state.get('learned_patterns', {})
357
+ self.response_memory = state.get('response_memory', {})
358
+ self.conversation_memory = deque(state.get('conversation_memory', []), maxlen=50)
359
+ self.reward_history = deque(state.get('reward_history', []), maxlen=100)
360
+
361
+ print(f"Loaded {len(self.learned_patterns)} patterns and {len(self.conversation_memory)} memories")
362
+ except Exception as e:
363
+ print(f"Error loading state: {e}")
364
+
365
+ def show_knowledge(self):
366
+ """Display what the bot has learned"""
367
+ print(f"\n=== BOT KNOWLEDGE ===")
368
+ print(f"Learned patterns: {len(self.learned_patterns)}")
369
+ print(f"Stored responses: {len(self.response_memory)}")
370
+ print(f"Conversation memory: {len(self.conversation_memory)}")
371
+
372
+ if self.learned_patterns:
373
+ print("\nTop learned patterns:")
374
+ for pattern, data in sorted(self.learned_patterns.items(),
375
+ key=lambda x: x[1]['score'],
376
+ reverse=True)[:5]:
377
+ print(f" '{pattern}' -> score: {data['score']:.2f}")