husseinelsaadi commited on
Commit
25c6eb9
·
1 Parent(s): ab83281
Files changed (1) hide show
  1. chatbot/chatbot.py +135 -214
chatbot/chatbot.py CHANGED
@@ -1,5 +1,5 @@
1
  # codingo/chatbot/chatbot.py
2
- """Chatbot module for Codingo with enhanced debugging"""
3
 
4
  import os
5
  import shutil
@@ -9,7 +9,7 @@ import traceback
9
  os.environ.setdefault("HF_HOME", "/tmp/huggingface")
10
  os.environ.setdefault("TRANSFORMERS_CACHE", "/tmp/huggingface/transformers")
11
  os.environ.setdefault("HUGGINGFACE_HUB_CACHE", "/tmp/huggingface/hub")
12
- os.environ["CUDA_LAUNCH_BLOCKING"] = "1" # Enable synchronous CUDA errors
13
 
14
  _hf_model = None
15
  _hf_tokenizer = None
@@ -20,15 +20,10 @@ _current_dir = os.path.dirname(os.path.abspath(__file__))
20
  _knowledge_base_path = os.path.join(_current_dir, "chatbot.txt")
21
  _chroma_db_dir = "/tmp/chroma_db"
22
 
23
- # Try a smaller, more reliable model for debugging
24
  DEFAULT_MODEL_NAME = "microsoft/DialoGPT-small"
25
 
26
  def _init_hf_model() -> None:
27
- from transformers import (
28
- AutoModelForCausalLM,
29
- AutoModelForSeq2SeqLM,
30
- AutoTokenizer,
31
- )
32
  import torch
33
 
34
  global _hf_model, _hf_tokenizer
@@ -42,51 +37,22 @@ def _init_hf_model() -> None:
42
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
43
  print(f"Using device: {device}")
44
 
45
- try:
46
- # Initialize tokenizer
47
- tokenizer = AutoTokenizer.from_pretrained(model_name)
48
- print("Tokenizer loaded successfully")
49
-
50
- # Try loading the model
51
- try:
52
- model = AutoModelForCausalLM.from_pretrained(
53
- model_name,
54
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
55
- low_cpu_mem_usage=True
56
- )
57
- model_type = "causal"
58
- print("Loaded as causal model")
59
- except Exception as e:
60
- print(f"Failed to load as causal model: {e}")
61
- model = AutoModelForSeq2SeqLM.from_pretrained(
62
- model_name,
63
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
64
- low_cpu_mem_usage=True
65
- )
66
- model_type = "seq2seq"
67
- print("Loaded as seq2seq model")
68
-
69
- # Move model to device
70
- model = model.to(device)
71
- model.eval()
72
- print("Model moved to device and set to eval mode")
73
-
74
- # Configure padding token
75
- if tokenizer.pad_token is None:
76
- tokenizer.pad_token = tokenizer.eos_token
77
- print(f"Set pad_token to: {tokenizer.pad_token}")
78
-
79
- # Store model type
80
- model.model_type = model_type
81
-
82
- _hf_model = model
83
- _hf_tokenizer = tokenizer
84
- print("Model initialization complete")
85
-
86
- except Exception as e:
87
- print(f"Error during model initialization: {e}")
88
- traceback.print_exc()
89
- raise
90
 
91
  def _init_vector_store() -> None:
92
  global _chatbot_embedder, _chatbot_collection
@@ -95,70 +61,49 @@ def _init_vector_store() -> None:
95
 
96
  print("Initializing vector store...")
97
 
98
- try:
99
- from langchain.text_splitter import RecursiveCharacterTextSplitter
100
- from sentence_transformers import SentenceTransformer
101
- import chromadb
102
- from chromadb.config import Settings
103
-
104
- # Clean up old database
105
- shutil.rmtree(_chroma_db_dir, ignore_errors=True)
106
- os.makedirs(_chroma_db_dir, exist_ok=True)
107
-
108
- # Load knowledge base
109
- try:
110
- with open(_knowledge_base_path, encoding="utf-8") as f:
111
- raw_text = f.read()
112
- print(f"Loaded knowledge base with {len(raw_text)} characters")
113
- except FileNotFoundError:
114
- print("Knowledge base file not found, using default text")
115
- raw_text = (
116
- "Codingo is an AI-powered recruitment platform designed to "
117
- "streamline job applications, candidate screening, and hiring. "
118
- "We make hiring smarter, faster, and fairer through automation "
119
- "and intelligent recommendations."
120
- )
121
-
122
- # Split text
123
- splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=100)
124
- docs = [doc.strip() for doc in splitter.split_text(raw_text) if doc.strip()]
125
- print(f"Split into {len(docs)} documents")
126
-
127
- # Initialize embedder
128
- print("Loading sentence transformer...")
129
- embedder = SentenceTransformer("all-MiniLM-L6-v2")
130
- print("Encoding documents...")
131
- embeddings = embedder.encode(docs, show_progress_bar=False, batch_size=32)
132
- print(f"Created {len(embeddings)} embeddings")
133
-
134
- # Initialize ChromaDB (use in-memory for HF Spaces)
135
- print("Initializing ChromaDB...")
136
- client = chromadb.Client(Settings(
137
- anonymized_telemetry=False,
138
- is_persistent=False, # Changed to False for HF Spaces
139
- ))
140
-
141
- # Create collection
142
- try:
143
- client.delete_collection("chatbot")
144
- except:
145
- pass
146
-
147
- collection = client.create_collection("chatbot")
148
-
149
- # Add documents
150
- ids = [f"doc_{i}" for i in range(len(docs))]
151
- collection.add(documents=docs, embeddings=embeddings.tolist(), ids=ids)
152
- print(f"Added {len(docs)} documents to collection")
153
 
154
- _chatbot_embedder = embedder
155
- _chatbot_collection = collection
156
- print("Vector store initialization complete")
157
-
158
- except Exception as e:
159
- print(f"Error during vector store initialization: {e}")
160
- traceback.print_exc()
161
- raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
 
163
  def get_chatbot_response(query: str) -> str:
164
  try:
@@ -167,24 +112,12 @@ def get_chatbot_response(query: str) -> str:
167
  if not query or not query.strip():
168
  return "Please type a question about the Codingo platform."
169
 
170
- # Clear GPU cache
171
  import torch
172
  if torch.cuda.is_available():
173
  torch.cuda.empty_cache()
174
- print("Cleared GPU cache")
175
 
176
- # Initialize components
177
- try:
178
- _init_vector_store()
179
- except Exception as e:
180
- print(f"Vector store initialization failed: {e}")
181
- return "I'm having trouble accessing my knowledge base. Please try again later."
182
-
183
- try:
184
- _init_hf_model()
185
- except Exception as e:
186
- print(f"Model initialization failed: {e}")
187
- return "I'm having trouble loading my language model. Please try again later."
188
 
189
  embedder = _chatbot_embedder
190
  collection = _chatbot_collection
@@ -192,102 +125,90 @@ def get_chatbot_response(query: str) -> str:
192
  tokenizer = _hf_tokenizer
193
 
194
  # Get relevant documents
195
- print("Creating query embedding...")
196
  query_embedding = embedder.encode([query])[0]
197
-
198
- print("Searching for relevant documents...")
199
  results = collection.query(query_embeddings=[query_embedding.tolist()], n_results=3)
200
  retrieved_docs = results.get("documents", [[]])[0] if results else []
201
- context = "\n".join(retrieved_docs[:3]) if retrieved_docs else ""
202
- print(f"Retrieved {len(retrieved_docs)} documents")
203
 
204
- # Prepare prompt
205
- if hasattr(model, 'model_type') and model.model_type == "seq2seq":
206
- prompt = f"Context: {context}\n\nUser: {query}\nAssistant:"
207
- else:
208
- # For DialoGPT or other causal models
209
- prompt = f"Context: {context}\n\nUser: {query}\nLUNA AI:"
210
 
211
- print(f"Prompt length: {len(prompt)} characters")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
 
213
  # Tokenize
214
- print("Tokenizing input...")
215
- try:
216
- inputs = tokenizer(
217
- prompt,
218
- return_tensors="pt",
219
- truncation=True,
220
- max_length=400, # Reduced for safety
221
- padding=True,
222
- return_attention_mask=True
223
- )
224
- print(f"Input shape: {inputs['input_ids'].shape}")
225
- except Exception as e:
226
- print(f"Tokenization error: {e}")
227
- traceback.print_exc()
228
- return "I had trouble processing your input. Please try a shorter question."
229
 
230
- # Move to device
231
- inputs = {k: v.to(model.device) for k, v in inputs.items()}
232
-
233
- # Generate response
234
- print("Generating response...")
235
  with torch.no_grad():
236
- try:
237
- output_ids = model.generate(
238
- input_ids=inputs['input_ids'],
239
- attention_mask=inputs['attention_mask'],
240
- max_new_tokens=100, # Reduced for safety
241
- min_length=10,
242
- num_beams=2, # Reduced for memory
243
- do_sample=True,
244
- temperature=0.8,
245
- pad_token_id=tokenizer.pad_token_id,
246
- eos_token_id=tokenizer.eos_token_id,
247
- early_stopping=True,
248
- )
249
- print(f"Output shape: {output_ids.shape}")
250
- except Exception as e:
251
- print(f"Generation error: {e}")
252
- traceback.print_exc()
253
-
254
- # Try a simpler generation
255
- try:
256
- print("Trying simpler generation...")
257
- output_ids = model.generate(
258
- input_ids=inputs['input_ids'],
259
- max_new_tokens=50,
260
- pad_token_id=tokenizer.pad_token_id,
261
- )
262
- except Exception as e2:
263
- print(f"Simple generation also failed: {e2}")
264
- return "I'm having trouble generating a response. Please try again."
265
-
266
- # Decode response
267
- print("Decoding response...")
268
- response = tokenizer.decode(output_ids[0], skip_special_tokens=True)
269
- print(f"Raw response: {response[:100]}...")
270
-
271
- # Clean up response
272
- if "LUNA AI:" in response:
273
- response = response.split("LUNA AI:")[-1].strip()
274
- elif "Assistant:" in response:
275
- response = response.split("Assistant:")[-1].strip()
276
-
277
- # Remove the input if it's in the response
278
- if query in response:
279
- response = response.replace(query, "").strip()
280
-
281
- # Final cleanup
282
- response = response.strip()
283
 
284
- if not response or len(response) < 5:
285
- response = "I'm here to help you with questions about the Codingo platform. What would you like to know?"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
- print(f"Final response: {response}")
288
  return response
289
 
290
  except Exception as e:
291
- print(f"Unexpected error in get_chatbot_response: {e}")
292
  traceback.print_exc()
293
- return "I apologize, but I encountered an unexpected error. Please try again with a different question."
 
1
  # codingo/chatbot/chatbot.py
2
+ """Chatbot module for Codingo - Optimized for conversational AI"""
3
 
4
  import os
5
  import shutil
 
9
  os.environ.setdefault("HF_HOME", "/tmp/huggingface")
10
  os.environ.setdefault("TRANSFORMERS_CACHE", "/tmp/huggingface/transformers")
11
  os.environ.setdefault("HUGGINGFACE_HUB_CACHE", "/tmp/huggingface/hub")
12
+ os.environ["CUDA_LAUNCH_BLOCKING"] = "1"
13
 
14
  _hf_model = None
15
  _hf_tokenizer = None
 
20
  _knowledge_base_path = os.path.join(_current_dir, "chatbot.txt")
21
  _chroma_db_dir = "/tmp/chroma_db"
22
 
 
23
  DEFAULT_MODEL_NAME = "microsoft/DialoGPT-small"
24
 
25
  def _init_hf_model() -> None:
26
+ from transformers import AutoModelForCausalLM, AutoTokenizer
 
 
 
 
27
  import torch
28
 
29
  global _hf_model, _hf_tokenizer
 
37
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
38
  print(f"Using device: {device}")
39
 
40
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
41
+ model = AutoModelForCausalLM.from_pretrained(
42
+ model_name,
43
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
44
+ low_cpu_mem_usage=True
45
+ )
46
+
47
+ model = model.to(device)
48
+ model.eval()
49
+
50
+ if tokenizer.pad_token is None:
51
+ tokenizer.pad_token = tokenizer.eos_token
52
+
53
+ _hf_model = model
54
+ _hf_tokenizer = tokenizer
55
+ print("Model initialization complete")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
  def _init_vector_store() -> None:
58
  global _chatbot_embedder, _chatbot_collection
 
61
 
62
  print("Initializing vector store...")
63
 
64
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
65
+ from sentence_transformers import SentenceTransformer
66
+ import chromadb
67
+ from chromadb.config import Settings
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ shutil.rmtree(_chroma_db_dir, ignore_errors=True)
70
+ os.makedirs(_chroma_db_dir, exist_ok=True)
71
+
72
+ try:
73
+ with open(_knowledge_base_path, encoding="utf-8") as f:
74
+ raw_text = f.read()
75
+ print(f"Loaded knowledge base with {len(raw_text)} characters")
76
+ except FileNotFoundError:
77
+ print("Knowledge base file not found, using default text")
78
+ raw_text = (
79
+ "Codingo is an AI-powered recruitment platform designed to "
80
+ "streamline job applications, candidate screening, and hiring."
81
+ )
82
+
83
+ splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=100)
84
+ docs = [doc.strip() for doc in splitter.split_text(raw_text) if doc.strip()]
85
+ print(f"Split into {len(docs)} documents")
86
+
87
+ embedder = SentenceTransformer("all-MiniLM-L6-v2")
88
+ embeddings = embedder.encode(docs, show_progress_bar=False, batch_size=32)
89
+
90
+ client = chromadb.Client(Settings(
91
+ anonymized_telemetry=False,
92
+ is_persistent=False, # In-memory for HF Spaces
93
+ ))
94
+
95
+ try:
96
+ client.delete_collection("chatbot")
97
+ except:
98
+ pass
99
+
100
+ collection = client.create_collection("chatbot")
101
+ ids = [f"doc_{i}" for i in range(len(docs))]
102
+ collection.add(documents=docs, embeddings=embeddings.tolist(), ids=ids)
103
+
104
+ _chatbot_embedder = embedder
105
+ _chatbot_collection = collection
106
+ print("Vector store initialization complete")
107
 
108
  def get_chatbot_response(query: str) -> str:
109
  try:
 
112
  if not query or not query.strip():
113
  return "Please type a question about the Codingo platform."
114
 
 
115
  import torch
116
  if torch.cuda.is_available():
117
  torch.cuda.empty_cache()
 
118
 
119
+ _init_vector_store()
120
+ _init_hf_model()
 
 
 
 
 
 
 
 
 
 
121
 
122
  embedder = _chatbot_embedder
123
  collection = _chatbot_collection
 
125
  tokenizer = _hf_tokenizer
126
 
127
  # Get relevant documents
 
128
  query_embedding = embedder.encode([query])[0]
 
 
129
  results = collection.query(query_embeddings=[query_embedding.tolist()], n_results=3)
130
  retrieved_docs = results.get("documents", [[]])[0] if results else []
 
 
131
 
132
+ print(f"Retrieved {len(retrieved_docs)} documents")
 
 
 
 
 
133
 
134
+ # First, try to find direct answers in the retrieved documents
135
+ query_lower = query.lower()
136
+
137
+ # Check for FAQ matches
138
+ for doc in retrieved_docs:
139
+ if "Q:" in doc and "A:" in doc:
140
+ lines = doc.split('\n')
141
+ for i, line in enumerate(lines):
142
+ if line.strip().startswith('Q:'):
143
+ question = line[2:].strip().lower()
144
+ # Check for keyword overlap
145
+ query_words = set(query_lower.split())
146
+ question_words = set(question.split())
147
+ overlap = len(query_words & question_words)
148
+ if overlap >= 2 or any(word in question for word in query_words if len(word) > 4):
149
+ # Found matching question
150
+ for j in range(i+1, len(lines)):
151
+ if lines[j].strip().startswith('A:'):
152
+ answer = lines[j][2:].strip()
153
+ print(f"Found FAQ match: {answer}")
154
+ return answer
155
+ elif lines[j].strip().startswith('Q:'):
156
+ break
157
+
158
+ # If no FAQ match, create a conversational response using the context
159
+ context_summary = ". ".join([doc[:150] for doc in retrieved_docs[:2]])
160
+
161
+ # Build conversation for DialoGPT
162
+ conversation = f"Tell me about Codingo. {context_summary} User asks: {query}"
163
 
164
  # Tokenize
165
+ inputs = tokenizer.encode(conversation, return_tensors="pt", truncation=True, max_length=200)
166
+ inputs = inputs.to(model.device)
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
+ # Generate
 
 
 
 
169
  with torch.no_grad():
170
+ output_ids = model.generate(
171
+ inputs,
172
+ max_length=inputs.shape[1] + 100,
173
+ num_beams=3,
174
+ temperature=0.8,
175
+ pad_token_id=tokenizer.eos_token_id,
176
+ eos_token_id=tokenizer.eos_token_id,
177
+ do_sample=True,
178
+ top_p=0.9,
179
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
+ # Decode
182
+ full_response = tokenizer.decode(output_ids[0], skip_special_tokens=True)
183
+
184
+ # Extract only the generated part
185
+ response = full_response[len(conversation):].strip()
186
+
187
+ # Clean up
188
+ if not response or len(response) < 10:
189
+ # Fallback: create response from context
190
+ if "how" in query_lower:
191
+ if "work" in query_lower:
192
+ return "Codingo works by using AI to match candidates with suitable job postings. Candidates create profiles, upload resumes, and our AI analyzes their skills to recommend the best job matches."
193
+ elif "improve" in query_lower:
194
+ return "To improve your match score on Codingo, update your profile with accurate skills, add relevant keywords from job descriptions, and include links to your portfolio projects."
195
+ elif "what" in query_lower:
196
+ if "codingo" in query_lower:
197
+ return "Codingo is an AI-powered recruitment platform that streamlines job applications and hiring. We help candidates find suitable jobs and employers find the right talent through intelligent matching."
198
+ elif "special" in query_lower or "different" in query_lower:
199
+ return "What makes Codingo special is our AI that understands both technical skills and language, real-time CV feedback, bias-aware algorithms, and specialized focus on tech professionals."
200
+ elif "can" in query_lower or "does" in query_lower:
201
+ if "chatbot" in query_lower:
202
+ return "I can help you with questions about the Codingo platform, including how to use it, improve your profile, understand our features, and get tips for job applications."
203
+ elif "free" in query_lower or "cost" in query_lower:
204
+ return "Profile creation and job applications are free on Codingo. Premium features may be offered for advanced analytics and additional services."
205
+
206
+ # Generic but relevant response
207
+ return "I'd be happy to help you with Codingo! You can ask me about creating profiles, job matching, CV tips, supported job types, or any other features of our recruitment platform."
208
 
 
209
  return response
210
 
211
  except Exception as e:
212
+ print(f"Error in get_chatbot_response: {e}")
213
  traceback.print_exc()
214
+ return "I apologize for the technical issue. Please try asking about Codingo's features, job matching, or how to improve your profile."