veerukhannan commited on
Commit
a8dcc53
Β·
verified Β·
1 Parent(s): 6404fd8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -80
app.py CHANGED
@@ -2,176 +2,243 @@ import gradio as gr
2
  from typing import List, Dict
3
  from langchain_core.prompts import ChatPromptTemplate
4
  from langchain_community.llms.huggingface_pipeline import HuggingFacePipeline
5
- from transformers import pipeline
 
 
 
6
  import chromadb
7
  from chromadb.utils import embedding_functions
8
- from sentence_transformers import SentenceTransformer
 
9
  import os
 
 
 
 
 
10
 
11
- class ChromaDBChatbot:
 
 
 
 
12
  def __init__(self):
13
- # Initialize in-memory ChromaDB
14
  self.chroma_client = chromadb.Client()
15
 
16
- # Initialize embedding function
17
  self.embedding_function = embedding_functions.SentenceTransformerEmbeddingFunction(
18
  model_name="all-MiniLM-L6-v2"
19
  )
20
 
21
- # Create or get collection
22
  self.collection = self.chroma_client.create_collection(
23
  name="text_collection",
24
- embedding_function=self.embedding_function
 
25
  )
26
 
27
- # Initialize the model - using a smaller model suitable for CPU
 
 
 
 
 
 
 
 
 
28
  pipe = pipeline(
29
  "text-generation",
30
- model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
 
31
  max_new_tokens=512,
32
  temperature=0.7,
33
  top_p=0.95,
34
- repetition_penalty=1.15
 
35
  )
 
36
  self.llm = HuggingFacePipeline(pipeline=pipe)
37
 
38
- # Enhanced prompt templates
39
  self.templates = {
40
  "default": """
41
- IMPORTANT: You are a helpful assistant that provides information based on the retrieved context.
42
 
43
- STRICT RULES:
44
- 1. Base your response ONLY on the provided context
45
- 2. If you cannot find relevant information, respond with: "I apologize, but I cannot find information about that in the database."
46
- 3. Do not make assumptions or use external knowledge
47
- 4. Be concise and accurate in your responses
48
- 5. If quoting from the context, clearly indicate it
49
 
50
  Context: {context}
51
  Chat History: {chat_history}
52
  Question: {question}
53
 
54
- Answer:""",
55
 
56
  "summary": """
57
- Create a concise summary of the following context.
58
 
59
  Context: {context}
60
 
61
- Key Requirements:
62
- 1. Highlight the main points
63
- 2. Keep it brief and clear
64
- 3. Use bullet points if appropriate
65
- 4. Include only information from the context
66
 
67
  Summary:""",
68
 
69
  "technical": """
70
- Provide a technical explanation based on the context.
71
 
72
  Context: {context}
73
  Question: {question}
74
 
75
- Guidelines:
76
- 1. Focus on technical details
77
  2. Explain complex concepts clearly
78
  3. Use appropriate technical terminology
79
- 4. Provide examples if present in the context
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- Technical Explanation:"""
82
  }
83
 
84
- self.chat_history = ""
85
  self.loaded = False
86
 
87
- def load_data(self, file_path: str):
88
- """Load data into ChromaDB"""
89
  if self.loaded:
90
- return
91
 
92
  try:
93
  # Read the text file
94
  with open(file_path, 'r', encoding='utf-8') as f:
95
  content = f.read()
96
 
97
- # Split into chunks (512 tokens each with 50 token overlap)
98
- chunk_size = 512
99
- overlap = 50
100
  chunks = []
101
-
102
  for i in range(0, len(content), chunk_size - overlap):
103
  chunk = content[i:i + chunk_size]
104
  chunks.append(chunk)
105
 
106
- # Add documents to collection
107
- self.collection.add(
108
- documents=chunks,
109
- ids=[f"doc_{i}" for i in range(len(chunks))]
110
- )
 
 
111
 
112
  self.loaded = True
113
- print(f"Loaded {len(chunks)} chunks into ChromaDB")
114
 
115
  except Exception as e:
116
  print(f"Error loading data: {str(e)}")
117
  return False
118
 
119
- def _search_chroma(self, query: str) -> List[Dict]:
120
- """Search ChromaDB for relevant documents"""
121
  try:
122
  results = self.collection.query(
123
  query_texts=[query],
124
- n_results=5
 
125
  )
126
- return [{"content": doc} for doc in results['documents'][0]]
 
 
 
 
 
 
 
 
 
 
 
 
127
  except Exception as e:
128
- print(f"Error searching ChromaDB: {str(e)}")
129
  return []
130
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  def chat(self, query: str, history) -> str:
132
- """Process a query and return a response"""
133
  try:
134
  if not self.loaded:
135
- self.load_data('a2023-45.txt')
 
136
 
137
- # Determine template type based on query
138
- template_type = "default"
139
- if any(word in query.lower() for word in ["summarize", "summary"]):
140
- template_type = "summary"
141
- elif any(word in query.lower() for word in ["technical", "explain", "how does"]):
142
- template_type = "technical"
143
-
144
- # Search ChromaDB for relevant content
145
- search_results = self._search_chroma(query)
146
 
147
  if not search_results:
148
- return "I apologize, but I cannot find information about that in the database."
149
 
150
- # Extract and combine relevant content
151
- context = "\n\n".join([result['content'] for result in search_results])
 
 
 
 
 
152
 
153
- # Create prompt with selected template
 
154
  prompt = ChatPromptTemplate.from_template(self.templates[template_type])
155
 
156
- # Generate response using LLM
157
  chain = prompt | self.llm
158
- result = chain.invoke({
159
  "context": context,
160
- "chat_history": self.chat_history,
161
  "question": query
162
  })
163
 
164
  # Update chat history
165
- self.chat_history += f"\nUser: {query}\nAI: {result}\n"
 
 
 
166
 
167
- return result
168
  except Exception as e:
169
  return f"Error processing query: {str(e)}"
170
 
171
- # Initialize the chatbot
172
- chatbot = ChromaDBChatbot()
173
 
174
- # Create the Gradio interface
175
  demo = gr.Interface(
176
  fn=chatbot.chat,
177
  inputs=[
@@ -183,17 +250,23 @@ demo = gr.Interface(
183
  gr.State([]) # For chat history
184
  ],
185
  outputs=gr.Textbox(label="Answer", lines=10),
186
- title="ChromaDB-powered Document Q&A",
187
  description="""
188
- Ask questions about your document:
189
- - For summaries, include words like 'summarize' or 'summary'
190
- - For technical details, use words like 'technical', 'explain', 'how does'
191
- - For general questions, just ask normally
 
 
 
 
 
192
  """,
193
  examples=[
194
- ["Can you summarize the main points?"],
195
- ["What are the technical details about this topic?"],
196
- ["Give me a general overview of the content."],
 
197
  ],
198
  theme=gr.themes.Soft()
199
  )
 
2
  from typing import List, Dict
3
  from langchain_core.prompts import ChatPromptTemplate
4
  from langchain_community.llms.huggingface_pipeline import HuggingFacePipeline
5
+ from langchain_community.embeddings import HuggingFaceEmbeddings
6
+ from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
7
+ import torch
8
+ from sentence_transformers import SentenceTransformer
9
  import chromadb
10
  from chromadb.utils import embedding_functions
11
+ import numpy as np
12
+ from tqdm import tqdm
13
  import os
14
+ from huggingface_hub import login
15
+ from dotenv import load_dotenv
16
+
17
+ # Load environment variables
18
+ load_dotenv()
19
 
20
+ # Login to Hugging Face Hub if token is available
21
+ if os.getenv("HUGGINGFACE_API_TOKEN"):
22
+ login(token=os.getenv("HUGGINGFACE_API_TOKEN"))
23
+
24
+ class EnhancedChatbot:
25
  def __init__(self):
26
+ # Initialize ChromaDB
27
  self.chroma_client = chromadb.Client()
28
 
29
+ # Initialize embedding model using sentence-transformers
30
  self.embedding_function = embedding_functions.SentenceTransformerEmbeddingFunction(
31
  model_name="all-MiniLM-L6-v2"
32
  )
33
 
34
+ # Create collection with cosine similarity
35
  self.collection = self.chroma_client.create_collection(
36
  name="text_collection",
37
+ embedding_function=self.embedding_function,
38
+ metadata={"hnsw:space": "cosine"}
39
  )
40
 
41
+ # Initialize the LLM with 8-bit quantization for efficiency
42
+ model_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
43
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
44
+ model = AutoModelForCausalLM.from_pretrained(
45
+ model_name,
46
+ load_in_8bit=True,
47
+ device_map="auto",
48
+ torch_dtype=torch.float16
49
+ )
50
+
51
  pipe = pipeline(
52
  "text-generation",
53
+ model=model,
54
+ tokenizer=tokenizer,
55
  max_new_tokens=512,
56
  temperature=0.7,
57
  top_p=0.95,
58
+ repetition_penalty=1.15,
59
+ do_sample=True
60
  )
61
+
62
  self.llm = HuggingFacePipeline(pipeline=pipe)
63
 
64
+ # Enhanced prompt templates with specific use cases
65
  self.templates = {
66
  "default": """
67
+ You are a knowledgeable assistant providing accurate information based on the given context.
68
 
69
+ GUIDELINES:
70
+ 1. Use ONLY the provided context
71
+ 2. If information is not in context, say "I don't have enough information"
72
+ 3. Be concise and clear
73
+ 4. Use markdown formatting for better readability
74
+ 5. If quoting, use proper citation format
75
 
76
  Context: {context}
77
  Chat History: {chat_history}
78
  Question: {question}
79
 
80
+ Response:""",
81
 
82
  "summary": """
83
+ Create a comprehensive summary of the provided context.
84
 
85
  Context: {context}
86
 
87
+ REQUIREMENTS:
88
+ 1. Structure the summary with clear headings
89
+ 2. Use bullet points for key information
90
+ 3. Highlight important concepts
91
+ 4. Maintain factual accuracy
92
 
93
  Summary:""",
94
 
95
  "technical": """
96
+ Provide a detailed technical analysis of the context.
97
 
98
  Context: {context}
99
  Question: {question}
100
 
101
+ GUIDELINES:
102
+ 1. Focus on technical specifications
103
  2. Explain complex concepts clearly
104
  3. Use appropriate technical terminology
105
+ 4. Include relevant examples from context
106
+ 5. Structure the response logically
107
+
108
+ Technical Analysis:""",
109
+
110
+ "comparative": """
111
+ Compare and analyze different aspects from the context.
112
+
113
+ Context: {context}
114
+ Question: {question}
115
+
116
+ APPROACH:
117
+ 1. Identify key points for comparison
118
+ 2. Analyze similarities and differences
119
+ 3. Present balanced viewpoints
120
+ 4. Use tables or lists for clarity
121
 
122
+ Comparison:"""
123
  }
124
 
125
+ self.chat_history = []
126
  self.loaded = False
127
 
128
+ def load_data(self, file_path: str, chunk_size: int = 512, overlap: int = 50):
129
+ """Load and index data with progress bar"""
130
  if self.loaded:
131
+ return True
132
 
133
  try:
134
  # Read the text file
135
  with open(file_path, 'r', encoding='utf-8') as f:
136
  content = f.read()
137
 
138
+ # Create chunks with overlap
 
 
139
  chunks = []
 
140
  for i in range(0, len(content), chunk_size - overlap):
141
  chunk = content[i:i + chunk_size]
142
  chunks.append(chunk)
143
 
144
+ # Add documents to collection with progress bar
145
+ for i, chunk in tqdm(enumerate(chunks), desc="Loading chunks", total=len(chunks)):
146
+ self.collection.add(
147
+ documents=[chunk],
148
+ ids=[f"chunk_{i}"],
149
+ metadatas=[{"source": file_path, "chunk_id": i}]
150
+ )
151
 
152
  self.loaded = True
153
+ return True
154
 
155
  except Exception as e:
156
  print(f"Error loading data: {str(e)}")
157
  return False
158
 
159
+ def _search_documents(self, query: str, n_results: int = 5) -> List[Dict]:
160
+ """Search for relevant documents"""
161
  try:
162
  results = self.collection.query(
163
  query_texts=[query],
164
+ n_results=n_results,
165
+ include=["documents", "metadatas", "distances"]
166
  )
167
+
168
+ return [
169
+ {
170
+ "content": doc,
171
+ "metadata": meta,
172
+ "similarity": 1 - dist # Convert distance to similarity
173
+ }
174
+ for doc, meta, dist in zip(
175
+ results['documents'][0],
176
+ results['metadatas'][0],
177
+ results['distances'][0]
178
+ )
179
+ ]
180
  except Exception as e:
181
+ print(f"Search error: {str(e)}")
182
  return []
183
 
184
+ def _select_template(self, query: str) -> str:
185
+ """Select appropriate template based on query content"""
186
+ query_lower = query.lower()
187
+
188
+ if any(word in query_lower for word in ["summarize", "summary", "overview"]):
189
+ return "summary"
190
+ elif any(word in query_lower for word in ["technical", "explain how", "how does"]):
191
+ return "technical"
192
+ elif any(word in query_lower for word in ["compare", "difference", "versus", "vs"]):
193
+ return "comparative"
194
+ return "default"
195
+
196
  def chat(self, query: str, history) -> str:
197
+ """Process query and generate response"""
198
  try:
199
  if not self.loaded:
200
+ if not self.load_data('a2023-45.txt'):
201
+ return "Error: Failed to load document data."
202
 
203
+ # Search for relevant content
204
+ search_results = self._search_documents(query)
 
 
 
 
 
 
 
205
 
206
  if not search_results:
207
+ return "I apologize, but I couldn't find relevant information in the database."
208
 
209
+ # Prepare context with similarity scores
210
+ context_parts = []
211
+ for result in search_results:
212
+ context_parts.append(
213
+ f"[Similarity: {result['similarity']:.2f}]\n{result['content']}"
214
+ )
215
+ context = "\n\n".join(context_parts)
216
 
217
+ # Select and use appropriate template
218
+ template_type = self._select_template(query)
219
  prompt = ChatPromptTemplate.from_template(self.templates[template_type])
220
 
221
+ # Generate response
222
  chain = prompt | self.llm
223
+ response = chain.invoke({
224
  "context": context,
225
+ "chat_history": "\n".join([f"{h[0]}: {h[1]}" for h in self.chat_history[-3:]]),
226
  "question": query
227
  })
228
 
229
  # Update chat history
230
+ self.chat_history.append(("User", query))
231
+ self.chat_history.append(("Assistant", response))
232
+
233
+ return response
234
 
 
235
  except Exception as e:
236
  return f"Error processing query: {str(e)}"
237
 
238
+ # Initialize chatbot
239
+ chatbot = EnhancedChatbot()
240
 
241
+ # Create Gradio interface
242
  demo = gr.Interface(
243
  fn=chatbot.chat,
244
  inputs=[
 
250
  gr.State([]) # For chat history
251
  ],
252
  outputs=gr.Textbox(label="Answer", lines=10),
253
+ title="πŸ€– Enhanced Document Q&A System",
254
  description="""
255
+ ### Advanced Document Question-Answering System
256
+
257
+ **Available Query Types:**
258
+ - πŸ“ **General Questions**: Just ask normally
259
+ - πŸ“Š **Summaries**: Include words like "summarize" or "overview"
260
+ - πŸ”§ **Technical Details**: Use words like "technical" or "explain how"
261
+ - πŸ”„ **Comparisons**: Ask to "compare" or use "versus"
262
+
263
+ *The system will automatically select the best response format based on your question.*
264
  """,
265
  examples=[
266
+ ["Can you summarize the main points of the document?"],
267
+ ["What are the technical details about the implementation?"],
268
+ ["Compare the different approaches mentioned in the text."],
269
+ ["What are the key concepts discussed?"]
270
  ],
271
  theme=gr.themes.Soft()
272
  )