adeena43 commited on
Commit
d7986f8
·
verified ·
1 Parent(s): 2479476

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +430 -0
  2. rag_engine.py +180 -0
  3. requirements.txt +10 -3
app.py ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+
3
+ import streamlit as st
4
+ from rag_engine import (
5
+ load_user_document,
6
+ build_retriever_from_docs,
7
+ answer_question
8
+ )
9
+ import os
10
+
11
+ # Set environment variables
12
+ os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
13
+ os.environ["USER_AGENT"] = "PythonicAI/1.0 (+adinafraz01@gmail.com)"
14
+
15
+ st.set_page_config(
16
+ page_title="AI Documentation Assistant",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded"
19
+ )
20
+
21
+ # -------------------- SESSION STATE --------------------
22
+ if "retriever" not in st.session_state:
23
+ st.session_state.retriever = None
24
+
25
+ if "docs_loaded" not in st.session_state:
26
+ st.session_state.docs_loaded = False
27
+
28
+ if "current_url" not in st.session_state:
29
+ st.session_state.current_url = None
30
+
31
+ if "processing" not in st.session_state:
32
+ st.session_state.processing = False
33
+
34
+ # -------------------- CUSTOM CSS --------------------
35
+ st.markdown("""
36
+ <style>
37
+ body {
38
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
39
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
40
+ min-height: 100vh;
41
+ }
42
+
43
+ .main-container {
44
+ max-width: 1000px;
45
+ margin: 0 auto;
46
+ }
47
+
48
+ .header-card {
49
+ background: rgba(255,255,255,0.95);
50
+ border-radius: 20px;
51
+ padding: 30px;
52
+ margin-bottom: 30px;
53
+ box-shadow: 0 10px 40px rgba(0,0,0,0.1);
54
+ backdrop-filter: blur(10px);
55
+ border: 1px solid rgba(255,255,255,0.2);
56
+ }
57
+
58
+ .input-card {
59
+ background: rgba(255,255,255,0.95);
60
+ border-radius: 15px;
61
+ padding: 25px;
62
+ margin-bottom: 25px;
63
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
64
+ border: 1px solid rgba(255,255,255,0.2);
65
+ }
66
+
67
+ .answer-card {
68
+ background: white;
69
+ border-radius: 15px;
70
+ padding: 30px;
71
+ margin-top: 25px;
72
+ box-shadow: 0 10px 40px rgba(0,0,0,0.08);
73
+ animation: fadeIn 0.5s ease;
74
+ border-left: 5px solid #667eea;
75
+ }
76
+
77
+ .url-display {
78
+ background: #f0f4ff;
79
+ border-radius: 10px;
80
+ padding: 15px;
81
+ margin: 15px 0;
82
+ border-left: 4px solid #4f46e5;
83
+ word-break: break-all;
84
+ }
85
+
86
+ .stButton button {
87
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
88
+ color: white;
89
+ font-weight: 600;
90
+ border: none;
91
+ border-radius: 12px;
92
+ padding: 14px 28px;
93
+ width: 100%;
94
+ transition: all 0.3s;
95
+ font-size: 16px;
96
+ }
97
+
98
+ .stButton button:hover {
99
+ transform: translateY(-3px);
100
+ box-shadow: 0 7px 20px rgba(0,0,0,0.2);
101
+ }
102
+
103
+ .primary-btn {
104
+ background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%) !important;
105
+ }
106
+
107
+ .secondary-btn {
108
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
109
+ }
110
+
111
+ .question-box {
112
+ background: #f8fafc;
113
+ border-radius: 12px;
114
+ padding: 20px;
115
+ border: 2px solid #e2e8f0;
116
+ font-size: 16px;
117
+ min-height: 120px;
118
+ }
119
+
120
+ .info-box {
121
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
122
+ color: white;
123
+ padding: 20px;
124
+ border-radius: 15px;
125
+ margin: 15px 0;
126
+ }
127
+
128
+ .example-chip {
129
+ display: inline-block;
130
+ background: #e0e7ff;
131
+ color: #4f46e5;
132
+ padding: 8px 16px;
133
+ margin: 5px;
134
+ border-radius: 20px;
135
+ cursor: pointer;
136
+ font-size: 14px;
137
+ transition: all 0.2s;
138
+ border: 1px solid #c7d2fe;
139
+ }
140
+
141
+ .example-chip:hover {
142
+ background: #c7d2fe;
143
+ transform: translateY(-2px);
144
+ }
145
+
146
+ @keyframes fadeIn {
147
+ from { opacity:0; transform:translateY(10px); }
148
+ to { opacity:1; transform:translateY(0); }
149
+ }
150
+
151
+ /* Scrollbar styling */
152
+ ::-webkit-scrollbar {
153
+ width: 8px;
154
+ }
155
+
156
+ ::-webkit-scrollbar-track {
157
+ background: #f1f1f1;
158
+ border-radius: 10px;
159
+ }
160
+
161
+ ::-webkit-scrollbar-thumb {
162
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
163
+ border-radius: 10px;
164
+ }
165
+
166
+ ::-webkit-scrollbar-thumb:hover {
167
+ background: linear-gradient(135deg, #5a67d8 0%, #6d28d9 100%);
168
+ }
169
+ </style>
170
+ """, unsafe_allow_html=True)
171
+
172
+ # -------------------- SIDEBAR --------------------
173
+ with st.sidebar:
174
+ st.markdown("""
175
+ <div style="text-align:center; padding:18px 0;">
176
+ <h2 style="color:black; margin-bottom:2px; font-size:20px;">📚 AI Doc Assistant</h2>
177
+ <p style="color:black; font-size:20px;">Python Documentation Q&A</p>
178
+ </div>
179
+ """, unsafe_allow_html=True)
180
+
181
+ st.markdown("---")
182
+
183
+ if st.session_state.docs_loaded:
184
+ st.markdown("### ✅ Document Status")
185
+ st.success("Document Loaded Successfully")
186
+
187
+ if st.session_state.current_url:
188
+ st.markdown("**Current URL:**")
189
+ st.code(st.session_state.current_url[:80] + "..." if len(st.session_state.current_url) > 80 else st.session_state.current_url, language="text")
190
+
191
+ if st.button("🗑️ Clear Document", use_container_width=True):
192
+ st.session_state.docs_loaded = False
193
+ st.session_state.retriever = None
194
+ st.session_state.current_url = None
195
+ st.rerun()
196
+ else:
197
+ st.markdown("### ⚠️ Status")
198
+ st.info("No document loaded")
199
+
200
+ st.markdown("---")
201
+ st.markdown("### 📖 How to Use")
202
+ st.markdown("""
203
+ 1. **Enter a URL** pointing to Python documentation
204
+ 2. Click **Load Document** to process
205
+ 3. Ask **Python-related questions** about the content
206
+ 4. Get instant, context-aware answers
207
+ """)
208
+
209
+ st.markdown("---")
210
+ st.markdown("### 💡 Example URLs")
211
+ urls = [
212
+ "https://numpy.org/doc/2.3/user/absolute_beginners.html",
213
+ "https://pandas.pydata.org/docs/",
214
+ "https://matplotlib.org/stable/users/index.html",
215
+ "https://scikit-learn.org/stable/user_guide.html"
216
+ ]
217
+
218
+ for url in urls:
219
+ if st.button(f"📄 {url.split('/')[2]}", key=f"url_{url}"):
220
+ st.session_state.url_input = url
221
+ st.rerun()
222
+
223
+ st.markdown("---")
224
+ st.markdown("""
225
+ <div style="text-align:center; color:black; font-size:12px; padding:10px;">
226
+ Powered by OpenAI • LangChain • FAISS
227
+ </div>
228
+ """, unsafe_allow_html=True)
229
+
230
+ # -------------------- MAIN CONTENT --------------------
231
+ st.markdown('<div class="main-container">', unsafe_allow_html=True)
232
+
233
+ # Header
234
+ st.markdown("""
235
+ <div class="header-card">
236
+ <h1 style="text-align:center; font-size:42px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom:10px;">
237
+ 🤖 AI Documentation Assistant
238
+ </h1>
239
+ <p style="text-align:center; font-size:18px; color:#4b5563; margin-bottom:20px;">
240
+ Ask questions about any Python documentation from a URL
241
+ </p>
242
+ </div>
243
+ """, unsafe_allow_html=True)
244
+
245
+ # URL Input Section
246
+ st.markdown('<div class="input-card">', unsafe_allow_html=True)
247
+ st.markdown("### 🌐 Enter Documentation URL")
248
+
249
+ # Initialize session state for URL if not exists
250
+ if "url_input" not in st.session_state:
251
+ st.session_state.url_input = "https://numpy.org/doc/2.3/user/absolute_beginners.html"
252
+
253
+ url = st.text_input(
254
+ "Enter the URL of Python documentation:",
255
+ value=st.session_state.url_input,
256
+ placeholder="https://example.com/python-docs",
257
+ label_visibility="collapsed",
258
+ key="url_input_field"
259
+ )
260
+
261
+ col1, col2 = st.columns([1, 1])
262
+
263
+ with col1:
264
+ load_clicked = st.button(
265
+ "📥 Load Document",
266
+ use_container_width=True,
267
+ type="primary",
268
+ disabled=not url.strip()
269
+ )
270
+
271
+ with col2:
272
+ if st.session_state.docs_loaded:
273
+ if st.button("🔄 Reload", use_container_width=True):
274
+ load_clicked = True
275
+
276
+ st.markdown('</div>', unsafe_allow_html=True)
277
+
278
+ # Handle document loading
279
+ if load_clicked:
280
+ if not url.strip():
281
+ st.error("❌ Please enter a valid URL")
282
+ else:
283
+ try:
284
+ with st.spinner("🔍 Loading and processing documentation..."):
285
+ # Store current URL
286
+ st.session_state.current_url = url
287
+
288
+ # Show progress
289
+ progress_text = st.empty()
290
+ progress_bar = st.progress(0)
291
+
292
+ progress_text.text("Downloading document...")
293
+ docs = load_user_document(url)
294
+ progress_bar.progress(30)
295
+
296
+ progress_text.text("Processing content...")
297
+ st.session_state.retriever = build_retriever_from_docs(docs)
298
+ progress_bar.progress(70)
299
+
300
+ progress_text.text("Finalizing...")
301
+ st.session_state.docs_loaded = True
302
+ progress_bar.progress(100)
303
+
304
+ progress_text.text("✅ Ready!")
305
+
306
+ st.success(f"✅ Successfully loaded documentation from: {url}")
307
+
308
+
309
+ # Show document info
310
+ st.markdown(f"""
311
+ <div class="url-display">
312
+ <strong>📄 Loaded Document:</strong> {url}<br>
313
+ <strong>📊 Pages Processed:</strong> {len(docs)}<br>
314
+ <strong>✅ Status:</strong> Ready for questions
315
+ </div>
316
+ """, unsafe_allow_html=True)
317
+
318
+ except Exception as e:
319
+ st.error(f"❌ Error loading document: {str(e)}")
320
+
321
+ # Question Input Section (only show if document is loaded)
322
+ if st.session_state.docs_loaded:
323
+ st.markdown('<div class="input-card">', unsafe_allow_html=True)
324
+
325
+ st.markdown("### 💭 Ask a Question")
326
+
327
+ # Example questions
328
+ st.markdown("**💡 Try asking:**")
329
+ col1, col2, col3, col4 = st.columns(4)
330
+
331
+ example_questions = [
332
+ "What is this library about?",
333
+ "How to get started?",
334
+ "What are the main functions?",
335
+ "Give me a code example"
336
+ ]
337
+
338
+ for i, question in enumerate(example_questions):
339
+ col = [col1, col2, col3, col4][i]
340
+ with col:
341
+ if st.button(question, use_container_width=True):
342
+ st.session_state.question_input = question
343
+ st.rerun()
344
+
345
+ # Question input
346
+ question = st.text_area(
347
+ "Enter your Python-related question:",
348
+ value=st.session_state.get("question_input", ""),
349
+ placeholder="Example: What is NumPy? How to create arrays? What are the main functions?",
350
+ height=120,
351
+ key="question_input_area"
352
+ )
353
+
354
+ # Answer button
355
+ col1, col2, col3 = st.columns([2, 1, 1])
356
+
357
+ with col2:
358
+ if st.button("🚀 Get Answer", type="primary", use_container_width=True):
359
+ if not question.strip():
360
+ st.warning("Please enter a question")
361
+ else:
362
+ st.session_state.processing = True
363
+ st.session_state.current_question = question
364
+
365
+ with col3:
366
+ if st.button("🗑️ Clear", use_container_width=True):
367
+ st.session_state.question_input = ""
368
+ st.rerun()
369
+
370
+ st.markdown('</div>', unsafe_allow_html=True)
371
+
372
+ # Process and display answer
373
+ if st.session_state.get("processing") and st.session_state.get("current_question"):
374
+ with st.spinner("🤔 Analyzing document and generating answer..."):
375
+ try:
376
+ answer = answer_question(
377
+ st.session_state.current_question,
378
+ st.session_state.retriever
379
+ )
380
+
381
+ st.markdown("### 📝 Answer")
382
+ st.markdown(f'<div class="answer-card">{answer}</div>', unsafe_allow_html=True)
383
+
384
+ # Reset processing state
385
+ st.session_state.processing = False
386
+
387
+ except Exception as e:
388
+ st.error(f"❌ Error generating answer: {str(e)}")
389
+ st.session_state.processing = False
390
+
391
+ # Quick actions
392
+ st.markdown("""
393
+ <div style="text-align:center; margin-top:30px;">
394
+ <hr style="border:none; height:1px; background:linear-gradient(90deg, transparent, #667eea, transparent); margin:20px 0;">
395
+ <p style="color:#6b7280; font-size:14px;">
396
+ 💡 Tip: Ask specific questions for better answers. Try asking about functions, usage examples, or concepts.
397
+ </p>
398
+ </div>
399
+ """, unsafe_allow_html=True)
400
+
401
+ else:
402
+ # Welcome message when no document is loaded
403
+ st.markdown("""
404
+ <div class="info-box">
405
+ <h3>👋 Welcome to AI Documentation Assistant!</h3>
406
+ <p>Get started by entering a URL to Python documentation above.</p>
407
+ <p><strong>Try the default URL</strong> or enter your own documentation link.</p>
408
+ </div>
409
+
410
+ <div style="background:white; border-radius:15px; padding:30px; margin-top:20px;">
411
+ <h3 style="color:#4f46e5;">✨ Features:</h3>
412
+ <ul style="font-size:16px; line-height:1.8;">
413
+ <li><strong>📚 URL-based</strong> - Just paste a documentation URL</li>
414
+ <li><strong>🤖 AI-powered</strong> - Get intelligent answers from docs</li>
415
+ <li><strong>⚡ Fast processing</strong> - Quick document analysis</li>
416
+ <li><strong>💬 Interactive</strong> - Ask follow-up questions</li>
417
+ <li><strong>🎯 Python-focused</strong> - Optimized for Python documentation</li>
418
+ </ul>
419
+
420
+ <h3 style="color:#4f46e5; margin-top:30px;">🚀 Quick Start:</h3>
421
+ <ol style="font-size:16px; line-height:1.8;">
422
+ <li>Enter a Python documentation URL (or use the default)</li>
423
+ <li>Click <strong>"Load Document"</strong></li>
424
+ <li>Wait a few seconds for processing</li>
425
+ <li>Ask any Python-related question about the documentation</li>
426
+ </ol>
427
+ </div>
428
+ """, unsafe_allow_html=True)
429
+
430
+ st.markdown('</div>', unsafe_allow_html=True)
rag_engine.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # rag_engine.py
2
+
3
+ import os
4
+ import warnings
5
+ from typing import List
6
+ from dotenv import load_dotenv
7
+
8
+ # Suppress warnings
9
+ warnings.filterwarnings('ignore')
10
+
11
+ # Set environment variables before imports
12
+ os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
13
+ os.environ["USER_AGENT"] = "PythonicAI/1.0 (+adinafraz01@gmail.com)"
14
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
15
+
16
+ # Load env variables
17
+ load_dotenv()
18
+ openai_key = os.getenv("OPENAI_API_KEY")
19
+
20
+ if not openai_key:
21
+ raise ValueError("OPENAI_API_KEY not found in environment variables. Please create a .env file with your API key.")
22
+
23
+ os.environ["OPENAI_API_KEY"] = openai_key
24
+
25
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
26
+ from langchain_openai import OpenAIEmbeddings, ChatOpenAI
27
+ from langchain_community.vectorstores import FAISS
28
+ from langchain_community.document_loaders import WebBaseLoader
29
+ from langchain_core.prompts import PromptTemplate
30
+ from langchain.schema import Document
31
+
32
+ # ------------------------------------------
33
+ # DOCUMENT LOADING (URL ONLY)
34
+ # ------------------------------------------
35
+
36
+ def load_user_document(url: str) -> List[Document]:
37
+ """Load URL into LangChain documents."""
38
+ try:
39
+ if not url:
40
+ raise ValueError("No URL provided")
41
+
42
+ # Clean URL
43
+ if not url.startswith(('http://', 'https://')):
44
+ url = f'https://{url}'
45
+
46
+ # Use WebBaseLoader with custom headers
47
+ headers = {
48
+ 'User-Agent': os.environ.get("USER_AGENT", "PythonicAI/1.0")
49
+ }
50
+
51
+ loader = WebBaseLoader(
52
+ web_paths=[url],
53
+ requests_per_second=1,
54
+ requests_kwargs={'headers': headers}
55
+ )
56
+ docs = loader.load()
57
+
58
+ if not docs:
59
+ raise ValueError("No content found in the URL")
60
+
61
+ return docs
62
+
63
+ except Exception as e:
64
+ raise Exception(f"Error loading URL: {str(e)}")
65
+
66
+ # ------------------------------------------
67
+ # BUILD RETRIEVER FROM DOCUMENTS
68
+ # ------------------------------------------
69
+
70
+ def build_retriever_from_docs(docs: List[Document]):
71
+ """Build FAISS vector store from documents."""
72
+ if not docs:
73
+ raise ValueError("No documents provided")
74
+
75
+ # Use the same splitter settings as your example
76
+ splitter = RecursiveCharacterTextSplitter(
77
+ chunk_size=1000,
78
+ chunk_overlap=200
79
+ )
80
+
81
+ chunks = splitter.split_documents(docs)
82
+
83
+ # Use the same embedding model as your example
84
+ embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
85
+
86
+ # Build vector store
87
+ vector_store = FAISS.from_documents(chunks, embeddings)
88
+
89
+ return vector_store.as_retriever(
90
+ search_type="similarity",
91
+ search_kwargs={"k": 6}
92
+ )
93
+
94
+ # ------------------------------------------
95
+ # PYTHON DOMAIN CHECK
96
+ # ------------------------------------------
97
+
98
+ _llm = None
99
+
100
+ def get_llm():
101
+ """Singleton pattern for LLM to avoid re-initialization."""
102
+ global _llm
103
+ if _llm is None:
104
+ _llm = ChatOpenAI(
105
+ model="gpt-4o-mini",
106
+ temperature=0.2,
107
+ max_tokens=1000,
108
+ request_timeout=60
109
+ )
110
+ return _llm
111
+
112
+ def is_python_related(question: str) -> bool:
113
+ """Check if question is Python-related."""
114
+ python_keywords = [
115
+ 'python', 'numpy', 'pandas', 'matplotlib', 'seaborn',
116
+ 'machine learning', 'deep learning', 'tensorflow', 'pytorch',
117
+ 'scikit-learn', 'sklearn', 'dataframe', 'array', 'plot',
118
+ 'import', 'def ', 'class ', 'function', 'method', 'library',
119
+ 'package', 'module', 'pip install', 'conda install', 'jupyter',
120
+ 'data', 'science', 'analysis', 'visualization', 'programming',
121
+ 'code', 'script', 'algorithm', 'model', 'training', 'prediction'
122
+ ]
123
+
124
+ # Quick keyword check first
125
+ question_lower = question.lower()
126
+ for keyword in python_keywords:
127
+ if keyword in question_lower:
128
+ return True
129
+
130
+ return False
131
+
132
+ # ------------------------------------------
133
+ # ANSWER QUESTION
134
+ # ------------------------------------------
135
+
136
+ # Define the prompt template (same as your example)
137
+ prompt = PromptTemplate(
138
+ template="""
139
+ You are a helpful python assistant.
140
+ Answer ONLY from the provided text context.
141
+ Display answer in a beautiful and user understandable format with proper formatting.
142
+ Use bullet points, numbered lists, or code blocks when appropriate.
143
+ If the context is insufficient, just say you don't know.
144
+
145
+ Context: {context}
146
+ Question: {question}
147
+ """,
148
+ input_variables=['context', 'question']
149
+ )
150
+
151
+ def answer_question(question: str, retriever):
152
+ """Answer question using RAG pipeline."""
153
+ # Validate topic
154
+ if not is_python_related(question):
155
+ return "I can only answer Python and data science related questions based on the provided documentation."
156
+
157
+ try:
158
+ # Retrieve relevant documents
159
+ docs = retriever.invoke(question)
160
+
161
+ if not docs:
162
+ return "I couldn't find relevant information in the documentation to answer this question."
163
+
164
+ # Combine context from retrieved documents
165
+ context = "\n\n".join(doc.page_content for doc in docs)
166
+
167
+ # Create final prompt
168
+ final_prompt = prompt.invoke({
169
+ "context": context,
170
+ "question": question
171
+ })
172
+
173
+ # Get answer from LLM
174
+ llm = get_llm()
175
+ answer = llm.invoke(final_prompt)
176
+
177
+ return answer.content
178
+
179
+ except Exception as e:
180
+ return f"Error generating answer: {str(e)}"
requirements.txt CHANGED
@@ -1,3 +1,10 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ langchain
3
+ langchain-community
4
+ langchain-openai
5
+ openai
6
+ python-dotenv
7
+ faiss-cpu
8
+ beautifulsoup4
9
+ requests
10
+ pypdf