Ali Abdullah commited on
Commit
02fe1da
Β·
verified Β·
1 Parent(s): 063051a

Update chatbot.py

Browse files
Files changed (1) hide show
  1. chatbot.py +261 -260
chatbot.py CHANGED
@@ -1,261 +1,262 @@
1
- import os
2
- from typing import List, Dict, Optional, Tuple
3
- from groq import Groq
4
- from dotenv import load_dotenv
5
- from web_scraper import WebScraper, TextChunker
6
-
7
- from vector_store import VectorStore
8
- import time
9
-
10
- load_dotenv()
11
-
12
- class RAGChatbot:
13
- def __init__(self):
14
- """Initialize RAG Chatbot with all components"""
15
- print("πŸ€– Initializing RAG Chatbot...")
16
-
17
- # Initialize Groq client
18
- self.groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
19
-
20
- # Initialize components
21
- self.vector_store = VectorStore()
22
- self.web_scraper = WebScraper(delay=1.0)
23
- self.text_chunker = TextChunker(
24
- chunk_size=int(os.getenv("MAX_CHUNK_SIZE", 500)),
25
- overlap=50
26
- )
27
-
28
- # Configuration
29
- self.model_name = "llama3-8b-8192"
30
- self.top_k = int(os.getenv("TOP_K_RESULTS", 5))
31
- self.max_tokens = 1000
32
-
33
- print("βœ… RAG Chatbot initialized successfully!")
34
-
35
- def ingest_url(self, url: str) -> Dict[str, any]:
36
- """
37
- Ingest content from a URL into the knowledge base
38
- Args:
39
- url: URL to scrape and ingest
40
- Returns:
41
- Dictionary with ingestion results
42
- """
43
- try:
44
- print(f"πŸ“₯ Ingesting content from: {url}")
45
-
46
- # Scrape the article
47
- article_data = self.web_scraper.scrape_article(url)
48
-
49
- if not article_data['content']:
50
- return {
51
- 'success': False,
52
- 'message': f"Could not extract content from {url}",
53
- 'chunks_added': 0
54
- }
55
-
56
- # Create chunks
57
- chunks = self.text_chunker.chunk_text(
58
- article_data['content'],
59
- metadata={
60
- 'url': article_data['url'],
61
- 'title': article_data['title']
62
- }
63
- )
64
-
65
- if not chunks:
66
- return {
67
- 'success': False,
68
- 'message': "No valid chunks created from content",
69
- 'chunks_added': 0
70
- }
71
-
72
- # Add to vector store
73
- success = self.vector_store.add_documents(chunks)
74
-
75
- if success:
76
- return {
77
- 'success': True,
78
- 'message': f"Successfully ingested '{article_data['title']}'",
79
- 'chunks_added': len(chunks),
80
- 'title': article_data['title'],
81
- 'word_count': article_data['word_count']
82
- }
83
- else:
84
- return {
85
- 'success': False,
86
- 'message': "Failed to add chunks to vector store",
87
- 'chunks_added': 0
88
- }
89
-
90
- except Exception as e:
91
- return {
92
- 'success': False,
93
- 'message': f"Error ingesting {url}: {str(e)}",
94
- 'chunks_added': 0
95
- }
96
-
97
- def chat(self, message: str, include_sources: bool = True) -> Dict[str, any]:
98
- """
99
- Chat with the RAG system
100
- Args:
101
- message: User's question/message
102
- include_sources: Whether to include source information
103
- Returns:
104
- Dictionary with response and metadata
105
- """
106
- try:
107
- print(f"πŸ’¬ Processing query: {message[:50]}...")
108
-
109
- # Step 1: Retrieve relevant context
110
- start_time = time.time()
111
- relevant_docs = self.vector_store.search_similar(message, top_k=self.top_k)
112
- retrieval_time = time.time() - start_time
113
-
114
- if not relevant_docs:
115
- return {
116
- 'response': "I don't have enough information to answer your question. Please add some relevant content to my knowledge base first.",
117
- 'sources': [],
118
- 'retrieval_time': retrieval_time,
119
- 'generation_time': 0,
120
- 'total_time': retrieval_time
121
- }
122
-
123
- # Step 2: Create context from retrieved documents
124
- context_parts = []
125
- sources = []
126
-
127
- for i, doc in enumerate(relevant_docs):
128
- context_parts.append(f"Context {i+1}: {doc['text']}")
129
- sources.append({
130
- 'title': doc['title'],
131
- 'url': doc['url'],
132
- 'similarity_score': doc['score'],
133
- 'snippet': doc['text'][:200] + "..." if len(doc['text']) > 200 else doc['text']
134
- })
135
-
136
- context = "\n\n".join(context_parts)
137
-
138
- # Step 3: Generate response using Groq
139
- generation_start = time.time()
140
- response = self._generate_response(message, context)
141
- generation_time = time.time() - generation_start
142
-
143
- total_time = time.time() - start_time
144
-
145
- return {
146
- 'response': response,
147
- 'sources': sources if include_sources else [],
148
- 'retrieval_time': round(retrieval_time, 3),
149
- 'generation_time': round(generation_time, 3),
150
- 'total_time': round(total_time, 3),
151
- 'context_used': len(relevant_docs)
152
- }
153
-
154
- except Exception as e:
155
- return {
156
- 'response': f"Sorry, I encountered an error: {str(e)}",
157
- 'sources': [],
158
- 'retrieval_time': 0,
159
- 'generation_time': 0,
160
- 'total_time': 0,
161
- 'error': str(e)
162
- }
163
-
164
- def _generate_response(self, query: str, context: str) -> str:
165
- """
166
- Generate response using Groq API
167
- Args:
168
- query: User's question
169
- context: Retrieved context
170
- Returns:
171
- Generated response
172
- """
173
- system_prompt = """You are a helpful AI assistant that answers questions based on the provided context.
174
-
175
- Guidelines:
176
- - Use ONLY the information provided in the context to answer questions
177
- - If the context doesn't contain enough information, say so clearly
178
- - Be accurate and cite specific details from the context
179
- - Provide comprehensive answers but stay focused on the question
180
- - If asked about sources, refer to the context provided
181
- - Be conversational and helpful in your tone
182
- """
183
-
184
- user_prompt = f"""Context:
185
- {context}
186
-
187
- Question: {query}
188
-
189
- Please provide a detailed answer based on the context above. If the context doesn't contain sufficient information to answer the question, please say so clearly."""
190
-
191
- try:
192
- completion = self.groq_client.chat.completions.create(
193
- model=self.model_name,
194
- messages=[
195
- {"role": "system", "content": system_prompt},
196
- {"role": "user", "content": user_prompt}
197
- ],
198
- max_tokens=self.max_tokens,
199
- temperature=0.3, # Lower temperature for more focused responses
200
- top_p=0.9
201
- )
202
-
203
- return completion.choices[0].message.content.strip()
204
-
205
- except Exception as e:
206
- return f"Error generating response: {str(e)}"
207
-
208
- def get_knowledge_base_stats(self) -> Dict[str, any]:
209
- """Get statistics about the knowledge base"""
210
- try:
211
- stats = self.vector_store.get_index_stats()
212
- return {
213
- 'total_documents': stats.get('total_vectors', 0),
214
- 'index_dimension': stats.get('dimension', 0),
215
- 'index_fullness': stats.get('index_fullness', 0),
216
- 'model_used': self.model_name,
217
- 'embedding_model': os.getenv("EMBEDDING_MODEL", "all-MiniLM-L6-v2")
218
- }
219
- except Exception as e:
220
- return {'error': str(e)}
221
-
222
- def clear_knowledge_base(self) -> bool:
223
- """Clear all documents from knowledge base"""
224
- try:
225
- return self.vector_store.delete_all()
226
- except Exception as e:
227
- print(f"Error clearing knowledge base: {str(e)}")
228
- return False
229
-
230
- # Test the chatbot
231
- if __name__ == "__main__":
232
- # Initialize chatbot
233
- chatbot = RAGChatbot()
234
-
235
- # Test ingestion (replace with your URL)
236
- test_url = "https://medium.com/@aminajavaid30/building-a-rag-system-the-data-ingestion-pipeline-d04235fd17ea"
237
-
238
- print("Testing content ingestion...")
239
- ingestion_result = chatbot.ingest_url(test_url)
240
- print(f"Ingestion result: {ingestion_result}")
241
-
242
- if ingestion_result['success']:
243
- print("\nTesting chat functionality...")
244
-
245
- # Test questions
246
- test_questions = [
247
- "What is RAG?",
248
- "How does the data ingestion pipeline work?",
249
- "What are the main components of a RAG system?"
250
- ]
251
-
252
- for question in test_questions:
253
- print(f"\n❓ Question: {question}")
254
- response = chatbot.chat(question)
255
- print(f"πŸ€– Answer: {response['response']}")
256
- print(f"⏱️ Time: {response['total_time']}s (Retrieval: {response['retrieval_time']}s, Generation: {response['generation_time']}s)")
257
- print(f"πŸ“š Sources used: {response['context_used']}")
258
-
259
- # Show knowledge base stats
260
- stats = chatbot.get_knowledge_base_stats()
 
261
  print(f"\nπŸ“Š Knowledge Base Stats: {stats}")
 
1
+ import os
2
+ from typing import List, Dict, Optional, Tuple
3
+ from groq import Groq
4
+ from dotenv import load_dotenv
5
+ from web_scraper import WebScraper, TextChunker
6
+
7
+ from vector_store import VectorStore
8
+ import time
9
+
10
+ load_dotenv()
11
+
12
+ class RAGChatbot:
13
+ def __init__(self):
14
+ """Initialize RAG Chatbot with all components"""
15
+ print("πŸ€– Initializing RAG Chatbot...")
16
+
17
+ # Initialize Groq client
18
+ self.groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))
19
+
20
+ # Initialize components
21
+ self.vector_store = VectorStore()
22
+ self.web_scraper = WebScraper(delay=1.0)
23
+ self.text_chunker = TextChunker(
24
+ chunk_size=int(os.getenv("MAX_CHUNK_SIZE", 500)),
25
+ overlap=50
26
+ )
27
+
28
+ # Configuration
29
+ self.model_name = "llama3-8b-8192"
30
+ self.top_k = int(os.getenv("TOP_K_RESULTS", 5))
31
+ self.max_tokens = 1000
32
+
33
+ print("βœ… RAG Chatbot initialized successfully!")
34
+
35
+ def ingest_url(self, url: str) -> Dict[str, any]:
36
+ """
37
+ Ingest content from a URL into the knowledge base
38
+ Args:
39
+ url: URL to scrape and ingest
40
+ Returns:
41
+ Dictionary with ingestion results
42
+ """
43
+ try:
44
+ print(f"πŸ“₯ Ingesting content from: {url}")
45
+
46
+ # Scrape the article
47
+ article_data = self.web_scraper.scrape_article(url)
48
+
49
+ if not article_data['content']:
50
+ return {
51
+ 'success': False,
52
+ 'message': f"Could not extract content from {url}",
53
+ 'chunks_added': 0
54
+ }
55
+
56
+ # Create chunks
57
+ chunks = self.text_chunker.chunk_text(
58
+ article_data['content'],
59
+ metadata={
60
+ 'url': article_data['url'],
61
+ 'title': article_data['title']
62
+ }
63
+ )
64
+
65
+ if not chunks:
66
+ return {
67
+ 'success': False,
68
+ 'message': "No valid chunks created from content",
69
+ 'chunks_added': 0
70
+ }
71
+
72
+ # Add to vector store
73
+ success = self.vector_store.add_documents(chunks)
74
+
75
+ if success:
76
+ return {
77
+ 'success': True,
78
+ 'message': f"Successfully ingested '{article_data['title']}'",
79
+ 'chunks_added': len(chunks),
80
+ 'title': article_data['title'],
81
+ 'word_count': article_data['word_count']
82
+ }
83
+ else:
84
+ return {
85
+ 'success': False,
86
+ 'message': "Failed to add chunks to vector store",
87
+ 'chunks_added': 0
88
+ }
89
+
90
+ except Exception as e:
91
+ return {
92
+ 'success': False,
93
+ 'message': f"Error ingesting {url}: {str(e)}",
94
+ 'chunks_added': 0
95
+ }
96
+
97
+ def chat(self, message: str, include_sources: bool = True) -> Dict[str, any]:
98
+ """
99
+ Chat with the RAG system
100
+ Args:
101
+ message: User's question/message
102
+ include_sources: Whether to include source information
103
+ Returns:
104
+ Dictionary with response and metadata
105
+ """
106
+ try:
107
+ print(f"πŸ’¬ Processing query: {message[:50]}...")
108
+
109
+ # Step 1: Retrieve relevant context
110
+ start_time = time.time()
111
+ relevant_docs = self.vector_store.search_similar(message, top_k=self.top_k)
112
+ retrieval_time = time.time() - start_time
113
+
114
+ if not relevant_docs:
115
+ return {
116
+ 'response': "I don't have enough information to answer your question. Please add some relevant content to my knowledge base first.",
117
+ 'sources': [],
118
+ 'retrieval_time': retrieval_time,
119
+ 'generation_time': 0,
120
+ 'total_time': retrieval_time
121
+ }
122
+
123
+ # Step 2: Create context from retrieved documents
124
+ context_parts = []
125
+ sources = []
126
+
127
+ for i, doc in enumerate(relevant_docs):
128
+ clean_text = doc['text'].replace("\n", " ").strip()
129
+ context_parts.append(clean_text)
130
+ sources.append({
131
+ 'title': doc['title'],
132
+ 'url': doc['url'],
133
+ 'similarity_score': doc['score'],
134
+ 'snippet': doc['text'][:200] + "..." if len(doc['text']) > 200 else doc['text']
135
+ })
136
+
137
+ context = "\n\n".join(context_parts)
138
+
139
+ # Step 3: Generate response using Groq
140
+ generation_start = time.time()
141
+ response = self._generate_response(message, context)
142
+ generation_time = time.time() - generation_start
143
+
144
+ total_time = time.time() - start_time
145
+
146
+ return {
147
+ 'response': response,
148
+ 'sources': sources if include_sources else [],
149
+ 'retrieval_time': round(retrieval_time, 3),
150
+ 'generation_time': round(generation_time, 3),
151
+ 'total_time': round(total_time, 3),
152
+ 'context_used': len(relevant_docs)
153
+ }
154
+
155
+ except Exception as e:
156
+ return {
157
+ 'response': f"Sorry, I encountered an error: {str(e)}",
158
+ 'sources': [],
159
+ 'retrieval_time': 0,
160
+ 'generation_time': 0,
161
+ 'total_time': 0,
162
+ 'error': str(e)
163
+ }
164
+
165
+ def _generate_response(self, query: str, context: str) -> str:
166
+ """
167
+ Generate response using Groq API
168
+ Args:
169
+ query: User's question
170
+ context: Retrieved context
171
+ Returns:
172
+ Generated response
173
+ """
174
+ system_prompt = """You are a helpful AI assistant that answers questions based on the provided context.
175
+
176
+ Guidelines:
177
+ - Use ONLY the information provided in the context to answer questions
178
+ - If the context doesn't contain enough information, say so clearly
179
+ - Be accurate and cite specific details from the context
180
+ - Provide comprehensive answers but stay focused on the question
181
+ - If asked about sources, refer to the context provided
182
+ - Be conversational and helpful in your tone
183
+ """
184
+
185
+ user_prompt = f"""Context:
186
+ {context}
187
+
188
+ Question: {query}
189
+
190
+ Please provide a detailed answer based on the context above. If the context doesn't contain sufficient information to answer the question, please say so clearly."""
191
+
192
+ try:
193
+ completion = self.groq_client.chat.completions.create(
194
+ model=self.model_name,
195
+ messages=[
196
+ {"role": "system", "content": system_prompt},
197
+ {"role": "user", "content": user_prompt}
198
+ ],
199
+ max_tokens=self.max_tokens,
200
+ temperature=0.3, # Lower temperature for more focused responses
201
+ top_p=0.9
202
+ )
203
+
204
+ return completion.choices[0].message.content.strip()
205
+
206
+ except Exception as e:
207
+ return f"Error generating response: {str(e)}"
208
+
209
+ def get_knowledge_base_stats(self) -> Dict[str, any]:
210
+ """Get statistics about the knowledge base"""
211
+ try:
212
+ stats = self.vector_store.get_index_stats()
213
+ return {
214
+ 'total_documents': stats.get('total_vectors', 0),
215
+ 'index_dimension': stats.get('dimension', 0),
216
+ 'index_fullness': stats.get('index_fullness', 0),
217
+ 'model_used': self.model_name,
218
+ 'embedding_model': os.getenv("EMBEDDING_MODEL", "all-MiniLM-L6-v2")
219
+ }
220
+ except Exception as e:
221
+ return {'error': str(e)}
222
+
223
+ def clear_knowledge_base(self) -> bool:
224
+ """Clear all documents from knowledge base"""
225
+ try:
226
+ return self.vector_store.delete_all()
227
+ except Exception as e:
228
+ print(f"Error clearing knowledge base: {str(e)}")
229
+ return False
230
+
231
+ # Test the chatbot
232
+ if __name__ == "__main__":
233
+ # Initialize chatbot
234
+ chatbot = RAGChatbot()
235
+
236
+ # Test ingestion (replace with your URL)
237
+ test_url = "https://medium.com/@aminajavaid30/building-a-rag-system-the-data-ingestion-pipeline-d04235fd17ea"
238
+
239
+ print("Testing content ingestion...")
240
+ ingestion_result = chatbot.ingest_url(test_url)
241
+ print(f"Ingestion result: {ingestion_result}")
242
+
243
+ if ingestion_result['success']:
244
+ print("\nTesting chat functionality...")
245
+
246
+ # Test questions
247
+ test_questions = [
248
+ "What is RAG?",
249
+ "How does the data ingestion pipeline work?",
250
+ "What are the main components of a RAG system?"
251
+ ]
252
+
253
+ for question in test_questions:
254
+ print(f"\n❓ Question: {question}")
255
+ response = chatbot.chat(question)
256
+ print(f"πŸ€– Answer: {response['response']}")
257
+ print(f"⏱️ Time: {response['total_time']}s (Retrieval: {response['retrieval_time']}s, Generation: {response['generation_time']}s)")
258
+ print(f"πŸ“š Sources used: {response['context_used']}")
259
+
260
+ # Show knowledge base stats
261
+ stats = chatbot.get_knowledge_base_stats()
262
  print(f"\nπŸ“Š Knowledge Base Stats: {stats}")