MuhammedSheded33 commited on
Commit
2ee953c
·
verified ·
1 Parent(s): d5c10b6

Upload 7 files

Browse files
GenerationEngine.py ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from langchain_pinecone import PineconeVectorStore
3
+
4
+ # from langchain_openai import OpenAI, ChatOpenAI
5
+ # from langchain_google_genai import ChatGoogleGenerativeAI
6
+
7
+ import openai
8
+
9
+ # from langchain.prompts import PromptTemplate
10
+ from langchain_huggingface import HuggingFaceEmbeddings
11
+ from langchain.output_parsers import PydanticOutputParser
12
+ from dotenv import load_dotenv
13
+ import os
14
+
15
+ from pydantic import BaseModel, Field
16
+ from typing import List, Union, Tuple, Any
17
+ from typing_extensions import Literal
18
+
19
+
20
+ # Function to download Hugging Face embeddings
21
+ def download_hugging_face_embeddings():
22
+ embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
23
+ return embeddings
24
+
25
+ # Load environment variables
26
+ def load_env_variables():
27
+ load_dotenv()
28
+ PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY')
29
+ OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
30
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
31
+
32
+ os.environ["PINECONE_API_KEY"] = PINECONE_API_KEY
33
+ os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
34
+ os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY
35
+
36
+ # utils for llm
37
+ def construct_prompt(prompt: str, role: str):
38
+ return{
39
+ "role": role,
40
+ "content": prompt
41
+ }
42
+
43
+
44
+ # Function to initialize Pinecone vector store and retriever
45
+ def initialize_vector_store(embeddings):
46
+ index_name = "yolotest"
47
+ vector_store = PineconeVectorStore.from_existing_index(index_name=index_name, embedding=embeddings)
48
+ # retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 5})
49
+ return vector_store
50
+
51
+ # Pydantic Schemas
52
+ class MetadataSchema(BaseModel):
53
+ page: float = Field(..., description="Page number of the document")
54
+ page_label: str = Field(..., description="Page label of the document")
55
+ total_pages: float = Field(..., description="Total pages in the document")
56
+ source: str = Field(..., description="Source file path of the document")
57
+ score: float = Field(..., description="Similarity score of the retrieved document")
58
+
59
+ class DocumentSchema(BaseModel):
60
+ metadata: MetadataSchema = Field(..., description="Filtered metadata of the document")
61
+ page_content: str = Field(..., description="Content of the document page")
62
+
63
+ class RetrievedDocsSchema(BaseModel):
64
+ documents: List[DocumentSchema]
65
+
66
+
67
+ # Define the individual Question model
68
+ class Question(BaseModel):
69
+ question: str = Field(..., description="The question prompt that the user needs to answer.")
70
+ type: Literal['fill_missing', 'MCQ', 'short_answer'] = Field(..., description="The type of question: fill_missing, MCQ, or short_answer.")
71
+ options: Union[List[str], None] = Field(None, description="The options for the question, used only for MCQ type.")
72
+
73
+ # Define the Questions model that contains a list of Question objects and the total number of questions
74
+ class Questions(BaseModel):
75
+ no_of_questions: int = Field(..., description="The total number of questions generated.")
76
+ questions: List[Question] = Field(..., description="A list of Question objects.")
77
+
78
+ def initialize_prompts(parser):
79
+ system_prompt = """
80
+ You are an AI assistant specializing in question generation. Your role is to analyze the provided query and context to create structured questions that match the given instructions.
81
+
82
+ Follow the specified question type format and ensure clarity, correctness, and alignment with the context.
83
+ """
84
+
85
+ document_prompt = """
86
+ **Query**: {query}
87
+
88
+ **Context**: {context}
89
+
90
+ **Question Type Instructions:**
91
+ - If a **specific question type** is provided, generate questions **only in that type**: {question_type}.
92
+ - Do **not** generate other question types if a type is specified.
93
+ - If 'MCQ', provide at least **3 options** per question.
94
+ - If 'fill_missing', leave a **blank space** for the missing word.
95
+ - If 'short_answer', ensure the **answer is clear** from the context.
96
+ - If no type is specified (or 'general' is selected), generate a **variety** of question types.
97
+
98
+ Ensure that the generated questions align with the query and retrieved context.
99
+ """
100
+
101
+ footer_prompt = f"""
102
+ **Output Format:**
103
+ {parser.get_format_instructions()}
104
+
105
+ **Important:**
106
+ - Ensure all questions match the requested type.
107
+ - Do not generate options unless the type is 'MCQ'.
108
+ - Maintain high accuracy and relevance to the query and context.
109
+ """
110
+
111
+ return system_prompt, document_prompt, footer_prompt
112
+
113
+
114
+ # Initialize components before user query
115
+ embeddings = download_hugging_face_embeddings()
116
+ load_env_variables()
117
+ vector_store = initialize_vector_store(embeddings)
118
+ parser = PydanticOutputParser(pydantic_object=Questions)
119
+ prompt_template = initialize_prompts(parser)
120
+
121
+
122
+ # Function for retrieving with score
123
+ def retrieve_and_format_results(vector_store: Any, query: str, k: int = 5, filter: dict = {}) -> RetrievedDocsSchema:
124
+ """
125
+ Retrieves documents using similarity search and formats them into the Pydantic schema.
126
+
127
+ Args:
128
+ vector_store (Any): The vector store used for retrieval.
129
+ query (str): The search query.
130
+ k (int): Number of documents to retrieve.
131
+ filter (dict): Optional filter for the search.
132
+
133
+ Returns:
134
+ RetrievedDocsSchema: A structured schema containing documents and metadata.
135
+ """
136
+ # Ensure query is valid
137
+ if not query or not isinstance(query, str):
138
+ raise ValueError("Query must be a non-empty string.")
139
+
140
+ # Retrieve documents with similarity scores
141
+ try:
142
+ retrieved_docs = vector_store.similarity_search_with_score(query, k=k, filter=filter)
143
+ except Exception as e:
144
+ raise RuntimeError(f"Error retrieving documents: {e}")
145
+
146
+ # Convert retrieved documents into the Pydantic schema
147
+ documents_list = [
148
+ DocumentSchema(
149
+ metadata=MetadataSchema(
150
+ page=doc.metadata.get("page", 0),
151
+ page_label=doc.metadata.get("page_label", ""),
152
+ total_pages=doc.metadata.get("total_pages", 0),
153
+ source=doc.metadata.get("source", ""),
154
+ score=float(score) if score is not None else 0.0 # Ensure score is a float
155
+ ),
156
+ page_content=doc.page_content.strip() # Trim extra spaces
157
+ )
158
+ for doc, score in retrieved_docs if doc.page_content.strip() # Filter out empty docs
159
+ ]
160
+
161
+ return RetrievedDocsSchema(documents=documents_list)
162
+
163
+ # LLM to be used
164
+ def generate_text_with_schema(prompt: str, response_format, chat_history: list = None, max_output_tokens: int = None, temperature: float = 0):
165
+
166
+ client = openai.OpenAI()
167
+
168
+ # Ensure chat_history is initialized and includes the prompt
169
+ if chat_history is None:
170
+ chat_history = []
171
+
172
+ chat_history.append({"role": "user", "content": prompt}) # Add user message
173
+
174
+ response = client.beta.chat.completions.parse(
175
+ model="gpt-4o-2024-08-06",
176
+ messages=chat_history, # Pass the updated history
177
+ max_tokens=max_output_tokens,
178
+ temperature=temperature,
179
+ response_format=response_format
180
+ )
181
+
182
+ return response.choices[0].message.parsed
183
+
184
+
185
+ # Process and Generate Questions
186
+ def process_and_generate_questions2(vector_store, query: str, parser, response_format, question_type: str = "general",
187
+ k: int = 5, filter: dict = {}, max_output_tokens: int = None, temperature: float = 0):
188
+ """
189
+ Retrieves relevant documents, formats prompts, and generates questions in a structured format.
190
+
191
+ Args:
192
+ vector_store (Any): The vector store used for retrieval.
193
+ query (str): The search query.
194
+ parser: Parser object to INST the retriever formatting.
195
+ response_format: Expected response format for structured output.
196
+ question_type (str): Type of questions to generate ('MCQ', 'fill_missing', 'short_answer', 'general').
197
+ k (int): Number of documents to retrieve.
198
+ filter (dict): Optional filter for retrieval.
199
+ max_output_tokens (int): Maximum number of tokens in the response.
200
+ temperature (float): Temperature setting for response generation.
201
+
202
+ Returns:
203
+ Parsed structured response containing generated questions.
204
+ """
205
+ retrieved_docs_schema = retrieve_and_format_results(vector_store, query, k, filter)
206
+ retrieved_docs = retrieved_docs_schema.documents
207
+ if not retrieved_docs:
208
+ return {"error": "No relevant documents found for the query."}
209
+
210
+ system_prompt, document_prompt_template, footer_prompt = initialize_prompts(parser)
211
+ # print(f"\n\n sys :{system_prompt} ")
212
+
213
+ chat_history = [construct_prompt(prompt=system_prompt, role="system")]
214
+ # print(f"\n\n hist before :{chat_history} ")
215
+
216
+ context = "\n\n".join([doc.page_content for doc in retrieved_docs])
217
+ # print(f"\n\n context :{context} ")
218
+
219
+ try:
220
+ document_prompt = document_prompt_template.format(query=query, context=context, question_type=question_type)
221
+ except KeyError as e:
222
+ raise ValueError(f"Missing format key in document prompt template: {e}")
223
+ # print(f"\n\n document_prompt :{document_prompt} ")
224
+ # print(question_type)
225
+
226
+
227
+ full_prompt = f"\n\n{document_prompt}\n\n{footer_prompt}"
228
+ # print(f"\n\n full_prompt :{full_prompt} ")
229
+
230
+ response = generate_text_with_schema(
231
+ prompt=full_prompt,
232
+ chat_history = chat_history,
233
+ response_format=response_format,
234
+ max_output_tokens=max_output_tokens,
235
+ temperature=temperature
236
+ )
237
+ chat_history.append(construct_prompt(prompt=response, role="assistant"))
238
+ print(f"\n\n hist after:{chat_history} ")
239
+
240
+
241
+ return response, full_prompt, retrieved_docs_schema, chat_history
242
+
243
+
244
+ def main():
245
+ st.set_page_config(layout="wide")
246
+ st.title("A Simple RAG App to Generate Questions in Specific Formats")
247
+
248
+ if 'chat_history' not in st.session_state:
249
+ st.session_state.chat_history = []
250
+
251
+ col1, col2, col3, col4 = st.columns([1, 2, 2, 2])
252
+
253
+ with col1:
254
+ st.subheader("Chat History")
255
+ with st.expander("Show/Hide Chat History", expanded=True):
256
+ for message in st.session_state.chat_history:
257
+ st.markdown(f"**{message['role'].capitalize()}**: {message['content']}")
258
+
259
+ with col2:
260
+ st.subheader("Retrieved Documents")
261
+ retrieved_docs_schema = RetrievedDocsSchema(documents=[])
262
+ for doc in retrieved_docs_schema.documents:
263
+ with st.expander(f"Source: {doc.metadata.source}, Page {doc.metadata.page}/{doc.metadata.total_pages} (Score: {doc.metadata.score:.4f})"):
264
+ st.text_area("Content:", doc.page_content, height=150)
265
+
266
+ with col3:
267
+ st.subheader("Generated Prompt")
268
+ st.write("Final prompt will appear here.")
269
+
270
+ with col4:
271
+ st.subheader("Generated Response")
272
+ st.write("Response will be displayed here.")
273
+
274
+ question_type = st.selectbox("Select Question Type", ["general", "MCQ", "fill_missing", "short_answer"])
275
+ query = st.chat_input("Enter your query: ")
276
+
277
+ if query and question_type:
278
+ with st.spinner('Generating questions...'):
279
+ st.write(f"**Your query:** {query}")
280
+ response, full_prompt, retrieved_docs_schema, chat_history = process_and_generate_questions2(
281
+ vector_store, query, parser, response_format=Questions, question_type=question_type
282
+ )
283
+
284
+ with col3:
285
+ st.write(f"**Final prompt is:** {full_prompt}")
286
+
287
+ with col2:
288
+ for doc in retrieved_docs_schema.documents:
289
+ with st.expander(f"Source: {doc.metadata.source}, Page {doc.metadata.page}/{doc.metadata.total_pages} (Score: {doc.metadata.score:.4f})"):
290
+ st.text_area("Content:", doc.page_content, height=150)
291
+
292
+ with col1:
293
+ st.session_state.chat_history.append({"role": "user", "content": query})
294
+ st.session_state.chat_history.append({"role": "assistant", "content": str(response)})
295
+ st.markdown(f"**Assistant**: {response}")
296
+
297
+ with col4:
298
+ st.write(response)
299
+
300
+ if __name__ == "__main__":
301
+ main()
QuestionGeneration_last_trial.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
app_v1_generation.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ # from src.helper import download_hugging_face_embeddings
4
+
5
+ from langchain_pinecone import PineconeVectorStore
6
+ from langchain_openai import OpenAI, ChatOpenAI
7
+ from langchain_google_genai import ChatGoogleGenerativeAI
8
+ from langchain.chains import create_retrieval_chain
9
+ from langchain.chains.combine_documents import create_stuff_documents_chain
10
+ from langchain_core.prompts import ChatPromptTemplate
11
+ from langchain_huggingface import HuggingFaceEmbeddings
12
+ from src.prompt import *
13
+
14
+ from dotenv import load_dotenv
15
+ import os
16
+
17
+
18
+ #Download the Embeddings from Hugging Face
19
+ def download_hugging_face_embeddings():
20
+ embeddings=HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
21
+ return embeddings
22
+
23
+
24
+ # Load env Variables
25
+ load_dotenv()
26
+
27
+ PINECONE_API_KEY=os.environ.get('PINECONE_API_KEY')
28
+ OPENAI_API_KEY=os.environ.get('OPENAI_API_KEY')
29
+ GOOGLE_API_KEY= os.environ.get("GOOGLE_API_KEY")
30
+
31
+ os.environ["PINECONE_API_KEY"] = PINECONE_API_KEY
32
+ os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
33
+ os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY
34
+ # Embedding model
35
+ embeddings = download_hugging_face_embeddings()
36
+
37
+
38
+ # load exisiting pinecone index
39
+ index_name = "yolotest"
40
+ Vector_store = PineconeVectorStore.from_existing_index(
41
+ index_name=index_name,
42
+ embedding=embeddings
43
+ )
44
+
45
+ # Retriever
46
+ retriever = Vector_store.as_retriever(search_type="similarity", search_kwargs={"k":5})
47
+
48
+ # llm
49
+ # llm = OpenAI(api_key=OPENAI_API_KEY, temperature=0, max_tokens=500)
50
+ # llm = ChatGoogleGenerativeAI(model="gemini-1.5-pro",temperature=0,max_tokens=None,timeout=None)
51
+ llm = ChatOpenAI(api_key=OPENAI_API_KEY, temperature=0, model='gpt-3.5-turbo-0125')
52
+
53
+ # streamlit
54
+ st.title("RAG Application built on Gemini Model")
55
+ query = st.chat_input("Say something: ")
56
+ prompt = query
57
+
58
+
59
+ prompt = ChatPromptTemplate.from_messages(
60
+ [
61
+ ("system", system_prompt),
62
+ ("human", "{input}"),
63
+ ]
64
+ )
65
+
66
+
67
+ if query:
68
+ question_answer_chain = create_stuff_documents_chain(llm, prompt)
69
+ rag_chain = create_retrieval_chain(retriever, question_answer_chain)
70
+
71
+ response = rag_chain.invoke({"input": query})
72
+
73
+ st.write(response["answer"])
app_v2_generation_schema.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from langchain_pinecone import PineconeVectorStore
3
+ from langchain_openai import OpenAI, ChatOpenAI
4
+ from langchain_google_genai import ChatGoogleGenerativeAI
5
+ from langchain.prompts import PromptTemplate
6
+ from langchain_huggingface import HuggingFaceEmbeddings
7
+ from langchain.output_parsers import PydanticOutputParser
8
+ from dotenv import load_dotenv
9
+ import os
10
+ from pydantic import BaseModel, Field
11
+ from typing import List, Union
12
+ from typing_extensions import Literal
13
+
14
+
15
+ # Pydantic Schema
16
+ class Question(BaseModel):
17
+ question: str = Field(..., description="The question prompt that the user needs to answer.")
18
+ type: Literal['fill_missing', 'MCQ', 'short_answer'] = Field(..., description="The type of question: fill_missing, MCQ, or short_answer.")
19
+ options: Union[List[str], None] = Field(None, description="The options for the question, used only for MCQ type.")
20
+
21
+ class Questions(BaseModel):
22
+ no_of_questions: int = Field(..., description="The total number of questions generated.")
23
+ questions: List[Question] = Field(..., description="A list of Question objects.")
24
+
25
+
26
+ # Function to download Hugging Face embeddings
27
+ def download_hugging_face_embeddings():
28
+ embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
29
+ return embeddings
30
+
31
+
32
+ # Load environment variables
33
+ def load_env_variables():
34
+ load_dotenv()
35
+ PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY')
36
+ OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
37
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
38
+
39
+ os.environ["PINECONE_API_KEY"] = PINECONE_API_KEY
40
+ os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
41
+ os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY
42
+
43
+
44
+ # Function to initialize Pinecone vector store and retriever
45
+ def initialize_vector_store(embeddings):
46
+ index_name = "yolotest"
47
+ vector_store = PineconeVectorStore.from_existing_index(index_name=index_name, embedding=embeddings)
48
+ retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 5})
49
+ return retriever
50
+
51
+
52
+ # Function to initialize LLM
53
+ def initialize_llm():
54
+ llm = ChatOpenAI(api_key=os.environ.get("OPENAI_API_KEY"), temperature=0, model='gpt-3.5-turbo-0125')
55
+ llm_structured = llm.with_structured_output(Questions)
56
+ return llm_structured
57
+
58
+
59
+ # Function to initialize prompt template
60
+ def initialize_prompt_template(parser):
61
+ prompt_template = """
62
+ You are given some context below. Based on the context, generate questions with one of the following types: ['fill_missing', 'MCQ', 'short_answer'].
63
+
64
+ Context: {context}
65
+
66
+ Make sure to generate a variety of questions. The types should be distributed among 'fill_missing', 'MCQ', and 'short_answer'.
67
+
68
+ For MCQs, provide at least 3 options. For 'fill_missing', make sure to leave a gap that can be filled. For 'short_answer', make sure the answer is clear from the context.
69
+
70
+ Return a list of questions, with each question having the following structure:
71
+ - 'question': The question prompt.
72
+ - 'type': The type of question: 'fill_missing', 'MCQ', or 'short_answer'.
73
+ - 'options': For 'MCQ', a list of options. For other types, this field should be omitted.
74
+
75
+ The response should be with a format that matches the following structure:
76
+ {format_instructions}
77
+ """
78
+ return prompt_template
79
+
80
+
81
+ # Initialize components before user query
82
+ embeddings = download_hugging_face_embeddings()
83
+ load_env_variables()
84
+ retriever = initialize_vector_store(embeddings)
85
+ llm_structured = initialize_llm()
86
+ parser = PydanticOutputParser(pydantic_object=Questions)
87
+ prompt_template = initialize_prompt_template(parser)
88
+
89
+
90
+ # Function to generate questions from the context
91
+ def generate_questions_from_context(query: str, retriever, llm_structured, prompt_template, parser):
92
+ # Retrieve relevant documents from the vector store using the retriever
93
+ retrieved_docs = retriever.invoke(query)
94
+ retrieved_docs = [rec.page_content for rec in retrieved_docs]
95
+
96
+ # Combine the retrieved documents into a single context string
97
+ context = " ".join([doc for doc in retrieved_docs])
98
+
99
+ # Initialize prompt template
100
+ prompt = PromptTemplate(template=prompt_template, input_variables=["context"],
101
+ partial_variables={"format_instructions": parser.get_format_instructions()})
102
+
103
+ # Create chain and generate response
104
+ chain = prompt | llm_structured
105
+ response = chain.invoke({
106
+ "context": context, "format_instructions": parser.get_format_instructions()
107
+ })
108
+ return response
109
+
110
+
111
+ # Streamlit Interface
112
+ def main():
113
+ st.title("A Simple RAG App to Generate Questions in Specific Formats")
114
+
115
+ # Display user query input
116
+ query = st.chat_input("Say something: ")
117
+ if query:
118
+ with st.spinner('Generating questions...'):
119
+ st.write(f"Your query: {query}") # Display user query
120
+
121
+ # Generate questions
122
+ result = generate_questions_from_context(query, retriever, llm_structured, prompt_template, parser)
123
+ st.write(result)
124
+ # # Display the result in a more readable format
125
+ # st.subheader("Generated Questions")
126
+ # for i, question in enumerate(result.questions):
127
+ # st.markdown(f"### Question {i + 1}: {question.question}")
128
+ # st.markdown(f"**Type**: {question.type}")
129
+ # if question.type == "MCQ" and question.options:
130
+ # st.markdown("**Options**:")
131
+ # for option in question.options:
132
+ # st.markdown(f"- {option}")
133
+ # st.markdown("---")
134
+
135
+
136
+ # Run the app
137
+ if __name__ == "__main__":
138
+ main()
app_v3_schema_history_QTypes.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from langchain_pinecone import PineconeVectorStore
3
+ from langchain_openai import OpenAI, ChatOpenAI
4
+ from langchain_google_genai import ChatGoogleGenerativeAI
5
+ from langchain.prompts import PromptTemplate
6
+ from langchain_huggingface import HuggingFaceEmbeddings
7
+ from langchain.output_parsers import PydanticOutputParser
8
+ from dotenv import load_dotenv
9
+ import os
10
+ from pydantic import BaseModel, Field
11
+ from typing import List, Union
12
+ from typing_extensions import Literal
13
+
14
+
15
+ # Pydantic Schema
16
+ class Question(BaseModel):
17
+ question: str = Field(..., description="The question prompt that the user needs to answer.")
18
+ type: Literal['fill_missing', 'MCQ', 'short_answer'] = Field(..., description="The type of question: fill_missing, MCQ, or short_answer.")
19
+ options: Union[List[str], None] = Field(None, description="The options for the question, used only for MCQ type.")
20
+
21
+ class Questions(BaseModel):
22
+ no_of_questions: int = Field(..., description="The total number of questions generated.")
23
+ questions: List[Question] = Field(..., description="A list of Question objects.")
24
+
25
+
26
+ # Function to download Hugging Face embeddings
27
+ def download_hugging_face_embeddings():
28
+ embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
29
+ return embeddings
30
+
31
+
32
+ # Load environment variables
33
+ def load_env_variables():
34
+ load_dotenv()
35
+ PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY')
36
+ OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
37
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
38
+
39
+ os.environ["PINECONE_API_KEY"] = PINECONE_API_KEY
40
+ os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
41
+ os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY
42
+
43
+
44
+ # Function to initialize Pinecone vector store and retriever
45
+ def initialize_vector_store(embeddings):
46
+ index_name = "yolotest"
47
+ vector_store = PineconeVectorStore.from_existing_index(index_name=index_name, embedding=embeddings)
48
+ retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 5})
49
+ return retriever
50
+
51
+
52
+ # Function to initialize LLM
53
+ def initialize_llm():
54
+ llm = ChatOpenAI(api_key=os.environ.get("OPENAI_API_KEY"), temperature=0, model='gpt-3.5-turbo-0125')
55
+ llm_structured = llm.with_structured_output(Questions)
56
+ return llm_structured
57
+
58
+
59
+ # Function to initialize prompt template
60
+ def initialize_prompt_template(parser):
61
+ prompt_template = """
62
+ You are given some context below. Based on the context, generate questions.
63
+
64
+ Context: {context}
65
+
66
+ - If a question type is specified, generate questions **only in that type**: {question_type}.
67
+ - If 'MCQ', provide at least 3 options per question.
68
+ - If 'fill_missing', leave a blank space for the missing word.
69
+ - If 'short_answer', ensure the answer is clear from the context.
70
+ - If no type is specified (or 'general' is selected), generate a variety of question types.
71
+
72
+ **Ensure that the generated questions match the requested type.**
73
+
74
+ The response should follow this structure:
75
+
76
+ - 'question': The question prompt.
77
+ - 'type': The type of question (should match the requested type, unless 'general').
78
+ - 'options': For 'MCQ', a list of answer options (otherwise, omit this field).
79
+
80
+ The response should strictly match this format:
81
+ {format_instructions}
82
+ """
83
+ return prompt_template
84
+
85
+
86
+
87
+
88
+ # Initialize components before user query
89
+ embeddings = download_hugging_face_embeddings()
90
+ load_env_variables()
91
+ retriever = initialize_vector_store(embeddings)
92
+ llm_structured = initialize_llm()
93
+ parser = PydanticOutputParser(pydantic_object=Questions)
94
+ prompt_template = initialize_prompt_template(parser)
95
+
96
+ # Function to generate questions from the context
97
+ def generate_questions_from_context(query: str, retriever, llm_structured, prompt_template, parser, chat_history, question_type=None):
98
+ # Default to 'general' if no question type is specified
99
+ if question_type is None:
100
+ question_type = "general"
101
+
102
+ # Append user query to chat history
103
+ chat_history.append({"role": "user", "content": query})
104
+
105
+ # # If history is too long, summarize it
106
+ # if len(chat_history) > 5:
107
+ # chat_history = summarize_chat_history(chat_history, llm_structured)
108
+
109
+ # Retrieve relevant documents from the vector store using the retriever
110
+ retrieved_docs = retriever.invoke(query)
111
+ retrieved_docs = [rec.page_content for rec in retrieved_docs]
112
+
113
+ # Combine retrieved documents into a single context string
114
+ context = " ".join([doc for doc in retrieved_docs] + [entry['content'] for entry in chat_history])
115
+
116
+ # Initialize prompt template with the user-specified or default question type
117
+ prompt = PromptTemplate(
118
+ template=prompt_template,
119
+ input_variables=["context", "question_type"],
120
+ partial_variables={"format_instructions": parser.get_format_instructions()}
121
+ )
122
+
123
+ # Create chain and generate response
124
+ chain = prompt | llm_structured
125
+ response = chain.invoke({
126
+ "context": context,
127
+ "question_type": question_type,
128
+ "format_instructions": parser.get_format_instructions()
129
+ })
130
+
131
+ # Append model response to chat history
132
+ chat_history.append({"role": "assistant", "content": str(response)})
133
+
134
+ return response
135
+
136
+
137
+
138
+
139
+ # Streamlit Interface
140
+ def main():
141
+ st.title("A Simple RAG App to Generate Questions in Specific Formats")
142
+
143
+ # Initialize chat history in session state
144
+ if 'chat_history' not in st.session_state:
145
+ st.session_state.chat_history = []
146
+
147
+ # Sidebar for chat history
148
+ with st.sidebar:
149
+ st.subheader("Chat History")
150
+ with st.expander("Show/Hide Chat History", expanded=False):
151
+ for message in st.session_state.chat_history:
152
+ if message['role'] == 'user':
153
+ st.markdown(f"**User**: {message['content']}")
154
+ else:
155
+ st.markdown(f"**Assistant**: {message['content']}")
156
+
157
+ # Add a dropdown in Streamlit to let the user choose the question type, defaulting to 'general'
158
+ question_type = st.selectbox("Select Question Type", ["general", "MCQ", "fill_missing", "short_answer"])
159
+
160
+ # Get user input
161
+ query = st.chat_input("Say something: ")
162
+
163
+ if query:
164
+ with st.spinner('Generating questions...'):
165
+ st.write(f"Your query: {query}") # Display user query
166
+
167
+ result = generate_questions_from_context(
168
+ query, retriever, llm_structured, prompt_template, parser, st.session_state.chat_history, question_type
169
+ )
170
+ st.write(result)
171
+
172
+
173
+
174
+ # Run the app
175
+ if __name__ == "__main__":
176
+ main()
app_v4.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from langchain_pinecone import PineconeVectorStore
3
+ from langchain_openai import OpenAI, ChatOpenAI
4
+ from langchain_google_genai import ChatGoogleGenerativeAI
5
+ from langchain.prompts import PromptTemplate
6
+ from langchain_huggingface import HuggingFaceEmbeddings
7
+ from langchain.output_parsers import PydanticOutputParser
8
+ from dotenv import load_dotenv
9
+ import os
10
+ from pydantic import BaseModel, Field
11
+ from typing import List, Union
12
+ from typing_extensions import Literal
13
+
14
+
15
+ # Pydantic Schema
16
+
17
+ # Generation Schema
18
+ class Question(BaseModel):
19
+ question: str = Field(..., description="The question prompt that the user needs to answer.")
20
+ type: Literal['fill_missing', 'MCQ', 'short_answer'] = Field(..., description="The type of question: fill_missing, MCQ, or short_answer.")
21
+ options: Union[List[str], None] = Field(None, description="The options for the question, used only for MCQ type.")
22
+
23
+ class Questions(BaseModel):
24
+ no_of_questions: int = Field(..., description="The total number of questions generated.")
25
+ questions: List[Question] = Field(..., description="A list of Question objects.")
26
+
27
+ # Retrieval schema
28
+ class MetadataSchema(BaseModel):
29
+ page: float = Field(..., description="Page number of the document")
30
+ page_label: str = Field(..., description="Page label of the document")
31
+ total_pages: float = Field(..., description="Total pages in the document")
32
+ source: str = Field(..., description="Source file path of the document")
33
+
34
+ class DocumentSchema(BaseModel):
35
+ metadata: MetadataSchema = Field(..., description="Filtered metadata of the document")
36
+ page_content: str = Field(..., description="Content of the document page")
37
+
38
+ class RetrievedDocsSchema(BaseModel):
39
+ documents: List[DocumentSchema]
40
+
41
+
42
+ # Function to download Hugging Face embeddings
43
+ def download_hugging_face_embeddings():
44
+ embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
45
+ return embeddings
46
+
47
+
48
+ # Load environment variables
49
+ def load_env_variables():
50
+ load_dotenv()
51
+ PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY')
52
+ OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
53
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
54
+
55
+ os.environ["PINECONE_API_KEY"] = PINECONE_API_KEY
56
+ os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
57
+ os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY
58
+
59
+
60
+ # Function to initialize Pinecone vector store and retriever
61
+ def initialize_vector_store(embeddings):
62
+ index_name = "yolotest"
63
+ vector_store = PineconeVectorStore.from_existing_index(index_name=index_name, embedding=embeddings)
64
+ retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 5})
65
+ return retriever
66
+
67
+
68
+ # Function to initialize LLM
69
+ def initialize_llm():
70
+ llm = ChatOpenAI(api_key=os.environ.get("OPENAI_API_KEY"), temperature=0, model='gpt-3.5-turbo-0125')
71
+ llm_structured = llm.with_structured_output(Questions)
72
+ return llm_structured
73
+
74
+
75
+ # Function to initialize prompt template
76
+ def initialize_prompt_template(parser):
77
+ prompt_template = """
78
+ You are given some context below. Based on the context, generate questions.
79
+
80
+ Context: {context}
81
+
82
+ - If a question type is specified, generate questions **only in that type**: {question_type}.
83
+ - If a question type is specified, Do not generate any other question type expect for the type mentioned
84
+ - If 'MCQ', provide at least 3 options per question.
85
+ - If 'fill_missing', leave a blank space for the missing word.
86
+ - If 'short_answer', ensure the answer is clear from the context.
87
+ - If no type is specified (or 'general' is selected), generate a variety of question types.
88
+
89
+ **Ensure that the generated questions match the requested type.**
90
+
91
+ The response should follow this structure:
92
+
93
+ - 'question': The question prompt.
94
+ - 'type': The type of question (should match the requested type, unless 'general').
95
+ - 'options': For 'MCQ', a list of answer options (otherwise, omit this field).
96
+
97
+ The response should strictly match this format:
98
+ {format_instructions}
99
+ """
100
+ return prompt_template
101
+
102
+
103
+ # Initialize components before user query
104
+ embeddings = download_hugging_face_embeddings()
105
+ load_env_variables()
106
+ retriever = initialize_vector_store(embeddings)
107
+ llm_structured = initialize_llm()
108
+ parser = PydanticOutputParser(pydantic_object=Questions)
109
+ prompt_template = initialize_prompt_template(parser)
110
+
111
+
112
+ def retrieve_documents(retriever, query: str, k: int = 5) -> RetrievedDocsSchema:
113
+ retrieved_docs = retriever.invoke(query)
114
+
115
+ extracted_docs = [
116
+ DocumentSchema(
117
+ metadata=MetadataSchema(
118
+ page=doc.metadata.get("page", 0.0),
119
+ page_label=doc.metadata.get("page_label", ""),
120
+ total_pages=doc.metadata.get("total_pages", 0.0),
121
+ source=doc.metadata.get("source", "")
122
+ ),
123
+ page_content=doc.page_content
124
+ )
125
+ for doc in retrieved_docs
126
+ ]
127
+
128
+ return RetrievedDocsSchema(documents=extracted_docs)
129
+
130
+
131
+ def generate_questions_from_context(query: str, retriever, llm_structured, prompt_template, parser, chat_history, question_type=None):
132
+ if question_type is None:
133
+ question_type = "general"
134
+
135
+ chat_history.append({"role": "user", "content": query})
136
+ retrieved_docs_schema = retrieve_documents(retriever, query)
137
+ retrieved_docs = [doc.page_content for doc in retrieved_docs_schema.documents]
138
+ context = " ".join(retrieved_docs)
139
+
140
+ prompt = PromptTemplate(
141
+ template=prompt_template,
142
+ input_variables=["context", "question_type"],
143
+ partial_variables={"format_instructions": parser.get_format_instructions()}
144
+ )
145
+
146
+ chain = prompt | llm_structured
147
+ response = chain.invoke({
148
+ "context": context,
149
+ "question_type": question_type,
150
+ "format_instructions": parser.get_format_instructions()
151
+ })
152
+
153
+ chat_history.append({"role": "assistant", "content": str(response)})
154
+ return response, retrieved_docs_schema
155
+
156
+ # Streamlit interface
157
+ def main():
158
+ st.title("A Simple RAG App to Generate Questions in Specific Formats")
159
+
160
+ if 'chat_history' not in st.session_state:
161
+ st.session_state.chat_history = []
162
+
163
+ with st.sidebar:
164
+ st.subheader("Chat History")
165
+ with st.expander("Show/Hide Chat History", expanded=False):
166
+ for message in st.session_state.chat_history:
167
+ st.markdown(f"**{message['role'].capitalize()}**: {message['content']}")
168
+
169
+ question_type = st.selectbox("Select Question Type", ["general", "MCQ", "fill_missing", "short_answer"])
170
+ query = st.chat_input("Enter your query: ")
171
+
172
+ if query and question_type:
173
+ with st.spinner('Generating questions...'):
174
+ st.write(f"**Your query:** {query}")
175
+ response, retrieved_docs_schema = generate_questions_from_context(
176
+ query, retriever, llm_structured, prompt_template, parser, st.session_state.chat_history, question_type
177
+ )
178
+
179
+ st.subheader("Generated Questions")
180
+ st.write(response)
181
+ # st.write(question_type)
182
+ st.subheader("Retrieved Documents")
183
+ for doc in retrieved_docs_schema.documents:
184
+ st.markdown(f"**Source:** {doc.metadata.source}, Page {doc.metadata.page}/{doc.metadata.total_pages}")
185
+ st.text_area("Content:", doc.page_content, height=100)
186
+
187
+ # st.subheader("Chat History")
188
+ # for message in st.session_state.chat_history:
189
+ # st.markdown(f"**{message['role'].capitalize()}**: {message['content']}")
190
+
191
+ if __name__ == "__main__":
192
+ main()
app_v5_scoring.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from langchain_pinecone import PineconeVectorStore
3
+ from langchain_openai import OpenAI, ChatOpenAI
4
+ from langchain_google_genai import ChatGoogleGenerativeAI
5
+ from langchain.prompts import PromptTemplate
6
+ from langchain_huggingface import HuggingFaceEmbeddings
7
+ from langchain.output_parsers import PydanticOutputParser
8
+ from dotenv import load_dotenv
9
+ import os
10
+ from pydantic import BaseModel, Field
11
+ from typing import List, Union, Tuple, Any
12
+ from typing_extensions import Literal
13
+
14
+
15
+ # Pydantic Schema
16
+
17
+ # Generation Schema
18
+ class Question(BaseModel):
19
+ question: str = Field(..., description="The question prompt that the user needs to answer.")
20
+ type: Literal['fill_missing', 'MCQ', 'short_answer'] = Field(..., description="The type of question: fill_missing, MCQ, or short_answer.")
21
+ options: Union[List[str], None] = Field(None, description="The options for the question, used only for MCQ type.")
22
+
23
+ class Questions(BaseModel):
24
+ no_of_questions: int = Field(..., description="The total number of questions generated.")
25
+ questions: List[Question] = Field(..., description="A list of Question objects.")
26
+
27
+ # Retrieval schema
28
+ class MetadataSchema(BaseModel):
29
+ page: float = Field(..., description="Page number of the document")
30
+ page_label: str = Field(..., description="Page label of the document")
31
+ total_pages: float = Field(..., description="Total pages in the document")
32
+ source: str = Field(..., description="Source file path of the document")
33
+ score: float = Field(..., description="Similarity score of the retrieved document")
34
+
35
+ class DocumentSchema(BaseModel):
36
+ metadata: MetadataSchema = Field(..., description="Filtered metadata of the document")
37
+ page_content: str = Field(..., description="Content of the document page")
38
+
39
+ class RetrievedDocsSchema(BaseModel):
40
+ documents: List[DocumentSchema]
41
+
42
+
43
+ # Function to download Hugging Face embeddings
44
+ def download_hugging_face_embeddings():
45
+ embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
46
+ return embeddings
47
+
48
+
49
+ # Load environment variables
50
+ def load_env_variables():
51
+ load_dotenv()
52
+ PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY')
53
+ OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
54
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
55
+
56
+ os.environ["PINECONE_API_KEY"] = PINECONE_API_KEY
57
+ os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
58
+ os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY
59
+
60
+
61
+ # Function to initialize Pinecone vector store and retriever
62
+ def initialize_vector_store(embeddings):
63
+ index_name = "yolotest"
64
+ vector_store = PineconeVectorStore.from_existing_index(index_name=index_name, embedding=embeddings)
65
+ # retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 5})
66
+ return vector_store
67
+
68
+
69
+ # Function to initialize LLM
70
+ def initialize_llm():
71
+ llm = ChatOpenAI(api_key=os.environ.get("OPENAI_API_KEY"), temperature=0, model='gpt-3.5-turbo-0125')
72
+ llm_structured = llm.with_structured_output(Questions)
73
+ return llm_structured
74
+
75
+
76
+ # Function to initialize prompt template
77
+ # Function to initialize prompt template
78
+ def initialize_prompt_template(parser):
79
+ prompt_template = """
80
+ You are given a **query** and relevant **context** below. Based on both, generate questions.
81
+
82
+ **Query**: {query}
83
+
84
+ **Context**: {context}
85
+
86
+ **Question Type Instructions:**
87
+ - If a **specific question type** is provided, generate questions **only in that type**: {question_type}.
88
+ - Do **not** generate other question types if a type is specified.
89
+ - If 'MCQ', provide at least **3 options** per question.
90
+ - If 'fill_missing', leave a **blank space** for the missing word.
91
+ - If 'short_answer', ensure the **answer is clear** from the context.
92
+ - If no type is specified (or 'general' is selected), generate a **variety** of question types.
93
+
94
+ **Ensure the generated questions align with the query and the retrieved context.**
95
+
96
+ The response should be structured in this format:
97
+
98
+ - 'question': The question prompt.
99
+ - 'type': The type of question (should match the requested type, unless 'general').
100
+ - 'options': For 'MCQ', a list of answer options (omit for other types).
101
+
102
+ **Strictly follow this structured format**:
103
+ {format_instructions}
104
+ """
105
+ return prompt_template
106
+
107
+
108
+
109
+ # Initialize components before user query
110
+ embeddings = download_hugging_face_embeddings()
111
+ load_env_variables()
112
+ vector_store = initialize_vector_store(embeddings)
113
+ llm_structured = initialize_llm()
114
+ parser = PydanticOutputParser(pydantic_object=Questions)
115
+ prompt_template = initialize_prompt_template(parser)
116
+
117
+ # Function for retrieving with score
118
+ def retrieve_and_format_results(vector_store: Any, query: str, k: int = 5, filter: dict = {}) -> RetrievedDocsSchema:
119
+ """
120
+ Retrieves documents using similarity search and formats them into the Pydantic schema.
121
+
122
+ Args:
123
+ vector_store (Any): The vector store used for retrieval.
124
+ query (str): The search query.
125
+ k (int): Number of documents to retrieve.
126
+ filter (dict): Optional filter for the search.
127
+
128
+ Returns:
129
+ RetrievedDocsSchema: A structured schema containing documents and metadata.
130
+ """
131
+ # Retrieve documents with similarity scores
132
+ retrieved_docs = vector_store.similarity_search_with_score(query, k=k, filter=filter)
133
+
134
+ # Convert retrieved documents into the Pydantic schema
135
+ documents_list = [
136
+ DocumentSchema(
137
+ metadata=MetadataSchema(
138
+ page=doc.metadata.get("page", 0),
139
+ page_label=doc.metadata.get("page_label", ""),
140
+ total_pages=doc.metadata.get("total_pages", 0),
141
+ source=doc.metadata.get("source", ""),
142
+ score=score # Assign the similarity score
143
+ ),
144
+ page_content=doc.page_content
145
+ )
146
+ for doc, score in retrieved_docs
147
+ ]
148
+
149
+ return RetrievedDocsSchema(documents=documents_list)
150
+
151
+ # llm Generation function
152
+ def generate_questions_from_context(query: str, vector_store: Any, llm_structured, prompt_template: str,
153
+ parser: PydanticOutputParser, chat_history: List[dict], question_type: str = "general") -> Tuple[Any, RetrievedDocsSchema]:
154
+ """
155
+ Generates questions based on retrieved document context using an LLM.
156
+
157
+ Args:
158
+ query (str): The search query.
159
+ vector_store (Any): The vector store used for retrieval.
160
+ llm_structured: The structured LLM output function.
161
+ prompt_template (str): The prompt template for question generation.
162
+ parser (PydanticOutputParser): The Pydantic output parser.
163
+ chat_history (List[dict]): A list to store conversation history.
164
+ question_type (str): The type of question (default is "general").
165
+
166
+ Returns:
167
+ Tuple[Any, RetrievedDocsSchema]: A tuple containing the LLM-generated response and retrieved document schema.
168
+ """
169
+ # Append user query to chat history
170
+ chat_history.append({"role": "user", "content": query})
171
+
172
+ # Retrieve and format results using structured schema
173
+ retrieved_docs_schema = retrieve_and_format_results(vector_store, query, k=5, filter={})
174
+
175
+ # Extract only the page_content from the retrieved documents
176
+ retrieved_docs = [doc.page_content for doc in retrieved_docs_schema.documents]
177
+
178
+ # Combine retrieved documents into a single context string
179
+ context = " ".join(retrieved_docs)
180
+
181
+ # Initialize the prompt with query, context, and format instructions
182
+ prompt = PromptTemplate(
183
+ template=prompt_template,
184
+ input_variables=["query", "context", "question_type"],
185
+ partial_variables={"format_instructions": parser.get_format_instructions()}
186
+ )
187
+
188
+ # Format the prompt with input variables
189
+ formatted_prompt = prompt.format(
190
+ query=query,
191
+ context=context,
192
+ question_type=question_type
193
+ )
194
+
195
+ # Generate response using the LLM
196
+ chain = prompt | llm_structured
197
+ response = chain.invoke({
198
+ "query": query,
199
+ "context": context,
200
+ "question_type": question_type,
201
+ "format_instructions": parser.get_format_instructions()
202
+ })
203
+
204
+
205
+ # Append assistant response to chat history
206
+ chat_history.append({"role": "assistant", "content": str(response)})
207
+
208
+ return response, retrieved_docs_schema, formatted_prompt
209
+
210
+
211
+ # Streamlit interface
212
+ import streamlit as st
213
+
214
+ def main():
215
+ st.title("A Simple RAG App to Generate Questions in Specific Formats")
216
+
217
+ # Initialize chat history in session state
218
+ if 'chat_history' not in st.session_state:
219
+ st.session_state.chat_history = []
220
+
221
+ # Sidebar for Chat History
222
+ with st.sidebar:
223
+ st.subheader("Chat History")
224
+ with st.expander("Show/Hide Chat History", expanded=False):
225
+ for message in st.session_state.chat_history:
226
+ st.markdown(f"**{message['role'].capitalize()}**: {message['content']}")
227
+
228
+ # Dropdown for Question Type Selection
229
+ question_type = st.selectbox("Select Question Type", ["general", "MCQ", "fill_missing", "short_answer"])
230
+
231
+ # Input for Query
232
+ query = st.chat_input("Enter your query: ")
233
+
234
+ if query and question_type:
235
+ with st.spinner('Generating questions...'):
236
+ st.write(f"**Your query:** {query}")
237
+
238
+ # Call the updated function that now returns the generated prompt as well
239
+ response, retrieved_docs_schema, generated_prompt = generate_questions_from_context(
240
+ query, vector_store, llm_structured, prompt_template, parser, st.session_state.chat_history, question_type
241
+ )
242
+
243
+ # Display Generated Prompt
244
+ st.subheader("Generated Prompt")
245
+ # st.code(generated_prompt, language="plaintext")
246
+ st.write(f"**Final prompt is:** {generated_prompt}")
247
+
248
+ # Display Generated Questions
249
+ st.subheader("Generated Questions")
250
+ st.write(response) # Displaying as structured JSON for clarity
251
+
252
+ # Display Retrieved Documents with Scores
253
+ st.subheader("Retrieved Documents")
254
+ for doc in retrieved_docs_schema.documents:
255
+ with st.expander(f"Source: {doc.metadata.source}, Page {doc.metadata.page}/{doc.metadata.total_pages} (Score: {doc.metadata.score:.4f})"):
256
+ st.text_area("Content:", doc.page_content, height=150)
257
+
258
+ if __name__ == "__main__":
259
+ main()
260
+