husseinelsaadi commited on
Commit
7814b36
·
1 Parent(s): 25c6eb9
Files changed (1) hide show
  1. chatbot/chatbot.py +115 -125
chatbot/chatbot.py CHANGED
@@ -1,18 +1,17 @@
1
  # codingo/chatbot/chatbot.py
2
- """Chatbot module for Codingo - Optimized for conversational AI"""
3
 
4
  import os
5
  import shutil
6
  from typing import List
7
- import traceback
8
 
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
16
  _chatbot_embedder = None
17
  _chatbot_collection = None
18
 
@@ -20,77 +19,68 @@ _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
- 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
30
- if _hf_model is not None and _hf_tokenizer is not None:
31
  return
32
-
33
- print("Initializing HF model...")
34
- model_name = os.getenv("HF_CHATBOT_MODEL", DEFAULT_MODEL_NAME)
35
- print(f"Loading model: {model_name}")
36
 
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
59
  if _chatbot_embedder is not None and _chatbot_collection is not None:
60
  return
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")
@@ -103,112 +93,112 @@ def _init_vector_store() -> None:
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:
110
- print(f"\n=== Processing query: {query} ===")
111
-
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
124
- model = _hf_model
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."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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")
11
  os.environ.setdefault("HUGGINGFACE_HUB_CACHE", "/tmp/huggingface/hub")
 
12
 
13
+ _model = None
14
+ _tokenizer = None
15
  _chatbot_embedder = None
16
  _chatbot_collection = None
17
 
 
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
 
54
  print("Initializing vector store...")
 
55
  from langchain.text_splitter import RecursiveCharacterTextSplitter
56
  from sentence_transformers import SentenceTransformer
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:
67
  raw_text = f.read()
68
+ print(f"Loaded knowledge base: {len(raw_text)} characters")
69
  except FileNotFoundError:
70
+ print("Knowledge base not found!")
71
+ raw_text = "Codingo is an AI recruitment platform."
 
 
 
72
 
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:
86
  client.delete_collection("chatbot")
 
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
+
105
+ # Clear GPU cache
106
  if torch.cuda.is_available():
107
  torch.cuda.empty_cache()
 
 
 
108
 
109
+ # Initialize
110
+ _init_vector_store()
111
+ _init_model()
 
 
 
 
 
 
 
 
112
 
113
+ # Search for relevant context
114
+ query_embedding = _chatbot_embedder.encode([query])[0]
115
+ results = _chatbot_collection.query(
116
+ query_embeddings=[query_embedding.tolist()],
117
+ n_results=3
118
+ )
119
 
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)