Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
from helper import download_hugging_face_embeddings
|
2 |
-
from
|
|
|
3 |
from langchain_community.llms import Ollama
|
4 |
from langchain.chains import create_retrieval_chain
|
5 |
from langchain.chains.combine_documents import create_stuff_documents_chain
|
@@ -7,36 +8,139 @@ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
|
7 |
from langchain.chains import create_history_aware_retriever
|
8 |
from langchain.memory import ChatMessageHistory
|
9 |
from langchain_core.runnables.history import RunnableWithMessageHistory
|
|
|
|
|
10 |
from dotenv import load_dotenv
|
11 |
from prompt import system_prompt, retriever_prompt
|
12 |
import os
|
13 |
import logging
|
14 |
import gradio as gr
|
|
|
|
|
15 |
import uuid
|
|
|
16 |
|
17 |
-
logging.
|
|
|
18 |
|
|
|
19 |
base = {}
|
20 |
last_messages = 4
|
|
|
21 |
|
22 |
load_dotenv()
|
23 |
-
|
24 |
-
PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY')
|
25 |
BASE_URL = os.environ.get('BASE_URL')
|
26 |
-
|
27 |
-
os.environ['PINECONE_API_KEY'] = PINECONE_API_KEY
|
28 |
os.environ['BASE_URL'] = BASE_URL
|
29 |
|
30 |
-
|
|
|
|
|
|
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
retriever = docsearch.as_retriever(search_type='similarity', search_kwargs={'k':2})
|
38 |
|
39 |
-
llm = Ollama(model='llama3.2', base_url=BASE_URL
|
40 |
|
41 |
prompt = ChatPromptTemplate.from_messages(
|
42 |
[
|
@@ -70,14 +174,14 @@ rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chai
|
|
70 |
def get_session_history(session_id):
|
71 |
if session_id not in base:
|
72 |
base[session_id] = ChatMessageHistory()
|
73 |
-
|
74 |
stored_msg = base[session_id].messages
|
75 |
-
if len(stored_msg) >=
|
76 |
base[session_id].clear()
|
77 |
-
|
78 |
for msg in stored_msg[-last_messages:]:
|
79 |
base[session_id].add_message(msg)
|
80 |
-
|
81 |
return base[session_id]
|
82 |
|
83 |
chat_with_msg_history = RunnableWithMessageHistory(
|
@@ -87,25 +191,116 @@ chat_with_msg_history = RunnableWithMessageHistory(
|
|
87 |
history_messages_key='chat_history'
|
88 |
)
|
89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
with gr.Blocks() as demo:
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
|
|
|
|
|
|
|
|
|
|
|
96 |
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
100 |
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
|
103 |
-
response = chat_with_msg_history.invoke(
|
104 |
-
{'input': message},
|
105 |
-
{'configurable': {'session_id': session_id}},
|
106 |
-
)
|
107 |
|
108 |
-
chat_history.append((message, response['answer']))
|
109 |
-
return '', chat_history, session_id
|
110 |
-
msg.submit(get_response, [msg, chatbot, state], [msg, chatbot, state])
|
111 |
demo.launch(share=True)
|
|
|
1 |
from helper import download_hugging_face_embeddings
|
2 |
+
from url import md_files_url
|
3 |
+
from get_data import extract_repo_details, fetch_md_file_via_api, data_loader, chunk_text
|
4 |
from langchain_community.llms import Ollama
|
5 |
from langchain.chains import create_retrieval_chain
|
6 |
from langchain.chains.combine_documents import create_stuff_documents_chain
|
|
|
8 |
from langchain.chains import create_history_aware_retriever
|
9 |
from langchain.memory import ChatMessageHistory
|
10 |
from langchain_core.runnables.history import RunnableWithMessageHistory
|
11 |
+
from langchain.vectorstores import FAISS
|
12 |
+
from langchain.schema import Document
|
13 |
from dotenv import load_dotenv
|
14 |
from prompt import system_prompt, retriever_prompt
|
15 |
import os
|
16 |
import logging
|
17 |
import gradio as gr
|
18 |
+
import sqlite3
|
19 |
+
import bcrypt
|
20 |
import uuid
|
21 |
+
from datetime import datetime
|
22 |
|
23 |
+
logging.basicConfig(level=logging.INFO)
|
24 |
+
logger = logging.getLogger(__name__)
|
25 |
|
26 |
+
DB_PATH = "chatbot.db"
|
27 |
base = {}
|
28 |
last_messages = 4
|
29 |
+
documents = []
|
30 |
|
31 |
load_dotenv()
|
32 |
+
AUTH_TOKEN_KEY = os.environ.get('AUTH_TOKEN_KEY')
|
|
|
33 |
BASE_URL = os.environ.get('BASE_URL')
|
34 |
+
os.environ['AUTH_TOKEN_KEY'] = AUTH_TOKEN_KEY
|
|
|
35 |
os.environ['BASE_URL'] = BASE_URL
|
36 |
|
37 |
+
# ---- Database part ----- #
|
38 |
+
# Database Connection
|
39 |
+
def connect_db():
|
40 |
+
return sqlite3.connect(DB_PATH)
|
41 |
|
42 |
+
def create_tables():
|
43 |
+
with connect_db() as conn:
|
44 |
+
cursor = conn.cursor()
|
45 |
+
cursor.execute('''
|
46 |
+
CREATE TABLE IF NOT EXISTS users (
|
47 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
48 |
+
username TEXT UNIQUE NOT NULL,
|
49 |
+
password_hash TEXT NOT NULL
|
50 |
+
)''')
|
51 |
+
cursor.execute('''
|
52 |
+
CREATE TABLE IF NOT EXISTS chat_history (
|
53 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
54 |
+
user_id INTEGER NOT NULL,
|
55 |
+
message TEXT NOT NULL,
|
56 |
+
response TEXT NOT NULL,
|
57 |
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
58 |
+
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
59 |
+
)''')
|
60 |
+
conn.commit()
|
61 |
+
|
62 |
+
create_tables()
|
63 |
+
logger.info("Database tables created successfully!")
|
64 |
+
|
65 |
+
# Secure Password Hashing
|
66 |
+
def hash_password(password):
|
67 |
+
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
|
68 |
+
|
69 |
+
def check_password(password, hashed):
|
70 |
+
return bcrypt.checkpw(password.encode(), hashed.encode())
|
71 |
+
|
72 |
+
# Authenticate User
|
73 |
+
def authenticate(username, password):
|
74 |
+
with connect_db() as conn:
|
75 |
+
cursor = conn.cursor()
|
76 |
+
cursor.execute("SELECT id, password_hash FROM users WHERE username = ?", (username,))
|
77 |
+
user = cursor.fetchone()
|
78 |
+
|
79 |
+
if user and check_password(password, user[1]):
|
80 |
+
session_id = uuid.uuid4().hex # Unique session ID
|
81 |
+
return session_id, f"Welcome {username}!", user[0] # user_id
|
82 |
+
return None, "Invalid username or password.", None
|
83 |
+
|
84 |
+
# Signup Function
|
85 |
+
def signup(username, password):
|
86 |
+
try:
|
87 |
+
hashed_pw = hash_password(password)
|
88 |
+
with connect_db() as conn:
|
89 |
+
cursor = conn.cursor()
|
90 |
+
cursor.execute("INSERT INTO users (username, password_hash) VALUES (?, ?)", (username, hashed_pw))
|
91 |
+
conn.commit()
|
92 |
+
return f"User {username} registered successfully! You can now log in."
|
93 |
+
except sqlite3.IntegrityError:
|
94 |
+
return "Username already exists. Try another one."
|
95 |
+
|
96 |
+
# Store Chat in Database
|
97 |
+
def save_chat(user_id, message, response):
|
98 |
+
with connect_db() as conn:
|
99 |
+
cursor = conn.cursor()
|
100 |
+
cursor.execute("INSERT INTO chat_history (user_id, message, response, created_at) VALUES (?, ?, ?, ?)",
|
101 |
+
(user_id, message, response, datetime.now()))
|
102 |
+
conn.commit()
|
103 |
+
|
104 |
+
# Retrieve Chat History (User-specific)
|
105 |
+
def get_chat_history(user_id):
|
106 |
+
with connect_db() as conn:
|
107 |
+
cursor = conn.cursor()
|
108 |
+
cursor.execute("SELECT message, response FROM chat_history WHERE user_id = ? ORDER BY created_at", (user_id,))
|
109 |
+
chat_history = cursor.fetchall()
|
110 |
+
return [(msg, resp) for msg, resp in chat_history]
|
111 |
+
# --------------------------------- #
|
112 |
+
|
113 |
+
#Option 2-load directly from urls
|
114 |
+
for url in md_files_url:
|
115 |
+
try:
|
116 |
+
repo_owner, repo_name, file_path = extract_repo_details(url)
|
117 |
+
content = fetch_md_file_via_api(repo_owner, repo_name, file_path, AUTH_TOKEN_KEY)
|
118 |
+
if content:
|
119 |
+
document = Document(page_content=content, metadata={"source": file_path})
|
120 |
+
documents.append(document)
|
121 |
+
except ValueError as ve:
|
122 |
+
logging.error(f"Error processing URL {url}: {ve}")
|
123 |
+
print(f"Fetched {len(documents)} documents.")
|
124 |
+
|
125 |
+
text_chunk = chunk_text(documents)
|
126 |
+
try:
|
127 |
+
faiss_index = FAISS.from_documents(text_chunk, download_hugging_face_embeddings())
|
128 |
+
faiss_index.save_local('faiss_index')
|
129 |
+
except Exception as e:
|
130 |
+
logging.error(f"Error creating or saving FAISS index: {e}")
|
131 |
+
|
132 |
+
try:
|
133 |
+
docsearch = FAISS.load_local(
|
134 |
+
'faiss_index',
|
135 |
+
download_hugging_face_embeddings(),
|
136 |
+
allow_dangerous_deserialization=True
|
137 |
+
)
|
138 |
+
except Exception as e:
|
139 |
+
logging.error(f"Error loading FAISS index: {e}")
|
140 |
|
141 |
retriever = docsearch.as_retriever(search_type='similarity', search_kwargs={'k':2})
|
142 |
|
143 |
+
llm = Ollama(model='llama3.2', base_url=BASE_URL)
|
144 |
|
145 |
prompt = ChatPromptTemplate.from_messages(
|
146 |
[
|
|
|
174 |
def get_session_history(session_id):
|
175 |
if session_id not in base:
|
176 |
base[session_id] = ChatMessageHistory()
|
177 |
+
|
178 |
stored_msg = base[session_id].messages
|
179 |
+
if len(stored_msg) >= last_messages:
|
180 |
base[session_id].clear()
|
181 |
+
|
182 |
for msg in stored_msg[-last_messages:]:
|
183 |
base[session_id].add_message(msg)
|
184 |
+
|
185 |
return base[session_id]
|
186 |
|
187 |
chat_with_msg_history = RunnableWithMessageHistory(
|
|
|
191 |
history_messages_key='chat_history'
|
192 |
)
|
193 |
|
194 |
+
def get_response(message, chat_history, session_id, user_id):
|
195 |
+
if not session_id or not user_id:
|
196 |
+
return "Session expired. Please log in again.", []
|
197 |
+
|
198 |
+
chat_history = get_chat_history(user_id) # Load user's previous chat history
|
199 |
+
|
200 |
+
response = chat_with_msg_history.invoke(
|
201 |
+
{'input': message},
|
202 |
+
{'configurable': {'session_id': session_id}},
|
203 |
+
)
|
204 |
+
|
205 |
+
# 🔹 Log the response for debugging
|
206 |
+
print("LangChain Response:", response)
|
207 |
+
|
208 |
+
# 🔹 Ensure response contains 'answer' (adjust this if needed)
|
209 |
+
if isinstance(response, dict) and 'answer' in response:
|
210 |
+
chatbot_reply = response['answer']
|
211 |
+
else:
|
212 |
+
chatbot_reply = "I'm sorry, I couldn't process that request."
|
213 |
+
|
214 |
+
save_chat(user_id, message, chatbot_reply)
|
215 |
+
|
216 |
+
chat_history.append((message, chatbot_reply)) # Append instead of overwriting
|
217 |
+
return "", chat_history
|
218 |
+
|
219 |
+
# Logout Function
|
220 |
+
def logout(session_id):
|
221 |
+
if session_id in base:
|
222 |
+
del base[session_id] # Clear session history
|
223 |
+
return None, "Logged out successfully.", None
|
224 |
+
|
225 |
+
## ... [Keep all previous code up to the Gradio UI section] ...
|
226 |
+
# Gradio UI
|
227 |
with gr.Blocks() as demo:
|
228 |
+
gr.Markdown("## HumblebeeAI Customer Support Chatbot")
|
229 |
+
|
230 |
+
with gr.Row():
|
231 |
+
username = gr.Textbox(label="Username", interactive=True)
|
232 |
+
password = gr.Textbox(label="Password", type="password", interactive=True)
|
233 |
+
login_button = gr.Button("Login", interactive=False) # Initially disabled
|
234 |
+
signup_button = gr.Button("Signup", interactive=False) # Initially disabled
|
235 |
+
|
236 |
+
login_status = gr.Textbox(label="Status", interactive=False)
|
237 |
+
session_state = gr.State(None)
|
238 |
+
user_id_state = gr.State(None)
|
239 |
|
240 |
+
with gr.Column(visible=False) as chat_interface:
|
241 |
+
chatbot = gr.Chatbot()
|
242 |
+
msg = gr.Textbox(label="Message", placeholder="Ask me anything...")
|
243 |
+
send_button = gr.Button("Send")
|
244 |
+
logout_button = gr.Button("Logout")
|
245 |
|
246 |
+
# 🔹 Enable buttons only when both username & password are filled
|
247 |
+
def enable_buttons(username, password):
|
248 |
+
is_valid = bool(username.strip()) and bool(password.strip())
|
249 |
+
return gr.update(interactive=is_valid), gr.update(interactive=is_valid)
|
250 |
|
251 |
+
username.change(enable_buttons, [username, password], [login_button, signup_button])
|
252 |
+
password.change(enable_buttons, [username, password], [login_button, signup_button])
|
253 |
+
|
254 |
+
# 🔹 Login Logic (Clears Username & Password)
|
255 |
+
def login_user(username, password):
|
256 |
+
session_id, message, user_id = authenticate(username, password)
|
257 |
+
if session_id:
|
258 |
+
return session_id, user_id, message, "", "", get_chat_history(user_id), gr.update(visible=True)
|
259 |
+
return None, None, message, username, password, [], gr.update(visible=False)
|
260 |
+
|
261 |
+
login_button.click(
|
262 |
+
login_user,
|
263 |
+
[username, password],
|
264 |
+
[session_state, user_id_state, login_status, username, password, chatbot, chat_interface]
|
265 |
+
)
|
266 |
+
|
267 |
+
# 🔹 Signup Logic (Clears Username & Password)
|
268 |
+
def signup_user(username, password):
|
269 |
+
message = signup(username, password)
|
270 |
+
return message, "", ""
|
271 |
+
|
272 |
+
signup_button.click(
|
273 |
+
signup_user,
|
274 |
+
[username, password],
|
275 |
+
[login_status, username, password]
|
276 |
+
)
|
277 |
+
|
278 |
+
# 🔹 Sending Messages
|
279 |
+
send_button.click(
|
280 |
+
get_response,
|
281 |
+
[msg, chatbot, session_state, user_id_state],
|
282 |
+
[msg, chatbot]
|
283 |
+
)
|
284 |
+
|
285 |
+
msg.submit(
|
286 |
+
get_response,
|
287 |
+
[msg, chatbot, session_state, user_id_state],
|
288 |
+
[msg, chatbot]
|
289 |
+
)
|
290 |
+
|
291 |
+
# 🔹 Logout Logic (Clears Chat and Resets UI)
|
292 |
+
def logout_user():
|
293 |
+
return None, "", "", [], gr.update(visible=False)
|
294 |
+
|
295 |
+
# 🔹 Logout Function (Clears Status, Session, and Chat History)
|
296 |
+
def logout_user():
|
297 |
+
return None, "", "", "", [], gr.update(visible=False)
|
298 |
+
|
299 |
+
logout_button.click(
|
300 |
+
logout_user,
|
301 |
+
None,
|
302 |
+
[session_state, username, password, login_status, chatbot, chat_interface]
|
303 |
+
)
|
304 |
|
|
|
|
|
|
|
|
|
305 |
|
|
|
|
|
|
|
306 |
demo.launch(share=True)
|