Tesneem commited on
Commit
0354172
·
verified ·
1 Parent(s): 7da5413

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +465 -439
app.py CHANGED
@@ -1,439 +1,465 @@
1
- #############################################################################################################################
2
- # Filename : app.py
3
- # Description: A Streamlit application to showcase how RAG works.
4
- # Author : Georgios Ioannou
5
- #
6
- # Copyright © 2024 by Georgios Ioannou
7
- #############################################################################################################################
8
- # Import libraries.
9
- import os
10
- import streamlit as st
11
-
12
- from dotenv import load_dotenv, find_dotenv
13
- from huggingface_hub import InferenceClient
14
- from langchain.prompts import PromptTemplate
15
- from langchain.schema import Document
16
- from langchain.schema.runnable import RunnablePassthrough, RunnableLambda
17
- from langchain_community.embeddings import HuggingFaceInferenceAPIEmbeddings
18
- from langchain_community.vectorstores import MongoDBAtlasVectorSearch
19
- from pymongo import MongoClient
20
- from pymongo.collection import Collection
21
- from typing import Dict, Any
22
-
23
-
24
- #############################################################################################################################
25
-
26
-
27
- class RAGQuestionAnswering:
28
- def __init__(self):
29
- """
30
- Parameters
31
- ----------
32
- None
33
-
34
- Output
35
- ------
36
- None
37
-
38
- Purpose
39
- -------
40
- Initializes the RAG Question Answering system by setting up configuration
41
- and loading environment variables.
42
-
43
- Assumptions
44
- -----------
45
- - Expects .env file with MONGO_URI and HF_TOKEN
46
- - Requires proper MongoDB setup with vector search index
47
- - Needs connection to Hugging Face API
48
-
49
- Notes
50
- -----
51
- This is the main class that handles all RAG operations
52
- """
53
- self.load_environment()
54
- self.setup_mongodb()
55
- self.setup_embedding_model()
56
- self.setup_vector_search()
57
- self.setup_rag_chain()
58
-
59
- def load_environment(self) -> None:
60
- """
61
- Parameters
62
- ----------
63
- None
64
-
65
- Output
66
- ------
67
- None
68
-
69
- Purpose
70
- -------
71
- Loads environment variables from .env file and sets up configuration constants.
72
-
73
- Assumptions
74
- -----------
75
- Expects a .env file with MONGO_URI and HF_TOKEN defined
76
-
77
- Notes
78
- -----
79
- Will stop the application if required environment variables are missing
80
- """
81
-
82
- load_dotenv(find_dotenv())
83
- self.MONGO_URI = os.getenv("MONGO_URI")
84
- self.HF_TOKEN = os.getenv("HF_TOKEN")
85
-
86
- if not self.MONGO_URI or not self.HF_TOKEN:
87
- st.error("Please ensure MONGO_URI and HF_TOKEN are set in your .env file")
88
- st.stop()
89
-
90
- # MongoDB configuration.
91
- self.DB_NAME = "txts"
92
- self.COLLECTION_NAME = "txts_collection"
93
- self.VECTOR_SEARCH_INDEX = "vector_index"
94
-
95
- def setup_mongodb(self) -> None:
96
- """
97
- Parameters
98
- ----------
99
- None
100
-
101
- Output
102
- ------
103
- None
104
-
105
- Purpose
106
- -------
107
- Initializes the MongoDB connection and sets up the collection.
108
-
109
- Assumptions
110
- -----------
111
- - Valid MongoDB URI is available
112
- - Database and collection exist in MongoDB Atlas
113
-
114
- Notes
115
- -----
116
- Uses st.cache_resource for efficient connection management
117
- """
118
-
119
- @st.cache_resource
120
- def init_mongodb() -> Collection:
121
- cluster = MongoClient(self.MONGO_URI)
122
- return cluster[self.DB_NAME][self.COLLECTION_NAME]
123
-
124
- self.mongodb_collection = init_mongodb()
125
-
126
- def setup_embedding_model(self) -> None:
127
- """
128
- Parameters
129
- ----------
130
- None
131
-
132
- Output
133
- ------
134
- None
135
-
136
- Purpose
137
- -------
138
- Initializes the embedding model for vector search.
139
-
140
- Assumptions
141
- -----------
142
- - Valid Hugging Face API token
143
- - Internet connection to access the model
144
-
145
- Notes
146
- -----
147
- Uses the all-mpnet-base-v2 model from sentence-transformers
148
- """
149
-
150
- @st.cache_resource
151
- def init_embedding_model() -> HuggingFaceInferenceAPIEmbeddings:
152
- return HuggingFaceInferenceAPIEmbeddings(
153
- api_key=self.HF_TOKEN,
154
- model_name="sentence-transformers/all-mpnet-base-v2",
155
- )
156
-
157
- self.embedding_model = init_embedding_model()
158
-
159
- def setup_vector_search(self) -> None:
160
- """
161
- Parameters
162
- ----------
163
- None
164
-
165
- Output
166
- ------
167
- None
168
-
169
- Purpose
170
- -------
171
- Sets up the vector search functionality using MongoDB Atlas.
172
-
173
- Assumptions
174
- -----------
175
- - MongoDB Atlas vector search index is properly configured
176
- - Valid embedding model is initialized
177
-
178
- Notes
179
- -----
180
- Creates a retriever with similarity search and score threshold
181
- """
182
-
183
- @st.cache_resource
184
- def init_vector_search() -> MongoDBAtlasVectorSearch:
185
- return MongoDBAtlasVectorSearch.from_connection_string(
186
- connection_string=self.MONGO_URI,
187
- namespace=f"{self.DB_NAME}.{self.COLLECTION_NAME}",
188
- embedding=self.embedding_model,
189
- index_name=self.VECTOR_SEARCH_INDEX,
190
- )
191
-
192
- self.vector_search = init_vector_search()
193
- self.retriever = self.vector_search.as_retriever(
194
- search_type="similarity", search_kwargs={"k": 10, "score_threshold": 0.85}
195
- )
196
-
197
- def format_docs(self, docs: list[Document]) -> str:
198
- """
199
- Parameters
200
- ----------
201
- **docs:** list[Document] - List of documents to be formatted
202
-
203
- Output
204
- ------
205
- str: Formatted string containing concatenated document content
206
-
207
- Purpose
208
- -------
209
- Formats the retrieved documents into a single string for processing
210
-
211
- Assumptions
212
- -----------
213
- Documents have page_content attribute
214
-
215
- Notes
216
- -----
217
- Joins documents with double newlines for better readability
218
- """
219
-
220
- return "\n\n".join(doc.page_content for doc in docs)
221
-
222
- def generate_response(self, input_dict: Dict[str, Any]) -> str:
223
- """
224
- Parameters
225
- ----------
226
- **input_dict:** Dict[str, Any] - Dictionary containing context and question
227
-
228
- Output
229
- ------
230
- str: Generated response from the model
231
-
232
- Purpose
233
- -------
234
- Generates a response using the Hugging Face model based on context and question
235
-
236
- Assumptions
237
- -----------
238
- - Valid Hugging Face API token
239
- - Input dictionary contains 'context' and 'question' keys
240
-
241
- Notes
242
- -----
243
- Uses Qwen2.5-1.5B-Instruct model with controlled temperature
244
- """
245
- hf_client = InferenceClient(api_key=self.HF_TOKEN)
246
- formatted_prompt = self.prompt.format(**input_dict)
247
-
248
- response = hf_client.chat.completions.create(
249
- model="Qwen/Qwen2.5-1.5B-Instruct",
250
- messages=[
251
- {"role": "system", "content": formatted_prompt},
252
- {"role": "user", "content": input_dict["question"]},
253
- ],
254
- max_tokens=1000,
255
- temperature=0.2,
256
- )
257
-
258
- return response.choices[0].message.content
259
-
260
- def setup_rag_chain(self) -> None:
261
- """
262
- Parameters
263
- ----------
264
- None
265
-
266
- Output
267
- ------
268
- None
269
-
270
- Purpose
271
- -------
272
- Sets up the RAG chain for processing questions and generating answers
273
-
274
- Assumptions
275
- -----------
276
- Retriever and response generator are properly initialized
277
-
278
- Notes
279
- -----
280
- Creates a chain that combines retrieval and response generation
281
- """
282
-
283
- self.prompt = PromptTemplate.from_template(
284
- """Use the following pieces of context to answer the question at the end.
285
-
286
- START OF CONTEXT:
287
- {context}
288
- END OF CONTEXT:
289
-
290
- START OF QUESTION:
291
- {question}
292
- END OF QUESTION:
293
-
294
- If you do not know the answer, just say that you do not know.
295
- NEVER assume things.
296
- """
297
- )
298
-
299
- self.rag_chain = {
300
- "context": self.retriever | RunnableLambda(self.format_docs),
301
- "question": RunnablePassthrough(),
302
- } | RunnableLambda(self.generate_response)
303
-
304
- def process_question(self, question: str) -> str:
305
- """
306
- Parameters
307
- ----------
308
- **question:** str - The user's question to be answered
309
-
310
- Output
311
- ------
312
- str: The generated answer to the question
313
-
314
- Purpose
315
- -------
316
- Processes a user question through the RAG chain and returns an answer
317
-
318
- Assumptions
319
- -----------
320
- - Question is a non-empty string
321
- - RAG chain is properly initialized
322
-
323
- Notes
324
- -----
325
- Main interface for question-answering functionality
326
- """
327
-
328
- return self.rag_chain.invoke(question)
329
-
330
-
331
- #############################################################################################################################
332
- def setup_streamlit_ui() -> None:
333
- """
334
- Parameters
335
- ----------
336
- None
337
-
338
- Output
339
- ------
340
- None
341
-
342
- Purpose
343
- -------
344
- Sets up the Streamlit user interface with proper styling and layout
345
-
346
- Assumptions
347
- -----------
348
- - CSS file exists at ./static/styles/style.css
349
- - Image file exists at ./static/images/ctp.png
350
-
351
- Notes
352
- -----
353
- Handles all UI-related setup and styling
354
- """
355
-
356
- st.set_page_config(page_title="RAG Question Answering", page_icon="🤖")
357
-
358
- # Load CSS.
359
- with open("./static/styles/style.css") as f:
360
- st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
361
-
362
- # Title and subtitles.
363
- st.markdown(
364
- '<h1 align="center" style="font-family: monospace; font-size: 2.1rem; margin-top: -4rem">RAG Question Answering</h1>',
365
- unsafe_allow_html=True,
366
- )
367
- st.markdown(
368
- '<h3 align="center" style="font-family: monospace; font-size: 1.5rem; margin-top: -2rem">Using Zoom Closed Captioning From The Lectures</h3>',
369
- unsafe_allow_html=True,
370
- )
371
- st.markdown(
372
- '<h2 align="center" style="font-family: monospace; font-size: 1.5rem; margin-top: 0rem">CUNY Tech Prep Tutorial 5</h2>',
373
- unsafe_allow_html=True,
374
- )
375
-
376
- # Display logo.
377
- left_co, cent_co, last_co = st.columns(3)
378
- with cent_co:
379
- st.image("./static/images/ctp.png")
380
-
381
-
382
- #############################################################################################################################
383
-
384
-
385
- def main():
386
- """
387
- Parameters
388
- ----------
389
- None
390
-
391
- Output
392
- ------
393
- None
394
-
395
- Purpose
396
- -------
397
- Main function that runs the Streamlit application
398
-
399
- Assumptions
400
- -----------
401
- All required environment variables and files are present
402
-
403
- Notes
404
- -----
405
- Entry point for the application
406
- """
407
-
408
- # Setup UI.
409
- setup_streamlit_ui()
410
-
411
- # Initialize RAG system.
412
- rag_system = RAGQuestionAnswering()
413
-
414
- # Create input elements.
415
- query = st.text_input("Question:", key="question_input")
416
-
417
- # Handle submission.
418
- if st.button("Submit", type="primary"):
419
- if query:
420
- with st.spinner("Generating response..."):
421
- response = rag_system.process_question(query)
422
- st.text_area("Answer:", value=response, height=200, disabled=True)
423
- else:
424
- st.warning("Please enter a question.")
425
-
426
- # Add GitHub link.
427
- st.markdown(
428
- """
429
- <p align="center" style="font-family: monospace; color: #FAF9F6; font-size: 1rem;">
430
- <b>Check out our <a href="https://github.com/GeorgiosIoannouCoder/" style="color: #FAF9F6;">GitHub repository</a></b>
431
- </p>
432
- """,
433
- unsafe_allow_html=True,
434
- )
435
-
436
-
437
- #############################################################################################################################
438
- if __name__ == "__main__":
439
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #############################################################################################################################
2
+ # Filename : app.py
3
+ # Description: A Streamlit application to showcase how RAG works.
4
+ # Author : Georgios Ioannou
5
+ #
6
+ # Copyright © 2024 by Georgios Ioannou
7
+ #############################################################################################################################
8
+ # Import libraries.
9
+ import os
10
+ import streamlit as st
11
+
12
+ from dotenv import load_dotenv, find_dotenv
13
+ from huggingface_hub import InferenceClient
14
+ from langchain.prompts import PromptTemplate
15
+ from langchain.schema import Document
16
+ from langchain.schema.runnable import RunnablePassthrough, RunnableLambda
17
+ # from langchain_community.embeddings import HuggingFaceInferenceAPIEmbeddings
18
+ from langchain.embeddings import OpenAIEmbeddings
19
+ from langchain_community.vectorstores import MongoDBAtlasVectorSearch
20
+ from pymongo import MongoClient
21
+ from pymongo.collection import Collection
22
+ from typing import Dict, Any
23
+ from langchain.chat_models import ChatOpenAI
24
+
25
+
26
+
27
+ #############################################################################################################################
28
+
29
+
30
+ class RAGQuestionAnswering:
31
+ def __init__(self):
32
+ """
33
+ Parameters
34
+ ----------
35
+ None
36
+
37
+ Output
38
+ ------
39
+ None
40
+
41
+ Purpose
42
+ -------
43
+ Initializes the RAG Question Answering system by setting up configuration
44
+ and loading environment variables.
45
+
46
+ Assumptions
47
+ -----------
48
+ - Expects .env file with MONGO_URI and HF_TOKEN
49
+ - Requires proper MongoDB setup with vector search index
50
+ - Needs connection to Hugging Face API
51
+
52
+ Notes
53
+ -----
54
+ This is the main class that handles all RAG operations
55
+ """
56
+ self.load_environment()
57
+ self.setup_mongodb()
58
+ self.setup_embedding_model()
59
+ self.setup_vector_search()
60
+ self.setup_rag_chain()
61
+
62
+ def load_environment(self) -> None:
63
+ """
64
+ Parameters
65
+ ----------
66
+ None
67
+
68
+ Output
69
+ ------
70
+ None
71
+
72
+ Purpose
73
+ -------
74
+ Loads environment variables from .env file and sets up configuration constants.
75
+
76
+ Assumptions
77
+ -----------
78
+ Expects a .env file with MONGO_URI and HF_TOKEN defined
79
+
80
+ Notes
81
+ -----
82
+ Will stop the application if required environment variables are missing
83
+ """
84
+
85
+ load_dotenv(find_dotenv())
86
+ self.MONGO_URI = os.getenv("MONGO_URI")
87
+ # self.HF_TOKEN = os.getenv("HF_TOKEN")
88
+ self.OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
89
+
90
+
91
+ if not self.MONGO_URI or not self.OPENAI_API_KEY:
92
+ st.error("Please ensure MONGO_URI and OPENAI_API_KEY are set in your .env file")
93
+ st.stop()
94
+
95
+ # MongoDB configuration.
96
+ self.DB_NAME = "txts"
97
+ self.COLLECTION_NAME = "txts_collection"
98
+ self.VECTOR_SEARCH_INDEX = "vector_index"
99
+
100
+ def setup_mongodb(self) -> None:
101
+ """
102
+ Parameters
103
+ ----------
104
+ None
105
+
106
+ Output
107
+ ------
108
+ None
109
+
110
+ Purpose
111
+ -------
112
+ Initializes the MongoDB connection and sets up the collection.
113
+
114
+ Assumptions
115
+ -----------
116
+ - Valid MongoDB URI is available
117
+ - Database and collection exist in MongoDB Atlas
118
+
119
+ Notes
120
+ -----
121
+ Uses st.cache_resource for efficient connection management
122
+ """
123
+
124
+ @st.cache_resource
125
+ def init_mongodb() -> Collection:
126
+ cluster = MongoClient(self.MONGO_URI)
127
+ return cluster[self.DB_NAME][self.COLLECTION_NAME]
128
+
129
+ self.mongodb_collection = init_mongodb()
130
+
131
+ def setup_embedding_model(self) -> None:
132
+ """
133
+ Parameters
134
+ ----------
135
+ None
136
+
137
+ Output
138
+ ------
139
+ None
140
+
141
+ Purpose
142
+ -------
143
+ Initializes the embedding model for vector search.
144
+
145
+ Assumptions
146
+ -----------
147
+ - Valid Hugging Face API token
148
+ - Internet connection to access the model
149
+
150
+ Notes
151
+ -----
152
+ Uses the all-mpnet-base-v2 model from sentence-transformers
153
+ """
154
+
155
+ # @st.cache_resource
156
+ # def init_embedding_model() -> HuggingFaceInferenceAPIEmbeddings:
157
+ # return HuggingFaceInferenceAPIEmbeddings(
158
+ # api_key=self.HF_TOKEN,
159
+ # model_name="sentence-transformers/all-mpnet-base-v2",
160
+ # )
161
+
162
+ @st.cache_resource
163
+ def init_embedding_model() -> OpenAIEmbeddings:
164
+ return OpenAIEmbeddings(model="text-embedding-3-small", openai_api_key=self.OPENAI_API_KEY)
165
+
166
+ self.embedding_model = init_embedding_model()
167
+
168
+ def setup_vector_search(self) -> None:
169
+ """
170
+ Parameters
171
+ ----------
172
+ None
173
+
174
+ Output
175
+ ------
176
+ None
177
+
178
+ Purpose
179
+ -------
180
+ Sets up the vector search functionality using MongoDB Atlas.
181
+
182
+ Assumptions
183
+ -----------
184
+ - MongoDB Atlas vector search index is properly configured
185
+ - Valid embedding model is initialized
186
+
187
+ Notes
188
+ -----
189
+ Creates a retriever with similarity search and score threshold
190
+ """
191
+
192
+ @st.cache_resource
193
+ def init_vector_search() -> MongoDBAtlasVectorSearch:
194
+ return MongoDBAtlasVectorSearch.from_connection_string(
195
+ connection_string=self.MONGO_URI,
196
+ namespace=f"{self.DB_NAME}.{self.COLLECTION_NAME}",
197
+ embedding=self.embedding_model,
198
+ index_name=self.VECTOR_SEARCH_INDEX,
199
+ )
200
+
201
+ self.vector_search = init_vector_search()
202
+ self.retriever = self.vector_search.as_retriever(
203
+ search_type="similarity", search_kwargs={"k": 10, "score_threshold": 0.85}
204
+ )
205
+
206
+ def format_docs(self, docs: list[Document]) -> str:
207
+ """
208
+ Parameters
209
+ ----------
210
+ **docs:** list[Document] - List of documents to be formatted
211
+
212
+ Output
213
+ ------
214
+ str: Formatted string containing concatenated document content
215
+
216
+ Purpose
217
+ -------
218
+ Formats the retrieved documents into a single string for processing
219
+
220
+ Assumptions
221
+ -----------
222
+ Documents have page_content attribute
223
+
224
+ Notes
225
+ -----
226
+ Joins documents with double newlines for better readability
227
+ """
228
+
229
+ return "\n\n".join(doc.page_content for doc in docs)
230
+
231
+ # def generate_response(self, input_dict: Dict[str, Any]) -> str:
232
+ # """
233
+ # Parameters
234
+ # ----------
235
+ # **input_dict:** Dict[str, Any] - Dictionary containing context and question
236
+
237
+ # Output
238
+ # ------
239
+ # str: Generated response from the model
240
+
241
+ # Purpose
242
+ # -------
243
+ # Generates a response using the Hugging Face model based on context and question
244
+
245
+ # Assumptions
246
+ # -----------
247
+ # - Valid Hugging Face API token
248
+ # - Input dictionary contains 'context' and 'question' keys
249
+
250
+ # Notes
251
+ # -----
252
+ # Uses Qwen2.5-1.5B-Instruct model with controlled temperature
253
+ # """
254
+ # hf_client = InferenceClient(api_key=self.HF_TOKEN)
255
+ # formatted_prompt = self.prompt.format(**input_dict)
256
+
257
+ # response = hf_client.chat.completions.create(
258
+ # model="Qwen/Qwen2.5-1.5B-Instruct",
259
+ # messages=[
260
+ # {"role": "system", "content": formatted_prompt},
261
+ # {"role": "user", "content": input_dict["question"]},
262
+ # ],
263
+ # max_tokens=1000,
264
+ # temperature=0.2,
265
+ # )
266
+
267
+ # return response.choices[0].message.content
268
+ from langchain.chat_models import ChatOpenAI
269
+ from langchain.schema.messages import SystemMessage, HumanMessage
270
+
271
+ def generate_response(self, input_dict: Dict[str, Any]) -> str:
272
+ llm = ChatOpenAI(
273
+ model="gpt-4", # or "gpt-3.5-turbo"
274
+ temperature=0.2,
275
+ openai_api_key=self.OPENAI_API_KEY,
276
+ )
277
+
278
+ messages = [
279
+ SystemMessage(content=self.prompt.format(**input_dict)),
280
+ HumanMessage(content=input_dict["question"]),
281
+ ]
282
+
283
+ return llm(messages).content
284
+
285
+
286
+ def setup_rag_chain(self) -> None:
287
+ """
288
+ Parameters
289
+ ----------
290
+ None
291
+
292
+ Output
293
+ ------
294
+ None
295
+
296
+ Purpose
297
+ -------
298
+ Sets up the RAG chain for processing questions and generating answers
299
+
300
+ Assumptions
301
+ -----------
302
+ Retriever and response generator are properly initialized
303
+
304
+ Notes
305
+ -----
306
+ Creates a chain that combines retrieval and response generation
307
+ """
308
+
309
+ self.prompt = PromptTemplate.from_template(
310
+ """Use the following pieces of context to answer the question at the end.
311
+
312
+ START OF CONTEXT:
313
+ {context}
314
+ END OF CONTEXT:
315
+
316
+ START OF QUESTION:
317
+ {question}
318
+ END OF QUESTION:
319
+
320
+ If you do not know the answer, just say that you do not know.
321
+ NEVER assume things.
322
+ """
323
+ )
324
+
325
+ self.rag_chain = {
326
+ "context": self.retriever | RunnableLambda(self.format_docs),
327
+ "question": RunnablePassthrough(),
328
+ } | RunnableLambda(self.generate_response)
329
+
330
+ def process_question(self, question: str) -> str:
331
+ """
332
+ Parameters
333
+ ----------
334
+ **question:** str - The user's question to be answered
335
+
336
+ Output
337
+ ------
338
+ str: The generated answer to the question
339
+
340
+ Purpose
341
+ -------
342
+ Processes a user question through the RAG chain and returns an answer
343
+
344
+ Assumptions
345
+ -----------
346
+ - Question is a non-empty string
347
+ - RAG chain is properly initialized
348
+
349
+ Notes
350
+ -----
351
+ Main interface for question-answering functionality
352
+ """
353
+
354
+ return self.rag_chain.invoke(question)
355
+
356
+
357
+ #############################################################################################################################
358
+ def setup_streamlit_ui() -> None:
359
+ """
360
+ Parameters
361
+ ----------
362
+ None
363
+
364
+ Output
365
+ ------
366
+ None
367
+
368
+ Purpose
369
+ -------
370
+ Sets up the Streamlit user interface with proper styling and layout
371
+
372
+ Assumptions
373
+ -----------
374
+ - CSS file exists at ./static/styles/style.css
375
+ - Image file exists at ./static/images/ctp.png
376
+
377
+ Notes
378
+ -----
379
+ Handles all UI-related setup and styling
380
+ """
381
+
382
+ st.set_page_config(page_title="RAG Question Answering", page_icon="🤖")
383
+
384
+ # Load CSS.
385
+ with open("./static/styles/style.css") as f:
386
+ st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
387
+
388
+ # Title and subtitles.
389
+ st.markdown(
390
+ '<h1 align="center" style="font-family: monospace; font-size: 2.1rem; margin-top: -4rem">RAG Question Answering</h1>',
391
+ unsafe_allow_html=True,
392
+ )
393
+ st.markdown(
394
+ '<h3 align="center" style="font-family: monospace; font-size: 1.5rem; margin-top: -2rem">Using Zoom Closed Captioning From The Lectures</h3>',
395
+ unsafe_allow_html=True,
396
+ )
397
+ st.markdown(
398
+ '<h2 align="center" style="font-family: monospace; font-size: 1.5rem; margin-top: 0rem">CUNY Tech Prep Tutorial 5</h2>',
399
+ unsafe_allow_html=True,
400
+ )
401
+
402
+ # Display logo.
403
+ left_co, cent_co, last_co = st.columns(3)
404
+ with cent_co:
405
+ st.image("./static/images/ctp.png")
406
+
407
+
408
+ #############################################################################################################################
409
+
410
+
411
+ def main():
412
+ """
413
+ Parameters
414
+ ----------
415
+ None
416
+
417
+ Output
418
+ ------
419
+ None
420
+
421
+ Purpose
422
+ -------
423
+ Main function that runs the Streamlit application
424
+
425
+ Assumptions
426
+ -----------
427
+ All required environment variables and files are present
428
+
429
+ Notes
430
+ -----
431
+ Entry point for the application
432
+ """
433
+
434
+ # Setup UI.
435
+ setup_streamlit_ui()
436
+
437
+ # Initialize RAG system.
438
+ rag_system = RAGQuestionAnswering()
439
+
440
+ # Create input elements.
441
+ query = st.text_input("Question:", key="question_input")
442
+
443
+ # Handle submission.
444
+ if st.button("Submit", type="primary"):
445
+ if query:
446
+ with st.spinner("Generating response..."):
447
+ response = rag_system.process_question(query)
448
+ st.text_area("Answer:", value=response, height=200, disabled=True)
449
+ else:
450
+ st.warning("Please enter a question.")
451
+
452
+ # Add GitHub link.
453
+ st.markdown(
454
+ """
455
+ <p align="center" style="font-family: monospace; color: #FAF9F6; font-size: 1rem;">
456
+ <b>Check out our <a href="https://github.com/GeorgiosIoannouCoder/" style="color: #FAF9F6;">GitHub repository</a></b>
457
+ </p>
458
+ """,
459
+ unsafe_allow_html=True,
460
+ )
461
+
462
+
463
+ #############################################################################################################################
464
+ if __name__ == "__main__":
465
+ main()