Spaces:
Build error
Build error
File size: 7,872 Bytes
2ee953c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | import streamlit as st
from langchain_pinecone import PineconeVectorStore
from langchain_openai import OpenAI, ChatOpenAI
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain.prompts import PromptTemplate
from langchain_huggingface import HuggingFaceEmbeddings
from langchain.output_parsers import PydanticOutputParser
from dotenv import load_dotenv
import os
from pydantic import BaseModel, Field
from typing import List, Union
from typing_extensions import Literal
# Pydantic Schema
# Generation Schema
class Question(BaseModel):
question: str = Field(..., description="The question prompt that the user needs to answer.")
type: Literal['fill_missing', 'MCQ', 'short_answer'] = Field(..., description="The type of question: fill_missing, MCQ, or short_answer.")
options: Union[List[str], None] = Field(None, description="The options for the question, used only for MCQ type.")
class Questions(BaseModel):
no_of_questions: int = Field(..., description="The total number of questions generated.")
questions: List[Question] = Field(..., description="A list of Question objects.")
# Retrieval schema
class MetadataSchema(BaseModel):
page: float = Field(..., description="Page number of the document")
page_label: str = Field(..., description="Page label of the document")
total_pages: float = Field(..., description="Total pages in the document")
source: str = Field(..., description="Source file path of the document")
class DocumentSchema(BaseModel):
metadata: MetadataSchema = Field(..., description="Filtered metadata of the document")
page_content: str = Field(..., description="Content of the document page")
class RetrievedDocsSchema(BaseModel):
documents: List[DocumentSchema]
# Function to download Hugging Face embeddings
def download_hugging_face_embeddings():
embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
return embeddings
# Load environment variables
def load_env_variables():
load_dotenv()
PINECONE_API_KEY = os.environ.get('PINECONE_API_KEY')
OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
os.environ["PINECONE_API_KEY"] = PINECONE_API_KEY
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY
# Function to initialize Pinecone vector store and retriever
def initialize_vector_store(embeddings):
index_name = "yolotest"
vector_store = PineconeVectorStore.from_existing_index(index_name=index_name, embedding=embeddings)
retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 5})
return retriever
# Function to initialize LLM
def initialize_llm():
llm = ChatOpenAI(api_key=os.environ.get("OPENAI_API_KEY"), temperature=0, model='gpt-3.5-turbo-0125')
llm_structured = llm.with_structured_output(Questions)
return llm_structured
# Function to initialize prompt template
def initialize_prompt_template(parser):
prompt_template = """
You are given some context below. Based on the context, generate questions.
Context: {context}
- If a question type is specified, generate questions **only in that type**: {question_type}.
- If a question type is specified, Do not generate any other question type expect for the type mentioned
- If 'MCQ', provide at least 3 options per question.
- If 'fill_missing', leave a blank space for the missing word.
- If 'short_answer', ensure the answer is clear from the context.
- If no type is specified (or 'general' is selected), generate a variety of question types.
**Ensure that the generated questions match the requested type.**
The response should follow this structure:
- 'question': The question prompt.
- 'type': The type of question (should match the requested type, unless 'general').
- 'options': For 'MCQ', a list of answer options (otherwise, omit this field).
The response should strictly match this format:
{format_instructions}
"""
return prompt_template
# Initialize components before user query
embeddings = download_hugging_face_embeddings()
load_env_variables()
retriever = initialize_vector_store(embeddings)
llm_structured = initialize_llm()
parser = PydanticOutputParser(pydantic_object=Questions)
prompt_template = initialize_prompt_template(parser)
def retrieve_documents(retriever, query: str, k: int = 5) -> RetrievedDocsSchema:
retrieved_docs = retriever.invoke(query)
extracted_docs = [
DocumentSchema(
metadata=MetadataSchema(
page=doc.metadata.get("page", 0.0),
page_label=doc.metadata.get("page_label", ""),
total_pages=doc.metadata.get("total_pages", 0.0),
source=doc.metadata.get("source", "")
),
page_content=doc.page_content
)
for doc in retrieved_docs
]
return RetrievedDocsSchema(documents=extracted_docs)
def generate_questions_from_context(query: str, retriever, llm_structured, prompt_template, parser, chat_history, question_type=None):
if question_type is None:
question_type = "general"
chat_history.append({"role": "user", "content": query})
retrieved_docs_schema = retrieve_documents(retriever, query)
retrieved_docs = [doc.page_content for doc in retrieved_docs_schema.documents]
context = " ".join(retrieved_docs)
prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question_type"],
partial_variables={"format_instructions": parser.get_format_instructions()}
)
chain = prompt | llm_structured
response = chain.invoke({
"context": context,
"question_type": question_type,
"format_instructions": parser.get_format_instructions()
})
chat_history.append({"role": "assistant", "content": str(response)})
return response, retrieved_docs_schema
# Streamlit interface
def main():
st.title("A Simple RAG App to Generate Questions in Specific Formats")
if 'chat_history' not in st.session_state:
st.session_state.chat_history = []
with st.sidebar:
st.subheader("Chat History")
with st.expander("Show/Hide Chat History", expanded=False):
for message in st.session_state.chat_history:
st.markdown(f"**{message['role'].capitalize()}**: {message['content']}")
question_type = st.selectbox("Select Question Type", ["general", "MCQ", "fill_missing", "short_answer"])
query = st.chat_input("Enter your query: ")
if query and question_type:
with st.spinner('Generating questions...'):
st.write(f"**Your query:** {query}")
response, retrieved_docs_schema = generate_questions_from_context(
query, retriever, llm_structured, prompt_template, parser, st.session_state.chat_history, question_type
)
st.subheader("Generated Questions")
st.write(response)
# st.write(question_type)
st.subheader("Retrieved Documents")
for doc in retrieved_docs_schema.documents:
st.markdown(f"**Source:** {doc.metadata.source}, Page {doc.metadata.page}/{doc.metadata.total_pages}")
st.text_area("Content:", doc.page_content, height=100)
# st.subheader("Chat History")
# for message in st.session_state.chat_history:
# st.markdown(f"**{message['role'].capitalize()}**: {message['content']}")
if __name__ == "__main__":
main() |