| import os |
| from langchain_community.document_loaders import PyPDFLoader |
| from langchain.text_splitter import RecursiveCharacterTextSplitter |
| from langchain_openai import OpenAIEmbeddings, ChatOpenAI |
| from langchain_community.vectorstores import FAISS |
| from langchain_core.prompts import ChatPromptTemplate |
| from langchain_core.output_parsers import StrOutputParser |
| from langchain_core.runnables import RunnablePassthrough, RunnableParallel |
|
|
| class ProjectRAGEngine: |
| def __init__(self, api_key): |
| self.embeddings = OpenAIEmbeddings(openai_api_key=api_key) |
| self.llm = ChatOpenAI(model="gpt-4o", openai_api_key=api_key, temperature=0) |
| self.vector_store = None |
|
|
| def process_documents(self, pdf_paths): |
| all_docs = [] |
| for path in pdf_paths: |
| try: |
| loader = PyPDFLoader(path) |
| docs = loader.load() |
| all_docs.extend(docs) |
| except Exception as e: |
| print(f"Error loading {path}: {e}") |
| |
| |
| text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) |
| splits = text_splitter.split_documents(all_docs) |
| self.vector_store = FAISS.from_documents(splits, self.embeddings) |
|
|
| def _format_docs(self, docs): |
| return "\n\n".join(doc.page_content for doc in docs) |
|
|
| def get_answer(self, query): |
| if not self.vector_store: |
| return "Please upload documents first.", [] |
|
|
| |
| template = """ |
| You are a professional Project Analyst. Answer strictly based on the provided context. |
| If the answer is not in the context, say you don't know. |
| Cite document names and page numbers for every answer. Include direct quotes. |
| |
| Context: {context} |
| Question: {question} |
| """ |
| prompt = ChatPromptTemplate.from_template(template) |
| retriever = self.vector_store.as_retriever(search_kwargs={"k": 5}) |
|
|
| |
| rag_chain_from_docs = ( |
| RunnablePassthrough.assign(context=(lambda x: self._format_docs(x["context"]))) |
| | prompt |
| | self.llm |
| | StrOutputParser() |
| ) |
|
|
| rag_chain_with_source = RunnableParallel( |
| {"context": retriever, "question": RunnablePassthrough()} |
| ).assign(answer=rag_chain_from_docs) |
|
|
| result = rag_chain_with_source.invoke(query) |
| |
| sources = [{"content": doc.page_content, "metadata": doc.metadata} for doc in result["context"]] |
| return result["answer"], sources |