bazinga03 commited on
Commit
95c84d0
·
verified ·
1 Parent(s): 443e0b0
Files changed (1) hide show
  1. app.py +288 -60
app.py CHANGED
@@ -1,64 +1,292 @@
 
 
 
 
 
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
3
-
4
- """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
6
- """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
-
9
-
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
19
-
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
25
-
26
- messages.append({"role": "user", "content": message})
27
-
28
- response = ""
29
-
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
38
-
39
- response += token
40
- yield response
41
-
42
-
43
- """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- demo = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
- ],
60
- )
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  if __name__ == "__main__":
64
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import threading
4
+ import shutil
5
+ from pathlib import Path
6
  import gradio as gr
7
+ from langchain.document_loaders import PyPDFLoader
8
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
9
+ from langchain.embeddings import HuggingFaceEmbeddings
10
+ from langchain.vectorstores import FAISS
11
+ from langchain.chains import ConversationalRetrievalChain, LLMChain
12
+ from langchain.memory import ConversationBufferMemory
13
+ from langchain_groq import ChatGroq
14
+ from langchain.prompts import PromptTemplate
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ # Initialize environment variables
17
+ GROQ_API_KEY = "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"
18
+ TEMP_DIR = "temp_uploads"
19
+ VECTOR_DIR = "vector_stores"
20
+
21
+ # Create temporary directories if they don't exist
22
+ os.makedirs(TEMP_DIR, exist_ok=True)
23
+ os.makedirs(VECTOR_DIR, exist_ok=True)
24
+
25
+ class DocumentChat:
26
+ def __init__(self):
27
+ self.chain = None
28
+ self.db = None
29
+ self.current_vector_store = None
30
+ self.cleanup_timer = None
31
+
32
+ # Initialize embedding model
33
+ self.embedding_model = HuggingFaceEmbeddings(
34
+ model_name="sentence-transformers/all-mpnet-base-v2"
35
+ )
36
+
37
+ # Initialize LLM
38
+ self.llm = ChatGroq(
39
+ api_key=GROQ_API_KEY,
40
+ model_name="mixtral-8x7b-32768",
41
+ temperature=0.7,
42
+ )
43
+
44
+ # Initialize memory with output_key
45
+ self.memory = ConversationBufferMemory(
46
+ memory_key="chat_history",
47
+ output_key="answer",
48
+ return_messages=True
49
+ )
50
+
51
+ # Add generic question template
52
+ # Add generic question template
53
+ self.generic_template = PromptTemplate(
54
+ input_variables=["question"],
55
+ template="""You are a helpful AI assistant that can:
56
+ 1. Read and understand PDF documents that users upload
57
+ 2. Answer questions about the contents of uploaded documents
58
+ 3. Maintain context through conversation
59
+ 4. Process documents and remember their contents for the duration of the chat
60
+ 5. Provide accurate and relevant information from the documents
61
+
62
+ If the user asks: {question}
63
+
64
+ Provide a clear and helpful response about your capabilities in a structured way.
65
+ If the question is about the document and no document is uploaded yet, remind them to upload a document first.
66
+
67
+ Remember to be friendly and professional in your response."""
68
+ )
69
+
70
+ # Create generic chain
71
+ self.generic_chain = LLMChain(
72
+ llm=self.llm,
73
+ prompt=self.generic_template
74
+ )
75
+
76
+ def cleanup_files(self, vector_store_path, pdf_path):
77
+ """Clean up files after 10 minutes"""
78
+ time.sleep(600) # Wait for 10 minutes
79
+ try:
80
+ # Remove vector store
81
+ if os.path.exists(vector_store_path):
82
+ shutil.rmtree(vector_store_path)
83
+ # Remove PDF file
84
+ if os.path.exists(pdf_path):
85
+ os.remove(pdf_path)
86
+
87
+ # Reset the class variables
88
+ self.chain = None
89
+ self.db = None
90
+ self.current_vector_store = None
91
+
92
+ print(f"Cleanup completed for: {pdf_path}")
93
+ except Exception as e:
94
+ print(f"Cleanup error: {str(e)}")
95
+
96
+ def process_file(self, file_path):
97
+ try:
98
+ if file_path is None:
99
+ return "Please upload a file."
100
+
101
+ # Generate a unique filename for the vector store
102
+ timestamp = int(time.time())
103
+ vector_store_path = os.path.join(VECTOR_DIR, f"store_{timestamp}")
104
+
105
+ # Load and process document
106
+ loader = PyPDFLoader(file_path)
107
+ documents = loader.load()
108
+
109
+ # Split text
110
+ text_splitter = RecursiveCharacterTextSplitter(
111
+ chunk_size=1000,
112
+ chunk_overlap=200
113
+ )
114
+ docs = text_splitter.split_documents(documents)
115
+
116
+ # Create vector store
117
+ self.db = FAISS.from_documents(docs, self.embedding_model)
118
+ self.db.save_local(vector_store_path)
119
+ self.current_vector_store = vector_store_path
120
+
121
+ # Create conversation chain
122
+ self.chain = ConversationalRetrievalChain.from_llm(
123
+ llm=self.llm,
124
+ retriever=self.db.as_retriever(
125
+ search_type="similarity",
126
+ search_kwargs={"k": 3}
127
+ ),
128
+ memory=self.memory,
129
+ return_source_documents=True,
130
+ combine_docs_chain_kwargs={"prompt": None}
131
+ )
132
+
133
+ # Start cleanup timer
134
+ self.cleanup_timer = threading.Thread(
135
+ target=self.cleanup_files,
136
+ args=(vector_store_path, file_path)
137
+ )
138
+ self.cleanup_timer.daemon = True
139
+ self.cleanup_timer.start()
140
+
141
+ return "✅ Document processed successfully! You can now ask questions. Note: The document and its data will be automatically deleted after 10 minutes."
142
+
143
+ except Exception as e:
144
+ return f"❌ Error processing document: {str(e)}"
145
+
146
+ def chat(self, query):
147
+ # List of generic questions to check
148
+ generic_questions = [
149
+ "what can you do?",
150
+ "what are your capabilities?",
151
+ "help",
152
+ "what is this?",
153
+ "how does this work?",
154
+ "what are your functions?",
155
+ "what do you do?",
156
+ "how do i use this?",
157
+ "instructions",
158
+ "guide",
159
+ ]
160
+
161
+ try:
162
+ # Check if it's a generic question
163
+ if query.lower().strip() in generic_questions:
164
+ # Add document status to response
165
+ status = "I already have a document loaded and ready for questions." if self.chain else "No document is currently loaded. Please upload a PDF document first."
166
+ result = self.generic_chain.run(question=query)
167
+ return f"{result}\n\nCurrent Status: {status}"
168
+
169
+ # If not generic, process as document question
170
+ if self.chain is None:
171
+ return ("Please upload and process a document first. "
172
+ "Click the 'Choose Files' button above to upload a PDF document.")
173
+
174
+ result = self.chain({"question": query})
175
+ return result['answer']
176
+
177
+ except Exception as e:
178
+ return f"Error processing your question: {str(e)}"
179
+
180
+ def reset(self):
181
+ """Reset the chat session"""
182
+ try:
183
+ # Clean up current files if they exist
184
+ if self.current_vector_store and os.path.exists(self.current_vector_store):
185
+ shutil.rmtree(self.current_vector_store)
186
+
187
+ # Reset all instance variables
188
+ self.chain = None
189
+ self.db = None
190
+ self.current_vector_store = None
191
+ self.memory.clear()
192
+
193
+ return "Chat session has been reset. You can upload a new document."
194
+ except Exception as e:
195
+ return f"Error resetting chat session: {str(e)}"
196
+
197
+ def create_demo():
198
+ # Initialize DocumentChat
199
+ doc_chat = DocumentChat()
200
+
201
+ # Define Gradio interface
202
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
203
+ gr.Markdown("""
204
+ # 📚 Document Chat Interface
205
+ Upload a PDF document and chat with its contents. Files are automatically deleted after 10 minutes for privacy.
206
+
207
+ ## Instructions:
208
+ 1. Upload a PDF document using the file upload below
209
+ 2. Click 'Process Document' and wait for confirmation
210
+ 3. Start asking questions about your document
211
+ 4. Use 'Reset Chat' to start fresh with a new document
212
+ """)
213
+
214
+ # Status message
215
+ status_msg = gr.Textbox(label="Status", interactive=False)
216
+
217
+ with gr.Row():
218
+ with gr.Column(scale=1):
219
+ file_input = gr.File(
220
+ label="Drop your PDF here",
221
+ file_types=[".pdf"],
222
+ type="filepath"
223
+ )
224
+ process_button = gr.Button("📄 Process Document", variant="primary")
225
+ reset_button = gr.Button("🔄 Reset Chat", variant="secondary")
226
+
227
+ with gr.Column(scale=2):
228
+ chatbot = gr.Chatbot(
229
+ label="Chat History",
230
+ height=400,
231
+ bubble_full_width=False
232
+ )
233
+ msg = gr.Textbox(
234
+ label="Your Question",
235
+ placeholder="Ask something about the document or type 'help' for assistance...",
236
+ lines=2
237
+ )
238
+ send = gr.Button("🚀 Send", variant="primary")
239
+
240
+ # Event handlers
241
+ def user_message(message, history):
242
+ if not message.strip():
243
+ return "", history
244
+
245
+ response = doc_chat.chat(message)
246
+ history.append((message, response))
247
+ return "", history
248
+
249
+ def reset_chat():
250
+ result = doc_chat.reset()
251
+ return result, None # Clear chatbot history
252
+
253
+ process_button.click(
254
+ fn=doc_chat.process_file,
255
+ inputs=[file_input],
256
+ outputs=[status_msg]
257
+ )
258
+
259
+ reset_button.click(
260
+ fn=reset_chat,
261
+ inputs=[],
262
+ outputs=[status_msg, chatbot]
263
+ )
264
+
265
+ # Add both submit methods
266
+ msg.submit(
267
+ fn=user_message,
268
+ inputs=[msg, chatbot],
269
+ outputs=[msg, chatbot]
270
+ )
271
+
272
+ send.click(
273
+ fn=user_message,
274
+ inputs=[msg, chatbot],
275
+ outputs=[msg, chatbot]
276
+ )
277
+
278
+ return demo
279
 
280
  if __name__ == "__main__":
281
+ demo = create_demo()
282
+ demo.launch(
283
+ share=True,
284
+ show_error=True,
285
+ max_threads=40 # Replace enable_queue with max_threads
286
+ )
287
+
288
+
289
+
290
+
291
+
292
+