markobinario commited on
Commit
5674ea3
·
verified ·
1 Parent(s): 0d57887

Delete chatbot.py

Browse files
Files changed (1) hide show
  1. chatbot.py +0 -453
chatbot.py DELETED
@@ -1,453 +0,0 @@
1
- import requests
2
- import json
3
- from sentence_transformers import SentenceTransformer
4
- import numpy as np
5
- from sklearn.metrics.pairwise import cosine_similarity
6
- from recommender import CourseRecommender
7
- import re
8
- import string
9
- from difflib import SequenceMatcher
10
- import random
11
- import time
12
-
13
- class Chatbot:
14
- def __init__(self):
15
- self.qa_pairs = []
16
- self.question_embeddings = []
17
- self.model = None
18
- self.database_url = "https://database-dhe2.onrender.com"
19
- self.recommender = None
20
- self.conversation_history = []
21
- self.user_context = {}
22
- self.load_model()
23
- self.load_recommender()
24
- self.load_qa_data()
25
- self.load_conversational_responses()
26
-
27
- def load_model(self):
28
- """Load the sentence transformer model with error handling"""
29
- import time
30
- import os
31
-
32
- # List of models to try in order of preference
33
- models_to_try = [
34
- 'all-MiniLM-L6-v2',
35
- 'paraphrase-MiniLM-L6-v2',
36
- 'all-MiniLM-L12-v2'
37
- ]
38
-
39
- for model_name in models_to_try:
40
- try:
41
- print(f"Loading sentence transformer model: {model_name}...")
42
-
43
- # Try with cache directory first
44
- cache_dir = os.path.join(os.getcwd(), 'model_cache')
45
- os.makedirs(cache_dir, exist_ok=True)
46
-
47
- self.model = SentenceTransformer(model_name, cache_folder=cache_dir)
48
- print(f"Model {model_name} loaded successfully")
49
- return
50
-
51
- except Exception as e:
52
- print(f"Error loading {model_name}: {str(e)}")
53
- continue
54
-
55
- # If all models fail, try without cache
56
- try:
57
- print("Trying without cache directory...")
58
- self.model = SentenceTransformer('all-MiniLM-L6-v2')
59
- print("Model loaded successfully without cache")
60
- except Exception as e:
61
- print(f"Final attempt failed: {str(e)}")
62
- raise Exception("Could not load any sentence transformer model")
63
-
64
- def load_recommender(self):
65
- """Load the course recommender with error handling"""
66
- try:
67
- print("Loading course recommender...")
68
- self.recommender = CourseRecommender()
69
- print("Recommender loaded successfully")
70
- except Exception as e:
71
- print(f"Error loading recommender: {str(e)}")
72
- self.recommender = None
73
-
74
- def load_qa_data(self):
75
- """Load Q&A pairs from the faqs table in the database"""
76
- try:
77
- # The API requires a question parameter, so we can't load all FAQs at once
78
- # Instead, we'll use the API for real-time querying and keep fallback data
79
- print("Database API requires question parameter - using real-time querying with fallback data")
80
- self._load_fallback_data()
81
- except Exception as e:
82
- print(f"Error loading FAQ data: {str(e)}")
83
- self._load_fallback_data()
84
-
85
- def _load_fallback_data(self):
86
- """Load fallback data if database is unavailable"""
87
- # No fallback data - rely entirely on database API and conversational responses
88
- self.qa_pairs = []
89
- self.question_embeddings = []
90
- print("No fallback data loaded - using database API and conversational responses only")
91
-
92
- def load_conversational_responses(self):
93
- """Load conversational response templates and patterns"""
94
- self.conversational_patterns = {
95
- 'greetings': [
96
- "Hello! How can I help you today?",
97
- "Hi there! What can I assist you with?",
98
- "Good day! I'm here to help with your questions.",
99
- "Hello! I'm your AI assistant. What would you like to know?"
100
- ],
101
- 'farewells': [
102
- "Goodbye! Have a great day!",
103
- "See you later! Feel free to ask if you need more help.",
104
- "Take care! I'm here whenever you need assistance.",
105
- "Bye! Don't hesitate to reach out if you have more questions."
106
- ],
107
- 'thanks': [
108
- "You're welcome! Is there anything else I can help you with?",
109
- "My pleasure! What else would you like to know?",
110
- "Happy to help! Any other questions?",
111
- "Glad I could assist! Feel free to ask more questions."
112
- ],
113
- 'confusion': [
114
- "I'm not sure I understand that. Could you rephrase your question?",
115
- "That's an interesting question. Could you provide more details?",
116
- "I want to help you better. Can you explain that differently?",
117
- "Let me try to understand. Could you ask that in another way?"
118
- ],
119
- 'general_help': [
120
- "I can help you with information about the university, courses, admissions, and more. What would you like to know?",
121
- "I'm here to assist with university-related questions. What can I help you with?",
122
- "I can provide information about courses, admissions, campus locations, and other university topics. What do you need?",
123
- "Feel free to ask me about university services, courses, or any other questions you might have!"
124
- ],
125
- 'encouragement': [
126
- "That's a great question! Let me help you with that.",
127
- "I'm here to help you find the information you need.",
128
- "Don't worry, I'll do my best to assist you.",
129
- "I understand you're looking for information. Let me help!"
130
- ]
131
- }
132
-
133
- # Common question patterns for better understanding
134
- self.question_patterns = {
135
- 'location': ['where', 'location', 'address', 'find', 'located'],
136
- 'time': ['when', 'time', 'schedule', 'hours', 'open', 'close'],
137
- 'contact': ['contact', 'phone', 'email', 'call', 'reach'],
138
- 'admission': ['admission', 'apply', 'application', 'requirements', 'deadline'],
139
- 'course': ['course', 'program', 'degree', 'study', 'major'],
140
- 'help': ['help', 'assist', 'support', 'guide', 'how to']
141
- }
142
-
143
- print("Conversational response system loaded")
144
-
145
-
146
- def detect_conversation_intent(self, user_input):
147
- """Detect the type of conversation the user is having"""
148
- user_input_lower = user_input.lower().strip()
149
-
150
- # Greeting detection
151
- greetings = ['hello', 'hi', 'hey', 'good morning', 'good afternoon', 'good evening', 'greetings']
152
- if any(greeting in user_input_lower for greeting in greetings):
153
- return 'greeting'
154
-
155
- # Farewell detection
156
- farewells = ['bye', 'goodbye', 'see you', 'farewell', 'take care', 'later']
157
- if any(farewell in user_input_lower for farewell in farewells):
158
- return 'farewell'
159
-
160
- # Thanks detection
161
- thanks = ['thank', 'thanks', 'appreciate', 'grateful']
162
- if any(thank in user_input_lower for thank in thanks):
163
- return 'thanks'
164
-
165
- # Help request detection
166
- help_words = ['help', 'assist', 'support', 'guide', 'how', 'what can you do']
167
- if any(help_word in user_input_lower for help_word in help_words):
168
- return 'help_request'
169
-
170
- # Question type detection
171
- for category, keywords in self.question_patterns.items():
172
- if any(keyword in user_input_lower for keyword in keywords):
173
- return f'question_{category}'
174
-
175
- return 'general_question'
176
-
177
- def generate_conversational_response(self, user_input, intent=None):
178
- """Generate a conversational response when no specific answer is found"""
179
- if intent is None:
180
- intent = self.detect_conversation_intent(user_input)
181
-
182
- # Store conversation context
183
- self.conversation_history.append({
184
- 'user': user_input,
185
- 'intent': intent,
186
- 'timestamp': time.time()
187
- })
188
-
189
- # Keep only last 10 interactions
190
- if len(self.conversation_history) > 10:
191
- self.conversation_history = self.conversation_history[-10:]
192
-
193
- # Generate response based on intent
194
- if intent in self.conversational_patterns:
195
- base_response = random.choice(self.conversational_patterns[intent])
196
- else:
197
- base_response = random.choice(self.conversational_patterns['general_help'])
198
-
199
- # Add contextual information based on question type
200
- contextual_info = self.get_contextual_help(user_input, intent)
201
-
202
- if contextual_info:
203
- response = f"{base_response}\n\n{contextual_info}"
204
- else:
205
- response = base_response
206
-
207
- # Add follow-up suggestions
208
- suggestions = self.get_follow_up_suggestions(intent)
209
- if suggestions:
210
- response += f"\n\nYou might also want to ask about:\n{suggestions}"
211
-
212
- # Add note about logging the question for improvement
213
- if intent not in ['greeting', 'farewell', 'thanks']:
214
- response += f"\n\nNote: Your question has been logged to help improve our knowledge base."
215
-
216
- return response
217
-
218
- def get_contextual_help(self, user_input, intent):
219
- """Provide contextual help based on the question type"""
220
- user_input_lower = user_input.lower()
221
-
222
- if intent == 'question_location':
223
- return "I can help you find locations on campus. Try asking about specific places like 'admission office', 'library', 'cafeteria', or 'registrar office'."
224
-
225
- elif intent == 'question_time':
226
- return "I can help with schedules and timing. Ask about office hours, class schedules, or event times."
227
-
228
- elif intent == 'question_contact':
229
- return "I can help you find contact information. Ask about specific departments or offices you need to reach."
230
-
231
- elif intent == 'question_admission':
232
- return "I can help with admission-related questions. Ask about requirements, deadlines, application process, or admission office location."
233
-
234
- elif intent == 'question_course':
235
- return "I can help with course information. Ask about specific programs, requirements, or course recommendations."
236
-
237
- return None
238
-
239
- def get_follow_up_suggestions(self, intent):
240
- """Provide follow-up suggestions based on the conversation context"""
241
- suggestions = {
242
- 'greeting': "• University services\n• Course information\n• Admission requirements\n• Campus locations",
243
- 'question_location': "• Admission office hours\n• Library location\n• Cafeteria hours\n• Parking areas",
244
- 'question_admission': "• Application deadlines\n• Required documents\n• Admission office location\n• Course requirements",
245
- 'question_course': "• Available programs\n• Course recommendations\n• Prerequisites\n• Career paths",
246
- 'help_request': "• Campus locations\n• University services\n• Course information\n• Admission process"
247
- }
248
-
249
- return suggestions.get(intent, "• Campus information\n• University services\n• Course details\n• Admission help")
250
-
251
- def query_database_api(self, user_input):
252
- """Query the database API for a real-time answer"""
253
- try:
254
- faqs_url = f"{self.database_url}/faqs"
255
- print(f"Querying database API: {faqs_url} with question: {user_input[:50]}...")
256
-
257
- response = requests.get(faqs_url, params={'question': user_input}, timeout=10)
258
- print(f"Database API response: Status {response.status_code}")
259
-
260
- if response.status_code == 200:
261
- data = response.json()
262
- print(f"Database API data: {data}")
263
-
264
- if data.get('answer') and data.get('source') != 'not_found':
265
- print("Found answer in database")
266
- return {
267
- 'question': user_input,
268
- 'answer': data.get('answer'),
269
- 'source': 'database'
270
- }
271
- else:
272
- print("No answer found in database")
273
- # Log unanswered question for future improvement
274
- self.log_unanswered_question(user_input)
275
- else:
276
- print(f"Database API error: {response.status_code} - {response.text}")
277
- return None
278
- except requests.exceptions.Timeout:
279
- print("Database API timeout")
280
- return None
281
- except requests.exceptions.ConnectionError:
282
- print("Database API connection error")
283
- return None
284
- except Exception as e:
285
- print(f"Error querying database API: {str(e)}")
286
- return None
287
-
288
- def log_unanswered_question(self, question):
289
- """Log unanswered questions to the database for future reference"""
290
- try:
291
- unanswered_url = f"{self.database_url}/unanswered_questions"
292
-
293
- # Prepare data for unanswered question (matching your database server format)
294
- unanswered_data = {
295
- 'question': question.strip()
296
- }
297
-
298
- print(f"Logging unanswered question: {question[:50]}...")
299
-
300
- # Try to send to database
301
- response = requests.post(unanswered_url, json=unanswered_data, timeout=5)
302
-
303
- if response.status_code == 200:
304
- print("Unanswered question logged successfully")
305
- return True
306
- elif response.status_code == 201:
307
- print("Unanswered question created successfully")
308
- return True
309
- else:
310
- print(f"Failed to log unanswered question: {response.status_code} - {response.text[:100]}")
311
- return False
312
-
313
- except requests.exceptions.Timeout:
314
- print("Timeout logging unanswered question")
315
- return False
316
- except requests.exceptions.ConnectionError:
317
- print("Connection error logging unanswered question")
318
- return False
319
- except Exception as e:
320
- print(f"Error logging unanswered question: {str(e)}")
321
- return False
322
- # Don't raise the exception - this shouldn't break the main flow
323
-
324
- def get_unanswered_questions(self):
325
- """Get list of unanswered questions from the database"""
326
- try:
327
- # Note: Your database server doesn't have a GET endpoint for unanswered_questions
328
- # This would need to be added to your Flask server
329
- print("Note: GET endpoint for unanswered_questions not available in current database server")
330
- return []
331
-
332
- except Exception as e:
333
- print(f"Error getting unanswered questions: {str(e)}")
334
- return []
335
-
336
- def find_best_match(self, user_input, threshold=0.5):
337
- """Find the best matching question using database API only"""
338
- # Only try to get answer from database API
339
- db_result = self.query_database_api(user_input)
340
- if db_result:
341
- return db_result, 1.0 # High confidence for database results
342
-
343
- # No local data to fall back to - return None
344
- return None, 0
345
-
346
- def get_response(self, user_input):
347
- """Get response for user input"""
348
- if not user_input.strip():
349
- return "Please enter a message."
350
-
351
- # First, try to find a specific answer from database
352
- best_match, similarity = self.find_best_match(user_input)
353
-
354
- if best_match and similarity >= 0.5: # Only use specific answers if confidence is high enough
355
- source = best_match.get('source', 'database')
356
- answer = best_match.get('answer', 'No answer found')
357
-
358
- # Add source indicator for database answers
359
- if source == 'database':
360
- answer = f"\n{answer}"
361
-
362
- return {
363
- 'answer': answer,
364
- 'confidence': float(similarity),
365
- 'matched_question': best_match.get('question', ''),
366
- 'status': 'success',
367
- 'source': source
368
- }
369
- else:
370
- # Use conversational AI for all other questions
371
- conversational_response = self.generate_conversational_response(user_input)
372
-
373
- return {
374
- 'answer': conversational_response,
375
- 'confidence': 0.8, # High confidence for conversational responses
376
- 'matched_question': '',
377
- 'status': 'conversational',
378
- 'source': 'conversational_ai'
379
- }
380
-
381
- def get_qa_count(self):
382
- """Get the number of loaded Q&A pairs"""
383
- return len(self.qa_pairs)
384
-
385
- def get_unanswered_stats(self):
386
- """Get statistics about unanswered questions"""
387
- try:
388
- unanswered_questions = self.get_unanswered_questions()
389
- if not unanswered_questions:
390
- return "No unanswered questions found."
391
-
392
- # Count questions by date
393
- from collections import defaultdict
394
- date_counts = defaultdict(int)
395
- for question in unanswered_questions:
396
- if isinstance(question, dict) and 'timestamp' in question:
397
- date = question['timestamp'][:10] # Get just the date part
398
- date_counts[date] += 1
399
-
400
- stats = f"Unanswered Questions Statistics:\n"
401
- stats += f"Total unanswered questions: {len(unanswered_questions)}\n\n"
402
- stats += "Questions by date:\n"
403
-
404
- for date in sorted(date_counts.keys(), reverse=True):
405
- stats += f" {date}: {date_counts[date]} questions\n"
406
-
407
- return stats
408
-
409
- except Exception as e:
410
- return f"Error getting unanswered statistics: {str(e)}"
411
-
412
- def get_course_recommendations(self, stanine, gwa, strand, hobbies):
413
- """Get course recommendations using the recommender system"""
414
- try:
415
- # Validate inputs
416
- stanine = int(stanine) if isinstance(stanine, str) else stanine
417
- gwa = float(gwa) if isinstance(gwa, str) else gwa
418
-
419
- if not (1 <= stanine <= 9):
420
- return "Stanine score must be between 1 and 9"
421
- if not (75 <= gwa <= 100):
422
- return "GWA must be between 75 and 100"
423
- if not strand:
424
- return "Please select a strand"
425
- if not hobbies or not str(hobbies).strip():
426
- return "Please enter your hobbies/interests"
427
-
428
- if self.recommender is None:
429
- return "Course recommendation system is not available at the moment. Please try again later."
430
-
431
- # Get recommendations
432
- recommendations = self.recommender.recommend_courses(
433
- stanine=stanine,
434
- gwa=gwa,
435
- strand=strand,
436
- hobbies=str(hobbies)
437
- )
438
-
439
- if not recommendations:
440
- return "No recommendations available at the moment."
441
-
442
- # Format response (without confidence scores)
443
- response = f"## Course Recommendations for You\n\n"
444
- response += f"**Profile:** Stanine {stanine}, GWA {gwa}, {strand} Strand\n"
445
- response += f"**Interests:** {hobbies}\n\n"
446
-
447
- for i, rec in enumerate(recommendations, 1):
448
- response += f"### {i}. {rec['code']} - {rec['name']}\n\n"
449
-
450
- return response
451
-
452
- except Exception as e:
453
- return f"Error getting recommendations: {str(e)}"