husseinelsaadi commited on
Commit
7502aed
·
1 Parent(s): 7814b36

chatbot updated

Browse files
Files changed (1) hide show
  1. chatbot/chatbot.py +112 -66
chatbot/chatbot.py CHANGED
@@ -1,10 +1,11 @@
1
  # codingo/chatbot/chatbot.py
2
- """Interactive chatbot using Flan-T5 for dynamic responses"""
3
 
4
  import os
5
  import shutil
6
  from typing import List
7
  import torch
 
8
 
9
  os.environ.setdefault("HF_HOME", "/tmp/huggingface")
10
  os.environ.setdefault("TRANSFORMERS_CACHE", "/tmp/huggingface/transformers")
@@ -14,40 +15,45 @@ _model = None
14
  _tokenizer = None
15
  _chatbot_embedder = None
16
  _chatbot_collection = None
 
17
 
18
  _current_dir = os.path.dirname(os.path.abspath(__file__))
19
  _knowledge_base_path = os.path.join(_current_dir, "chatbot.txt")
20
  _chroma_db_dir = "/tmp/chroma_db"
21
 
22
- # Using Flan-T5 - it's small, fast, and great for Q&A
23
- MODEL_NAME = "google/flan-t5-small"
24
 
25
  def _init_model():
26
  global _model, _tokenizer
27
  if _model is not None and _tokenizer is not None:
28
  return
29
 
30
- print("Loading Flan-T5 model...")
31
- from transformers import T5ForConditionalGeneration, T5Tokenizer
32
 
33
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
34
  print(f"Using device: {device}")
35
 
36
- tokenizer = T5Tokenizer.from_pretrained(MODEL_NAME)
37
- model = T5ForConditionalGeneration.from_pretrained(
 
 
 
 
38
  MODEL_NAME,
39
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
40
- low_cpu_mem_usage=True
 
41
  )
42
- model = model.to(device)
43
  model.eval()
44
 
45
  _model = model
46
  _tokenizer = tokenizer
47
- print("Model loaded successfully!")
48
 
49
  def _init_vector_store():
50
- global _chatbot_embedder, _chatbot_collection
51
  if _chatbot_embedder is not None and _chatbot_collection is not None:
52
  return
53
 
@@ -57,10 +63,6 @@ def _init_vector_store():
57
  import chromadb
58
  from chromadb.config import Settings
59
 
60
- # Clean and create directory
61
- shutil.rmtree(_chroma_db_dir, ignore_errors=True)
62
- os.makedirs(_chroma_db_dir, exist_ok=True)
63
-
64
  # Load knowledge base
65
  try:
66
  with open(_knowledge_base_path, encoding="utf-8") as f:
@@ -73,13 +75,13 @@ def _init_vector_store():
73
  # Split into chunks
74
  splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=50)
75
  docs = [doc.strip() for doc in splitter.split_text(raw_text) if doc.strip()]
76
- print(f"Created {len(docs)} document chunks")
77
 
78
  # Create embeddings
79
  embedder = SentenceTransformer("all-MiniLM-L6-v2")
80
  embeddings = embedder.encode(docs, show_progress_bar=False)
81
 
82
- # Create ChromaDB collection
83
  client = chromadb.Client(Settings(anonymized_telemetry=False, is_persistent=False))
84
 
85
  try:
@@ -93,12 +95,31 @@ def _init_vector_store():
93
 
94
  _chatbot_embedder = embedder
95
  _chatbot_collection = collection
96
- print("Vector store ready!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  def get_chatbot_response(query: str) -> str:
99
  try:
100
  if not query or not query.strip():
101
- return "Hi! I'm LUNA AI. Ask me anything about Codingo!"
102
 
103
  print(f"\nProcessing: '{query}'")
104
 
@@ -120,85 +141,110 @@ def get_chatbot_response(query: str) -> str:
120
  retrieved_docs = results.get("documents", [[]])[0] if results else []
121
  print(f"Found {len(retrieved_docs)} relevant chunks")
122
 
123
- # Combine the most relevant information
124
- context = " ".join(retrieved_docs[:2]) if retrieved_docs else "Codingo is an AI recruitment platform."
 
 
 
125
 
126
- # Create a prompt for Flan-T5
127
- prompt = f"""Answer the question based on the context about Codingo.
 
 
 
 
128
 
129
- Context: {context}
130
 
131
- Question: {query}
132
 
133
- Answer:"""
134
 
135
- # Tokenize
136
  inputs = _tokenizer(
137
- prompt,
138
- max_length=512,
139
- truncation=True,
140
- return_tensors="pt"
141
- ).to(_model.device)
 
 
142
 
143
  # Generate response
144
  with torch.no_grad():
145
  outputs = _model.generate(
146
  **inputs,
147
- max_new_tokens=150,
148
- num_beams=4,
149
  temperature=0.7,
150
  do_sample=True,
151
  top_p=0.9,
152
- repetition_penalty=1.2
 
 
 
153
  )
154
 
155
  # Decode response
156
- response = _tokenizer.decode(outputs[0], skip_special_tokens=True)
157
- print(f"Generated: '{response}'")
158
-
159
- # Make sure we have a good response
160
- if not response or len(response) < 5:
161
- # Fallback: try a simpler prompt
162
- simple_prompt = f"Question about Codingo: {query}\nAnswer:"
163
- inputs = _tokenizer(simple_prompt, max_length=256, truncation=True, return_tensors="pt").to(_model.device)
164
-
165
- with torch.no_grad():
166
- outputs = _model.generate(**inputs, max_new_tokens=100, temperature=0.8)
167
-
168
- response = _tokenizer.decode(outputs[0], skip_special_tokens=True)
169
 
170
- # Clean up the response
171
- response = response.strip()
172
 
173
- # If still too short, provide a helpful response
174
- if len(response) < 10:
175
- if "hello" in query.lower() or "hi" in query.lower():
176
- return "Hello! I'm LUNA AI, your Codingo assistant. I can help you with questions about our AI recruitment platform, job matching, CV tips, and more!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  else:
178
- return f"I can help you with that! Based on what I know about Codingo: {retrieved_docs[0][:200] if retrieved_docs else 'Codingo is an AI-powered recruitment platform that helps match candidates with jobs.'}"
 
 
 
 
179
 
 
180
  return response
181
 
182
  except Exception as e:
183
  print(f"Error: {e}")
184
  import traceback
185
  traceback.print_exc()
186
- return "I'm having a technical issue. Please try asking your question again!"
187
 
188
- # Test function
189
  if __name__ == "__main__":
190
- # Test the chatbot
191
  test_queries = [
 
192
  "What is Codingo?",
193
  "How does it work?",
194
- "What makes Codingo special?",
195
- "How can I improve my profile?",
196
- "Is it free?"
 
197
  ]
198
 
199
- print("Testing chatbot...")
200
  for q in test_queries:
201
  response = get_chatbot_response(q)
202
- print(f"\nQ: {q}")
203
- print(f"A: {response}")
204
- print("-" * 50)
 
1
  # codingo/chatbot/chatbot.py
2
+ """Interactive chatbot using Microsoft Phi-2 for efficient, quality responses"""
3
 
4
  import os
5
  import shutil
6
  from typing import List
7
  import torch
8
+ import re
9
 
10
  os.environ.setdefault("HF_HOME", "/tmp/huggingface")
11
  os.environ.setdefault("TRANSFORMERS_CACHE", "/tmp/huggingface/transformers")
 
15
  _tokenizer = None
16
  _chatbot_embedder = None
17
  _chatbot_collection = None
18
+ _knowledge_chunks = []
19
 
20
  _current_dir = os.path.dirname(os.path.abspath(__file__))
21
  _knowledge_base_path = os.path.join(_current_dir, "chatbot.txt")
22
  _chroma_db_dir = "/tmp/chroma_db"
23
 
24
+ # Phi-2: 2.7B params, great performance, fits easily on T4
25
+ MODEL_NAME = "microsoft/phi-2"
26
 
27
  def _init_model():
28
  global _model, _tokenizer
29
  if _model is not None and _tokenizer is not None:
30
  return
31
 
32
+ print("Loading Phi-2 model...")
33
+ from transformers import AutoModelForCausalLM, AutoTokenizer
34
 
35
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
36
  print(f"Using device: {device}")
37
 
38
+ # Load tokenizer
39
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
40
+ tokenizer.pad_token = tokenizer.eos_token
41
+
42
+ # Load model
43
+ model = AutoModelForCausalLM.from_pretrained(
44
  MODEL_NAME,
45
+ torch_dtype=torch.float16,
46
+ device_map="auto",
47
+ trust_remote_code=True
48
  )
 
49
  model.eval()
50
 
51
  _model = model
52
  _tokenizer = tokenizer
53
+ print("Phi-2 loaded successfully!")
54
 
55
  def _init_vector_store():
56
+ global _chatbot_embedder, _chatbot_collection, _knowledge_chunks
57
  if _chatbot_embedder is not None and _chatbot_collection is not None:
58
  return
59
 
 
63
  import chromadb
64
  from chromadb.config import Settings
65
 
 
 
 
 
66
  # Load knowledge base
67
  try:
68
  with open(_knowledge_base_path, encoding="utf-8") as f:
 
75
  # Split into chunks
76
  splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=50)
77
  docs = [doc.strip() for doc in splitter.split_text(raw_text) if doc.strip()]
78
+ _knowledge_chunks = docs # Store for reference
79
 
80
  # Create embeddings
81
  embedder = SentenceTransformer("all-MiniLM-L6-v2")
82
  embeddings = embedder.encode(docs, show_progress_bar=False)
83
 
84
+ # Create ChromaDB collection (in-memory)
85
  client = chromadb.Client(Settings(anonymized_telemetry=False, is_persistent=False))
86
 
87
  try:
 
95
 
96
  _chatbot_embedder = embedder
97
  _chatbot_collection = collection
98
+ print(f"Vector store ready with {len(docs)} chunks!")
99
+
100
+ def extract_faq_answer(query: str, docs: List[str]) -> str:
101
+ """Try to find direct FAQ answers"""
102
+ query_lower = query.lower()
103
+
104
+ for doc in docs:
105
+ # Look for Q&A patterns
106
+ if "Q:" in doc and "A:" in doc:
107
+ lines = doc.split('\n')
108
+ for i, line in enumerate(lines):
109
+ if line.strip().startswith('Q:'):
110
+ question = line[2:].strip().lower()
111
+ # Check similarity
112
+ if any(word in question for word in query_lower.split() if len(word) > 3):
113
+ # Find the answer
114
+ for j in range(i+1, min(i+5, len(lines))):
115
+ if lines[j].strip().startswith('A:'):
116
+ return lines[j][2:].strip()
117
+ return None
118
 
119
  def get_chatbot_response(query: str) -> str:
120
  try:
121
  if not query or not query.strip():
122
+ return "Hello! I'm LUNA AI, your Codingo assistant. I can help you with questions about our AI recruitment platform, job matching, CV tips, and more!"
123
 
124
  print(f"\nProcessing: '{query}'")
125
 
 
141
  retrieved_docs = results.get("documents", [[]])[0] if results else []
142
  print(f"Found {len(retrieved_docs)} relevant chunks")
143
 
144
+ # Try to find FAQ answer first
145
+ faq_answer = extract_faq_answer(query, retrieved_docs)
146
+ if faq_answer:
147
+ print("Found FAQ match!")
148
+ return faq_answer
149
 
150
+ # Build context from retrieved docs
151
+ context = "\n".join(retrieved_docs[:2]) if retrieved_docs else ""
152
+
153
+ # Create an instruction-following prompt for Phi-2
154
+ prompt = f"""Instruct: You are LUNA AI, a helpful assistant for Codingo recruitment platform.
155
+ Use the following information to answer the user's question:
156
 
157
+ {context}
158
 
159
+ User Question: {query}
160
 
161
+ Output: Based on the information provided, """
162
 
163
+ # Tokenize with appropriate length
164
  inputs = _tokenizer(
165
+ prompt,
166
+ return_tensors="pt",
167
+ truncation=True,
168
+ max_length=800,
169
+ padding=True
170
+ )
171
+ inputs = {k: v.to(_model.device) for k, v in inputs.items()}
172
 
173
  # Generate response
174
  with torch.no_grad():
175
  outputs = _model.generate(
176
  **inputs,
177
+ max_new_tokens=200,
 
178
  temperature=0.7,
179
  do_sample=True,
180
  top_p=0.9,
181
+ repetition_penalty=1.15,
182
+ pad_token_id=_tokenizer.pad_token_id,
183
+ eos_token_id=_tokenizer.eos_token_id,
184
+ early_stopping=True
185
  )
186
 
187
  # Decode response
188
+ full_response = _tokenizer.decode(outputs[0], skip_special_tokens=True)
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
+ # Extract only the generated part
191
+ response = full_response.split("Output:")[-1].strip()
192
 
193
+ # Clean up common artifacts
194
+ response = response.replace("Based on the information provided,", "").strip()
195
+
196
+ # Remove the original prompt if it appears
197
+ if prompt in response:
198
+ response = response.replace(prompt, "").strip()
199
+
200
+ # Ensure quality response
201
+ if len(response) < 20 or response.lower() == query.lower():
202
+ # Generate a contextual response
203
+ query_lower = query.lower()
204
+
205
+ if "hello" in query_lower or "hi" in query_lower:
206
+ return "Hello! Welcome to Codingo! I'm LUNA AI, here to help you navigate our AI-powered recruitment platform. You can ask me about creating profiles, job matching, improving your CV, or any of our features!"
207
+
208
+ elif "what" in query_lower and "codingo" in query_lower:
209
+ return "Codingo is an innovative AI-driven recruitment platform that transforms how companies hire and how candidates find jobs. We use advanced algorithms to match skills with opportunities, provide instant CV feedback, and streamline the entire hiring process."
210
+
211
+ elif "how" in query_lower and ("work" in query_lower or "use" in query_lower):
212
+ return "Here's how Codingo works: As a candidate, you create a profile, upload your resume, and add portfolio links. Our AI then analyzes your skills and matches you with suitable jobs. You'll receive personalized recommendations and CV improvement tips. For employers, we offer smart candidate filtering and automated screening insights!"
213
+
214
+ elif "feature" in query_lower or "special" in query_lower:
215
+ return "What makes Codingo special is our combination of AI-powered job matching, real-time CV analysis, bias-aware algorithms, and focus on tech professionals. We support various roles from developers to designers, making the hiring process smarter, faster, and fairer for everyone."
216
+
217
  else:
218
+ # Use context to create a response
219
+ if retrieved_docs:
220
+ return f"Let me help you with that! {retrieved_docs[0][:250]}..."
221
+ else:
222
+ return "I'd be happy to help you learn more about Codingo! Could you please ask about specific features like job matching, CV tips, supported job types, or how our platform works?"
223
 
224
+ print(f"Generated: {response[:100]}...")
225
  return response
226
 
227
  except Exception as e:
228
  print(f"Error: {e}")
229
  import traceback
230
  traceback.print_exc()
231
+ return "I apologize for the technical issue. Please feel free to ask me about Codingo's features, job matching process, or how to get started!"
232
 
233
+ # Test the chatbot
234
  if __name__ == "__main__":
235
+ print("Testing Codingo Chatbot...")
236
  test_queries = [
237
+ "Hello!",
238
  "What is Codingo?",
239
  "How does it work?",
240
+ "What job types do you support?",
241
+ "How can I improve my match score?",
242
+ "Is Codingo free?",
243
+ "Tell me about CV tips"
244
  ]
245
 
 
246
  for q in test_queries:
247
  response = get_chatbot_response(q)
248
+ print(f"\nUser: {q}")
249
+ print(f"LUNA: {response}")
250
+ print("-" * 80)