I have written a code to create a PDF QA Bot. The code for it is - """import numpy as np
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.document_loaders import PyPDFLoader
from langchain import HuggingFacePipeline
from langchain import PromptTemplate
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferWindowMemory
from sentence_transformers import SentenceTransformer
import gradio as gr
from langchain_community.vectorstores import FAISS

#nltk.download()
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, pipeline, TextStreamer


def create_multipage_pdf(text, filename='output.pdf', font_size=12, margin=50):
    # Split text into chunks that fit on one page
    chunks = []
    chunk_size = 50  # Number of characters per chunk
    for i in range(0, len(text), chunk_size):
        chunks.append(text[i:i+chunk_size])

    # Create PDF
    c = canvas.Canvas(filename, pagesize=letter)
    width, height = letter
    y = height - margin
    for chunk in chunks:
        # Draw text
        text_object = c.beginText(margin, y)
        text_object.setFont("Helvetica", font_size)
        text_object.textLines(chunk)
        c.drawText(text_object)

        # Update y position
        y -= (font_size + 4)  # Adjust spacing between lines

        # Check if we need to start a new page
        if y <= margin:
            c.showPage()
            y = height - margin

    c.save()

# Example usage
#create_multipage_pdf(text, filename='document.pdf')

bnb_config = BitsAndBytesConfig(load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=False)

model_id = "meta-llama/Llama-2-7b-chat-hf"

tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config = bnb_config,device_map={"":0})

import json
import textwrap

B_INST, E_INST = "
", "
"
B_SYS, E_SYS = "
\n", "\n
\n\n"
DEFAULT_SYSTEM_PROMPT = """\
You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.

If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information."""



def get_prompt(instruction, new_system_prompt=DEFAULT_SYSTEM_PROMPT ):
    SYSTEM_PROMPT = B_SYS + new_system_prompt + E_SYS
    prompt_template =  B_INST + SYSTEM_PROMPT + instruction + E_INST
    return prompt_template


loader = PyPDFLoader("document.pdf")

text_splitter = RecursiveCharacterTextSplitter(
    # Set a really small chunk size, just to show.
    chunk_size = 500,
    chunk_overlap  = 20,
    length_function = len,
)

pages = loader.load_and_split(text_splitter)
# testing with embeddings
embeddings = HuggingFaceEmbeddings(model_name="hkunlp/instructor-xl", model_kwargs={'device': 'cuda'})
#embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2", model_kwargs={'device': 'cuda'})
print("OK1")
db = FAISS.from_documents(pages, embedding=embeddings)
#db = Chroma.from_documents(pages, embedding=embeddings)
print("OK2")
instruction = "Given the context that has been provided. \n {context}, Answer the following question - \n{question}"

# This part was remove from first line of the system_prompt for testing purposes. Be precise in your answers wherever possible.
system_prompt = """You are an honest virtual assistant. You will be given a context to answer from.  In case you are sure you don't know the answer then you say that based on the context you don't know the answer. In all other instances you provide an answer to the best of your capability. Cite context when you can access them maintaining formatting. Don't say 'based on the context provided more than once."""

get_prompt(instruction, system_prompt)

"""## Setting up with LangChain"""

template = get_prompt(instruction, system_prompt)
print(template)

prompt = PromptTemplate(template=template, input_variables=["context", "question"])

memory = ConversationBufferWindowMemory(
    memory_key="chat_history", k=5,
    return_messages=True
)

retriever = db.as_retriever(search_kwargs={'k': 10})

def create_pipeline(max_new_tokens=1024):
    pipe = pipeline("text-generation",
                model=model,
                tokenizer = tokenizer,
                max_new_tokens = max_new_tokens,
                temperature = 0.6)
    return pipe

class ChessBot:
  # send re_ranked docs instead of retriever for advanced RAG
  def __init__(self, memory, prompt, task:str = "text-generation", retriever = retriever):
    self.memory = memory
    self.prompt = prompt
    self.retriever = retriever
    


  def create_chat_bot(self, max_new_tokens = 1024):
    hf_pipe = create_pipeline(max_new_tokens)
    llm = HuggingFacePipeline(pipeline =hf_pipe)
    qa = ConversationalRetrievalChain.from_llm(
      llm=llm,
      retriever=self.retriever,
      memory=self.memory,
      combine_docs_chain_kwargs={"prompt": self.prompt}
  )
    return qa

chess_bot = ChessBot(memory = memory, prompt = prompt)

bot = chess_bot.create_chat_bot()



def clear_llm_memory():
  bot.memory.clear()

def update_prompt(sys_prompt):
  if sys_prompt == "":
    sys_prompt = system_prompt
  template = get_prompt(instruction, sys_prompt)

  prompt = PromptTemplate(template=template, input_variables=["context", "question"])

  bot.combine_docs_chain.llm_chain.prompt = prompt

"""1. Not using API
2. Use cases are not defined
3. Just a POC emphasis
"""

with gr.Blocks() as demo:
    gr.Markdown(
    """
    #
    Please ask your questions!
    """)
    # Commenting update prompt option
    #update_sys_prompt = gr.Textbox(label = "Update System Prompt")
    chatbot = gr.Chatbot(label="SAMSUNG SDS", height = 300)
    msg = gr.Textbox(label = "Enter your query!")
    with gr.Column(scale=1):
        clear = gr.ClearButton([msg, chatbot])
    with gr.Column(scale=1):
        clear_memory = gr.Button(value = "Clear LLM Memory")

    def respond(message, chat_history):
        print("Query:::", message)
        bot_message = bot({"question": message})['answer']
        chat_history.append((message, bot_message))
        return "", chat_history

    msg.submit(respond, inputs=[msg, chatbot], outputs=[msg, chatbot])
    clear_memory.click(clear_llm_memory)
    # Commenting update prompt option
    #update_sys_prompt.submit(update_prompt, inputs=update_sys_prompt)

demo.launch(share=False, server_name='216.48.177.144', server_port=8502)
""". Now, I want to incorporate this logic to my existing code - """from langchain.document_transformers import LongContextReorder
from langchain.retrievers import ContextualCompressionRetriever, DocumentCompressorPipeline


reordering = LongContextReorder()

pipeline_compressor = DocumentCompressorPipeline(
    transformers=[
        reordering
    ]
)

compression_retriever = ContextualCompressionRetriever(base_compressor=pipeline_compressor, base_retriever=vector_store.as_retriever())

qa = ConversationalRetrievalChain.from_llm(llm=llm, retriever=compression_retriever, memory=memory, return_source_documents=True)""". Rewrite the complete code using this logic. You can skip the gradio and import part