ManTea commited on
Commit
9213889
·
1 Parent(s): f75062a

fix connect mongodb

Browse files
Files changed (1) hide show
  1. NLP_model/chatbot.py +155 -108
NLP_model/chatbot.py CHANGED
@@ -1,19 +1,32 @@
1
  import os
2
  import google.generativeai as genai
3
  from langchain_google_genai import ChatGoogleGenerativeAI
4
- # from langchain_community.embeddings import HuggingFaceEmbeddings
5
- # from langchain_community.vectorstores import FAISS
6
  from langchain.chains import RetrievalQA, ConversationalRetrievalChain
7
  from langchain_google_genai import ChatGoogleGenerativeAI
8
  from langchain.prompts import PromptTemplate
9
- # from langchain_ollama import OllamaLLM
10
  from pinecone import Pinecone, ServerlessSpec
11
  from langchain_pinecone import PineconeVectorStore
12
  from dotenv import load_dotenv
13
  import threading
14
  from datetime import datetime
 
15
  from langchain.schema import HumanMessage, AIMessage
16
  from langchain_google_genai import GoogleGenerativeAIEmbeddings
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  # Load environment variables
18
  load_dotenv()
19
 
@@ -29,19 +42,64 @@ os.environ["PINECONE_API_KEY"] = pinecone_api_key
29
 
30
  genai.configure(api_key=google_api_key)
31
 
32
- #lấy model chatbot
33
- model = ChatGoogleGenerativeAI(model="gemini-1.5-flash-8b-latest",
34
- temperature=0.8)
35
- # model = OllamaLLM(model="llama2")
36
- # print("Llama2 đã được tải thành công!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
- #lấy model embedding
39
  embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
 
40
 
41
-
42
- # Biến lưu history cho từng user (dạng chuỗi)
43
- user_histories = {}
44
- history_lock = threading.Lock()
 
 
45
 
46
  # Create a prompt template with conversation history
47
  prompt = PromptTemplate(
@@ -53,77 +111,34 @@ Return Format:
53
  Respond in friendly, natural, and concise English like a real tour guide.
54
 
55
  Warning:
56
- Let's support users like a real tour guide, not a bot. The information in context is your own knowledge.
57
- Your knowledge is provided in the Context. All of information in Context is about Da Nang, Vietnam.
58
  You just care about current time that user mention when user ask about Solana event.
59
  If you do not have enough information to answer user's question, reply with "I don't know. I don't have information about that".
60
 
61
- Context:
62
  {context}
63
 
64
  Conversation History:
65
  {chat_history}
66
 
67
- User chat:
68
  {question}
69
 
70
- Your chat:
71
  """,
72
  input_variables = ["context", "question", "chat_history"],
73
  )
74
 
75
  def get_history(user_id):
76
- """Get conversation history for a specific user"""
77
- with history_lock:
78
- return user_histories.get(user_id, "")
79
-
80
- def update_history(user_id, new_entry):
81
- """Update conversation history for a user.
82
- new_entry should be a string containing the new conversation information, e.g.:
83
- "User: {question}\nBot: {answer}\n"
84
- """
85
- with history_lock:
86
- current_history = user_histories.get(user_id, "")
87
- # Store only the last 30 interactions by keeping the 60 most recent lines
88
- # (assuming 2 lines per interaction: 1 for user, 1 for bot)
89
- history_lines = current_history.split('\n')
90
- if len(history_lines) > 60:
91
- history_lines = history_lines[-60:]
92
- current_history = '\n'.join(history_lines)
93
-
94
- updated_history = current_history + new_entry + "\n"
95
- user_histories[user_id] = updated_history
96
 
97
- def string_to_message_history(history_str):
98
- """Convert string-based history to LangChain message history format"""
99
- if not history_str.strip():
100
- return []
101
-
102
- messages = []
103
- lines = history_str.strip().split('\n')
104
- i = 0
105
-
106
- while i < len(lines):
107
- line = lines[i].strip()
108
- if line.startswith("User:"):
109
- user_message = line[5:].strip() # Get the user message without "User:"
110
- messages.append(HumanMessage(content=user_message))
111
-
112
- # Look for a Bot response (should be the next line)
113
- if i + 1 < len(lines) and lines[i + 1].strip().startswith("Bot:"):
114
- bot_response = lines[i + 1][4:].strip() # Get bot response without "Bot:"
115
- messages.append(AIMessage(content=bot_response))
116
- i += 2 # Skip the bot line too
117
- else:
118
- i += 1
119
- else:
120
- i += 1 # Skip any unexpected format lines
121
-
122
- return messages
123
 
124
  def get_chain():
125
- """Get the retrieval chain with Pinecone vector store"""
126
  try:
 
127
  pc = Pinecone(
128
  api_key=os.environ["PINECONE_API_KEY"]
129
  )
@@ -135,64 +150,96 @@ def get_chain():
135
  text_key="text"
136
  )
137
 
138
- retrieve = vectorstore.as_retriever(search_kwargs={"k": 3})
139
-
140
- return retrieve
141
  except Exception as e:
142
- print(f"Error getting vector store: {e}")
143
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
  def chat(request, user_id="default_user"):
146
  """Process a chat request from a specific user"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  try:
148
- # Get retrieval chain
149
  retriever = get_chain()
150
  if not retriever:
151
  return "Error: Could not initialize retriever"
152
 
153
- # Get current conversation history as string
154
- conversation_history_str = get_history(user_id)
155
-
156
- # Convert string history to LangChain message format
157
- message_history = string_to_message_history(conversation_history_str)
158
-
159
- # Get current time
160
  current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
161
 
162
- # Add timestamp to question
163
- question_with_time = f"{request}\n(Current time: {current_time})"
164
- # print("User question:", question_with_time)
165
-
166
- # Create a ConversationalRetrievalChain
167
- # Get relevant documents from retriever
168
- retrieved_docs = retriever.get_relevant_documents(question_with_time)
169
- print("Retrieved documents page content:", [doc.page_content for doc in retrieved_docs])
170
 
171
- conversation_chain = ConversationalRetrievalChain.from_llm(
172
- llm=model,
173
- retriever=retriever,
174
- combine_docs_chain_kwargs={"prompt": prompt}
 
175
  )
 
176
 
177
- # Call the chain with question and converted message history
178
- response = conversation_chain({"question": question_with_time, "chat_history": message_history})
179
- answer = str(response['answer'])
180
 
181
- # Update conversation history string
182
- new_entry = f"User: {question_with_time}\nBot: {answer}"
183
- update_history(user_id, new_entry)
184
- print(get_history(user_id))
 
 
185
 
186
- print(answer)
187
  return answer
188
  except Exception as e:
189
- print(f"Error in chat: {e}")
190
- return f"I encountered an error: {str(e)}"
191
-
192
- def clear_memory(user_id="default_user"):
193
- """Clear the conversation history for a specific user"""
194
- with history_lock:
195
- if user_id in user_histories:
196
- del user_histories[user_id]
197
- return f"Conversation history cleared for user {user_id}"
198
- return f"No conversation history found for user {user_id}"
 
1
  import os
2
  import google.generativeai as genai
3
  from langchain_google_genai import ChatGoogleGenerativeAI
4
+ from langchain_community.embeddings import HuggingFaceEmbeddings
5
+ from langchain_community.vectorstores import FAISS
6
  from langchain.chains import RetrievalQA, ConversationalRetrievalChain
7
  from langchain_google_genai import ChatGoogleGenerativeAI
8
  from langchain.prompts import PromptTemplate
 
9
  from pinecone import Pinecone, ServerlessSpec
10
  from langchain_pinecone import PineconeVectorStore
11
  from dotenv import load_dotenv
12
  import threading
13
  from datetime import datetime
14
+ import time
15
  from langchain.schema import HumanMessage, AIMessage
16
  from langchain_google_genai import GoogleGenerativeAIEmbeddings
17
+ import functools
18
+ import hashlib
19
+ import logging
20
+ import random
21
+ from mongodb import get_chat_history
22
+
23
+ # Configure logging
24
+ logging.basicConfig(
25
+ level=logging.INFO,
26
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
27
+ )
28
+ logger = logging.getLogger(__name__)
29
+
30
  # Load environment variables
31
  load_dotenv()
32
 
 
42
 
43
  genai.configure(api_key=google_api_key)
44
 
45
+ # Lấy model chatbot
46
+ try:
47
+ generation_config = {
48
+ "temperature": 0.9,
49
+ "top_p": 1,
50
+ "top_k": 1,
51
+ "max_output_tokens": 2048,
52
+ }
53
+
54
+ safety_settings = [
55
+ {
56
+ "category": "HARM_CATEGORY_HARASSMENT",
57
+ "threshold": "BLOCK_MEDIUM_AND_ABOVE"
58
+ },
59
+ {
60
+ "category": "HARM_CATEGORY_HATE_SPEECH",
61
+ "threshold": "BLOCK_MEDIUM_AND_ABOVE"
62
+ },
63
+ {
64
+ "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
65
+ "threshold": "BLOCK_MEDIUM_AND_ABOVE"
66
+ },
67
+ {
68
+ "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
69
+ "threshold": "BLOCK_MEDIUM_AND_ABOVE"
70
+ },
71
+ ]
72
+
73
+ model = genai.GenerativeModel(
74
+ model_name='models/gemini-2.0-flash',
75
+ generation_config=generation_config,
76
+ safety_settings=safety_settings
77
+ )
78
+
79
+ # Test the model with a simple prompt
80
+ test_response = model.generate_content("Hello")
81
+ logger.debug(f"Test response: {test_response.text if hasattr(test_response, 'text') else 'No text attribute'}")
82
+
83
+ except Exception as e:
84
+ logger.error(f"Error initializing or testing Gemini model: {str(e)}")
85
+ raise
86
+
87
+ # Lấy model embedding
88
+ # Print available embedding models
89
+ # available_models = GoogleGenerativeAIEmbeddings.list_models()
90
+ # embedding_models = [model.name for model in available_models if "embedding" in model.name.lower()]
91
+ # logger.info(f"Available embedding models: {embedding_models}")
92
 
93
+ # Use the embedding model
94
  embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
95
+ # embeddings = genai.GenerativeModel(model_name="models/embedding-004")
96
 
97
+ # Cache for responses
98
+ response_cache = {}
99
+ cache_lock = threading.Lock()
100
+ # Maximum cache size và thời gian sống (30 phút)
101
+ MAX_CACHE_SIZE = 100
102
+ CACHE_TTL = 1800 # 30 phút tính bằng giây
103
 
104
  # Create a prompt template with conversation history
105
  prompt = PromptTemplate(
 
111
  Respond in friendly, natural, and concise English like a real tour guide.
112
 
113
  Warning:
114
+ Let's support users like a real tour guide, not a bot. The information in core knowledge is your own knowledge.
115
+ Your knowledge is provided in the Core Knowledge. All of information in Core Knowledge is about Da Nang, Vietnam.
116
  You just care about current time that user mention when user ask about Solana event.
117
  If you do not have enough information to answer user's question, reply with "I don't know. I don't have information about that".
118
 
119
+ Core knowledge:
120
  {context}
121
 
122
  Conversation History:
123
  {chat_history}
124
 
125
+ User question:
126
  {question}
127
 
128
+ Your answer:
129
  """,
130
  input_variables = ["context", "question", "chat_history"],
131
  )
132
 
133
  def get_history(user_id):
134
+ """Get conversation history for a specific user from MongoDB"""
135
+ return get_chat_history(user_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  def get_chain():
139
+ """Get the retrieval chain with Pinecone vector store (singleton pattern)"""
140
  try:
141
+ start_time = time.time()
142
  pc = Pinecone(
143
  api_key=os.environ["PINECONE_API_KEY"]
144
  )
 
150
  text_key="text"
151
  )
152
 
153
+ _retriever_instance = vectorstore.as_retriever(search_kwargs={"k": 4})
154
+ logger.info(f"Pinecone retriever initialized in {time.time() - start_time:.2f} seconds")
155
+ return _retriever_instance
156
  except Exception as e:
157
+ logger.error(f"Error getting vector store from Pinecone: {e}")
158
+ # Fallback to a local vector store or return None
159
+ try:
160
+ # Try to load a local FAISS index if it exists
161
+ start_time = time.time()
162
+ vectorstore = FAISS.load_local("faiss_index", embeddings)
163
+ _retriever_instance = vectorstore.as_retriever(search_kwargs={"k": 3})
164
+ logger.info(f"FAISS retriever initialized in {time.time() - start_time:.2f} seconds")
165
+ return _retriever_instance
166
+ except Exception as faiss_error:
167
+ logger.error(f"Error getting FAISS vector store: {faiss_error}")
168
+ return None
169
+
170
+ def clean_cache():
171
+ """Clean expired cache entries"""
172
+ with cache_lock:
173
+ current_time = time.time()
174
+ expired_keys = [k for k, v in response_cache.items() if current_time - v['timestamp'] > CACHE_TTL]
175
+
176
+ for key in expired_keys:
177
+ del response_cache[key]
178
+
179
+ # Nếu cache vẫn quá lớn, xóa các mục cũ nhất
180
+ if len(response_cache) > MAX_CACHE_SIZE:
181
+ # Sắp xếp theo thời gian và giữ lại MAX_CACHE_SIZE mục mới nhất
182
+ sorted_items = sorted(response_cache.items(), key=lambda x: x[1]['timestamp'])
183
+ items_to_remove = sorted_items[:len(sorted_items) - MAX_CACHE_SIZE]
184
+
185
+ for key, _ in items_to_remove:
186
+ del response_cache[key]
187
+
188
+ def generate_cache_key(request, user_id):
189
+ """Generate a unique cache key from the request and user_id"""
190
+ # Tạo một chuỗi kết hợp để hash
191
+ combined = f"{request.strip().lower()}:{user_id}"
192
+ # Tạo MD5 hash
193
+ return hashlib.md5(combined.encode()).hexdigest()
194
 
195
  def chat(request, user_id="default_user"):
196
  """Process a chat request from a specific user"""
197
+ start_time = time.time()
198
+
199
+ # Định kỳ xóa các mục cache hết hạn
200
+ if random.random() < 0.1:
201
+ clean_cache()
202
+
203
+ cache_key = generate_cache_key(request, user_id)
204
+
205
+ with cache_lock:
206
+ if cache_key in response_cache:
207
+ cache_data = response_cache[cache_key]
208
+ if time.time() - cache_data['timestamp'] <= CACHE_TTL:
209
+ logger.info(f"Cache hit for user {user_id}, request: '{request[:30]}...'")
210
+ cache_data['timestamp'] = time.time()
211
+ return cache_data['response']
212
  try:
 
213
  retriever = get_chain()
214
  if not retriever:
215
  return "Error: Could not initialize retriever"
216
 
 
 
 
 
 
 
 
217
  current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
218
 
219
+ # Sử dụng invoke thay vì get_relevant_documents
220
+ retrieved_docs = retriever.invoke(request)
221
+ context = "\n".join([doc.page_content for doc in retrieved_docs])
 
 
 
 
 
222
 
223
+ # Sử dụng generate_content thay vì invoke cho model Gemini
224
+ prompt_text = prompt.format(
225
+ context=context,
226
+ question=request,
227
+ chat_history=get_history(user_id)
228
  )
229
+ print(prompt_text)
230
 
231
+ response = model.generate_content(prompt_text)
232
+ answer = response.text # Sử dụng .text thay vì .content
 
233
 
234
+ # Lưu vào cache
235
+ with cache_lock:
236
+ response_cache[cache_key] = {
237
+ 'response': answer,
238
+ 'timestamp': time.time()
239
+ }
240
 
241
+ logger.info(f"Total processing time: {time.time() - start_time:.2f} seconds")
242
  return answer
243
  except Exception as e:
244
+ logger.error(f"Error in chat: {e}")
245
+ return f"I don't know how to answer that right now. Let me forward this to the admin team."