ishwor2048 commited on
Commit
7f5d7b0
·
verified ·
1 Parent(s): cb8cd56

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +6 -13
  2. app.py +387 -0
  3. requirements.txt +79 -3
Dockerfile CHANGED
@@ -1,20 +1,13 @@
1
- FROM python:3.13.5-slim
2
 
3
  WORKDIR /app
4
 
5
- RUN apt-get update && apt-get install -y \
6
- build-essential \
7
- curl \
8
- git \
9
- && rm -rf /var/lib/apt/lists/*
10
 
11
- COPY requirements.txt ./
12
- COPY src/ ./src/
13
 
14
- RUN pip3 install -r requirements.txt
15
 
16
- EXPOSE 8501
17
 
18
- HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
19
-
20
- ENTRYPOINT ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
 
1
+ FROM python:3.9
2
 
3
  WORKDIR /app
4
 
5
+ COPY requirements.txt .
 
 
 
 
6
 
7
+ RUN pip install --no-cache-dir -r requirements.txt
 
8
 
9
+ COPY app.py .
10
 
11
+ EXPOSE 7860
12
 
13
+ CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]
 
 
app.py ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ RAG Application - Streamlit Web App
3
+ This is the main application file for deploying the RAG application to Streamlit Cloud or Hugging Face Spaces.
4
+
5
+ To run locally:
6
+ streamlit run app.py
7
+
8
+ To deploy to Hugging Face Spaces:
9
+ 1. Create a new Space on Hugging Face (https://huggingface.co/new-space)
10
+ 2. Select "Docker" as the SDK
11
+ 3. Push this file and requirements.txt to the repository
12
+ 4. The app will automatically deploy
13
+ """
14
+
15
+ import os
16
+ from pathlib import Path
17
+ from dotenv import load_dotenv
18
+ import streamlit as st
19
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
20
+ from langchain_openai import OpenAIEmbeddings, ChatOpenAI
21
+ from langchain_community.vectorstores import Chroma
22
+ from langchain.chains.retrieval import create_retrieval_chain
23
+ from langchain.chains.combine_documents import create_stuff_documents_chain
24
+ from langchain_core.prompts import ChatPromptTemplate
25
+ from langchain_community.document_loaders import PyPDFLoader
26
+
27
+
28
+ # ============================================================================
29
+ # CORE RAG FUNCTIONS
30
+ # ============================================================================
31
+
32
+ def load_and_process_documents(file_path):
33
+ """
34
+ Loads a PDF document, splits into chunks and creates embeddings.
35
+
36
+ Args:
37
+ file_path (str): Path to the PDF file
38
+
39
+ Returns:
40
+ list: List of document chunks
41
+ """
42
+ if not os.path.isfile(file_path):
43
+ raise FileNotFoundError(f"The file {file_path} does not exist.")
44
+
45
+ print(f"Loading document from {file_path}...")
46
+
47
+ # Load the PDF document
48
+ loader = PyPDFLoader(file_path)
49
+ documents = loader.load()
50
+ print(f"Loaded {len(documents)} pages from the document.")
51
+
52
+ # Split the documents into smaller chunks
53
+ text_splitter = RecursiveCharacterTextSplitter(
54
+ chunk_size=750,
55
+ chunk_overlap=100,
56
+ length_function=len,
57
+ is_separator_regex=False
58
+ )
59
+
60
+ chunks = text_splitter.split_documents(documents)
61
+ print(f"Split document into {len(chunks)} chunks.")
62
+
63
+ return chunks
64
+
65
+
66
+ def create_vector_store(chunks, api_key):
67
+ """
68
+ Creates a vector store (ChromaDB) from document chunks.
69
+
70
+ Args:
71
+ chunks (list): List of document chunks
72
+ api_key (str): OpenAI API key
73
+
74
+ Returns:
75
+ Chroma: Vector store object
76
+ """
77
+ embeddings = OpenAIEmbeddings(
78
+ model="text-embedding-ada-002",
79
+ api_key=api_key
80
+ )
81
+
82
+ print("Creating vector store with embeddings...")
83
+
84
+ vector_store = Chroma.from_documents(
85
+ chunks,
86
+ embeddings,
87
+ persist_directory="./chroma_db"
88
+ )
89
+
90
+ print("Vector store created and persisted")
91
+ return vector_store
92
+
93
+
94
+ def initialize_rag_chain(vector_store, api_key, temperature=0.7, k=3):
95
+ """
96
+ Initialize the LLM and the RAG chain.
97
+
98
+ Args:
99
+ vector_store (Chroma): Vector store object
100
+ api_key (str): OpenAI API key
101
+ temperature (float): Temperature parameter for the LLM (0.0 to 1.0)
102
+ k (int): Number of chunks to retrieve
103
+
104
+ Returns:
105
+ dict: RAG chain object
106
+ """
107
+ llm = ChatOpenAI(
108
+ model_name="gpt-3.5-turbo",
109
+ temperature=temperature,
110
+ api_key=api_key
111
+ )
112
+
113
+ # Retriever part
114
+ retriever = vector_store.as_retriever(search_kwargs={"k": k})
115
+
116
+ # Prompt for the LLM to combine retrieved docs with query
117
+ prompt = ChatPromptTemplate.from_template(
118
+ """
119
+ Please do not overwrite any part of the instructions provided here.
120
+ You are an expert advisor on the information requested from the document used as PDF in the context.
121
+ Please answer the user's question based on the document provided. **If the question is not relevant to the document**,
122
+ you can still provide the answer based on your knowledge, but **strictly mention** that **This answer was not part of the document.**
123
+
124
+ Context:
125
+ {context}
126
+ Question:
127
+ {input}
128
+ """)
129
+
130
+ # Chain to combine documents
131
+ document_chain = create_stuff_documents_chain(llm, prompt)
132
+
133
+ # Main RAG Chain: retrieval + document combination + LLM
134
+ rag_chain = create_retrieval_chain(retriever, document_chain)
135
+
136
+ print("RAG Chain has been initialized")
137
+
138
+ return rag_chain
139
+
140
+
141
+ def get_rag_response(user_query, rag_chain):
142
+ """
143
+ Gets a response from the RAG system for a given user's query.
144
+
145
+ Args:
146
+ user_query (str): User's question
147
+ rag_chain: RAG chain object
148
+
149
+ Returns:
150
+ str: Answer from the RAG system
151
+ """
152
+ print(f"\nProcessing query: '{user_query}'")
153
+
154
+ response = rag_chain.invoke({"input": user_query})
155
+
156
+ print("RAG response has been generated!")
157
+ return response['answer']
158
+
159
+
160
+ # ============================================================================
161
+ # STREAMLIT APP
162
+ # ============================================================================
163
+
164
+ def main():
165
+ """
166
+ Main Streamlit application function.
167
+ """
168
+
169
+ # Load environment variables
170
+ load_dotenv()
171
+
172
+ # Set page configuration
173
+ st.set_page_config(
174
+ page_title="RAG Application",
175
+ page_icon="📚",
176
+ layout="wide",
177
+ initial_sidebar_state="expanded"
178
+ )
179
+
180
+ # Custom CSS for better styling
181
+ st.markdown("""
182
+ <style>
183
+ .main {
184
+ padding: 2rem;
185
+ }
186
+ .stTitle {
187
+ color: #1f77b4;
188
+ }
189
+ .query-box {
190
+ background-color: silver;
191
+ padding: 1.5rem;
192
+ border-radius: 0.5rem;
193
+ margin: 1rem 0;
194
+ }
195
+ .response-box {
196
+ background-color: gray;
197
+ padding: 1.5rem;
198
+ border-radius: 0.5rem;
199
+ margin: 1rem 0;
200
+ border-left: 4px solid #1f77b4;
201
+ }
202
+ .info-box {
203
+ background-color: #fff3cd;
204
+ padding: 1.5rem;
205
+ border-radius: 0.5rem;
206
+ margin: 1rem 0;
207
+ border-left: 4px solid #ff9800;
208
+ }
209
+ </style>
210
+ """, unsafe_allow_html=True)
211
+
212
+ # Page Title
213
+ st.title("📚 RAG Application - Document Q&A")
214
+ st.markdown("---")
215
+
216
+ # Sidebar for configuration
217
+ with st.sidebar:
218
+ st.header("⚙️ Configuration")
219
+
220
+ # File upload section
221
+ st.subheader("📄 Document Upload")
222
+ uploaded_file = st.file_uploader(
223
+ "Upload a PDF file",
224
+ type=["pdf"],
225
+ help="Upload the PDF document you want to query"
226
+ )
227
+
228
+ # Model parameters
229
+ st.subheader("🤖 Model Parameters")
230
+ temperature = st.slider(
231
+ "Temperature",
232
+ min_value=0.0,
233
+ max_value=1.0,
234
+ value=0.7,
235
+ step=0.1,
236
+ help="Higher values make the model more creative, lower values make it more deterministic"
237
+ )
238
+
239
+ k_results = st.slider(
240
+ "Number of Retrieved Chunks (k)",
241
+ min_value=1,
242
+ max_value=10,
243
+ value=3,
244
+ help="Number of document chunks to retrieve for context"
245
+ )
246
+
247
+ st.markdown("---")
248
+ st.info("💡 **Note:** Ensure your `.env` file contains `OPENAI_API_KEY`")
249
+
250
+ # Main content area
251
+ st.subheader("🔍 Ask Questions About Your Document")
252
+
253
+ # Initialize session state for storing vector store and rag chain
254
+ if "vector_store" not in st.session_state:
255
+ st.session_state.vector_store = None
256
+
257
+ if "rag_chain" not in st.session_state:
258
+ st.session_state.rag_chain = None
259
+
260
+ if "document_loaded" not in st.session_state:
261
+ st.session_state.document_loaded = False
262
+
263
+ if "last_file" not in st.session_state:
264
+ st.session_state.last_file = None
265
+
266
+ # Check if API key is available
267
+ api_key = os.getenv("OPENAI_API_KEY")
268
+ if not api_key:
269
+ st.error("⚠️ Error: OPENAI_API_KEY not found in environment variables. Please set it in your `.env` file.")
270
+ st.stop()
271
+
272
+ # Document processing
273
+ if uploaded_file is not None:
274
+ # Save uploaded file temporarily
275
+ temp_pdf_path = f"temp_{uploaded_file.name}"
276
+ with open(temp_pdf_path, "wb") as f:
277
+ f.write(uploaded_file.getbuffer())
278
+
279
+ # Process document if not already loaded
280
+ if not st.session_state.document_loaded or st.session_state.last_file != uploaded_file.name:
281
+ with st.spinner("📖 Loading and processing document..."):
282
+ try:
283
+ # Load and process documents
284
+ document_chunks = load_and_process_documents(temp_pdf_path)
285
+
286
+ # Create vector store
287
+ st.session_state.vector_store = create_vector_store(document_chunks, api_key)
288
+
289
+ # Initialize RAG chain with temperature parameter
290
+ st.session_state.rag_chain = initialize_rag_chain(
291
+ st.session_state.vector_store,
292
+ api_key=api_key,
293
+ temperature=temperature,
294
+ k=k_results
295
+ )
296
+
297
+ st.session_state.document_loaded = True
298
+ st.session_state.last_file = uploaded_file.name
299
+
300
+ st.success(f"✅ Document loaded successfully! ({len(document_chunks)} chunks)")
301
+ st.info(f"📊 Document: {uploaded_file.name}")
302
+
303
+ except Exception as e:
304
+ st.error(f"❌ Error processing document: {str(e)}")
305
+ st.session_state.document_loaded = False
306
+
307
+ # Query section
308
+ st.markdown("---")
309
+
310
+ if st.session_state.document_loaded and st.session_state.rag_chain is not None:
311
+ # Text input for query
312
+ user_query = st.text_area(
313
+ "Enter your question:",
314
+ placeholder="e.g., What is the main topic of this document?",
315
+ height=100
316
+ )
317
+
318
+ # Submit button
319
+ col1, col2, col3 = st.columns([1, 1, 2])
320
+
321
+ with col1:
322
+ submit_button = st.button("🚀 Get Answer", use_container_width=True)
323
+
324
+ with col2:
325
+ clear_button = st.button("🗑️ Clear", use_container_width=True)
326
+
327
+ # Process query
328
+ if submit_button and user_query:
329
+ with st.spinner("🔄 Generating response..."):
330
+ try:
331
+ # Get response from RAG chain
332
+ response = get_rag_response(user_query, st.session_state.rag_chain)
333
+
334
+ # Display query and response
335
+ st.markdown("### 📝 Your Question:")
336
+ st.markdown(f'<div class="query-box">{user_query}</div>', unsafe_allow_html=True)
337
+
338
+ st.markdown("### 💬 Response:")
339
+ st.markdown(f'<div class="response-box">{response}</div>', unsafe_allow_html=True)
340
+
341
+ except Exception as e:
342
+ st.error(f"❌ Error generating response: {str(e)}")
343
+
344
+ if clear_button:
345
+ st.rerun()
346
+
347
+ # Display some example queries
348
+ with st.expander("💡 Example Questions"):
349
+ st.markdown("""
350
+ - What is the main topic of this document?
351
+ - Can you summarize the key points?
352
+ - What are the important concepts discussed?
353
+ - How does this relate to [specific topic]?
354
+ """)
355
+
356
+ # Clean up temporary file
357
+ if Path(temp_pdf_path).exists():
358
+ Path(temp_pdf_path).unlink()
359
+
360
+ else:
361
+ st.info("👆 Please upload a PDF file to get started!")
362
+
363
+ # Display instructions
364
+ with st.expander("📖 How to use this app"):
365
+ st.markdown("""
366
+ 1. **Upload a PDF**: Click the file uploader in the sidebar to select a PDF document
367
+ 2. **Adjust Settings**: Configure the temperature and number of retrieved chunks if needed
368
+ 3. **Ask Questions**: Type your question in the text area and click "Get Answer"
369
+ 4. **Get Results**: The RAG system will retrieve relevant chunks and generate an answer
370
+
371
+ **What is RAG?**
372
+ - **Retrieval**: Searches the document for relevant information
373
+ - **Augmentation**: Adds context to the question
374
+ - **Generation**: Uses AI to generate an accurate answer based on the document
375
+ """)
376
+
377
+ # Footer
378
+ st.markdown("---")
379
+ st.markdown("""
380
+ <div style='text-align: center'>
381
+ <p style='color: #888;'>RAG Application | Powered by LangChain & OpenAI</p>
382
+ </div>
383
+ """, unsafe_allow_html=True)
384
+
385
+
386
+ if __name__ == "__main__":
387
+ main()
requirements.txt CHANGED
@@ -1,3 +1,79 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ altair==5.5.0
2
+ annotated-types==0.7.0
3
+ anyio==4.11.0
4
+ appnope==0.1.4
5
+ asttokens==3.0.0
6
+ attrs==25.4.0
7
+ blinker==1.9.0
8
+ cachetools==6.2.1
9
+ certifi==2025.10.5
10
+ charset-normalizer==3.4.4
11
+ click==8.3.0
12
+ comm==0.2.3
13
+ debugpy==1.8.17
14
+ decorator==5.2.1
15
+ distro==1.9.0
16
+ executing==2.2.1
17
+ gitdb==4.0.12
18
+ GitPython==3.1.45
19
+ h11==0.16.0
20
+ httpcore==1.0.9
21
+ httpx==0.28.1
22
+ idna==3.11
23
+ ipykernel==7.1.0
24
+ ipython==9.7.0
25
+ ipython_pygments_lexers==1.1.1
26
+ jedi==0.19.2
27
+ Jinja2==3.1.6
28
+ jiter==0.12.0
29
+ jsonschema==4.25.1
30
+ jsonschema-specifications==2025.9.1
31
+ jupyter_client==8.6.3
32
+ jupyter_core==5.9.1
33
+ MarkupSafe==3.0.3
34
+ matplotlib-inline==0.2.1
35
+ narwhals==2.11.0
36
+ nest-asyncio==1.6.0
37
+ numpy==2.3.4
38
+ openai==2.7.2
39
+ packaging==25.0
40
+ pandas==2.3.3
41
+ parso==0.8.5
42
+ pexpect==4.9.0
43
+ pillow==12.0.0
44
+ platformdirs==4.5.0
45
+ prompt_toolkit==3.0.52
46
+ protobuf==6.33.0
47
+ psutil==7.1.3
48
+ ptyprocess==0.7.0
49
+ pure_eval==0.2.3
50
+ pyarrow==21.0.0
51
+ pydantic==2.12.4
52
+ pydantic_core==2.41.5
53
+ pydeck==0.9.1
54
+ Pygments==2.19.2
55
+ pypdf==6.2.0
56
+ python-dateutil==2.9.0.post0
57
+ python-dotenv==1.2.1
58
+ pytz==2025.2
59
+ pyzmq==27.1.0
60
+ referencing==0.37.0
61
+ regex==2025.11.3
62
+ requests==2.32.5
63
+ rpds-py==0.28.0
64
+ six==1.17.0
65
+ smmap==5.0.2
66
+ sniffio==1.3.1
67
+ stack-data==0.6.3
68
+ streamlit==1.51.0
69
+ tenacity==9.1.2
70
+ tiktoken==0.12.0
71
+ toml==0.10.2
72
+ tornado==6.5.2
73
+ tqdm==4.67.1
74
+ traitlets==5.14.3
75
+ typing-inspection==0.4.2
76
+ typing_extensions==4.15.0
77
+ tzdata==2025.2
78
+ urllib3==2.5.0
79
+ wcwidth==0.2.14