I have developed a code - """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
from flashrank.Ranker import Ranker, RerankRequest

#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 modify this code to include this concept - """Long-Context Reorder

No matter the architecture of your model, there is a substantial performance degradation when you include 10+ retrieved documents. In brief: When models must access relevant information in the middle of long contexts, they tend to ignore the provided documents. See: https://arxiv.org/abs/2307.03172

To avoid this issue you can re-order documents after retrieval to avoid performance degradation.

%pip install --upgrade --quiet  sentence-transformers > /dev/null

from langchain.chains import LLMChain, StuffDocumentsChain
from langchain.prompts import PromptTemplate
from langchain_community.document_transformers import (
    LongContextReorder,
)
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAI

# Get embeddings.
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")

texts = [
    "Basquetball is a great sport.",
    "Fly me to the moon is one of my favourite songs.",
    "The Celtics are my favourite team.",
    "This is a document about the Boston Celtics",
    "I simply love going to the movies",
    "The Boston Celtics won the game by 20 points",
    "This is just a random text.",
    "Elden Ring is one of the best games in the last 15 years.",
    "L. Kornet is one of the best Celtics players.",
    "Larry Bird was an iconic NBA player.",
]

# Create a retriever
retriever = Chroma.from_texts(texts, embedding=embeddings).as_retriever(
    search_kwargs={"k": 10}
)
query = "What can you tell me about the Celtics?"

# Get relevant documents ordered by relevance score
docs = retriever.get_relevant_documents(query)
docs

[Document(page_content='This is a document about the Boston Celtics'),
 Document(page_content='The Celtics are my favourite team.'),
 Document(page_content='L. Kornet is one of the best Celtics players.'),
 Document(page_content='The Boston Celtics won the game by 20 points'),
 Document(page_content='Larry Bird was an iconic NBA player.'),
 Document(page_content='Elden Ring is one of the best games in the last 15 years.'),
 Document(page_content='Basquetball is a great sport.'),
 Document(page_content='I simply love going to the movies'),
 Document(page_content='Fly me to the moon is one of my favourite songs.'),
 Document(page_content='This is just a random text.')]

# Reorder the documents:
# Less relevant document will be at the middle of the list and more
# relevant elements at beginning / end.
reordering = LongContextReorder()
reordered_docs = reordering.transform_documents(docs)

# Confirm that the 4 relevant documents are at beginning and end.
reordered_docs

[Document(page_content='The Celtics are my favourite team.'),
 Document(page_content='The Boston Celtics won the game by 20 points'),
 Document(page_content='Elden Ring is one of the best games in the last 15 years.'),
 Document(page_content='I simply love going to the movies'),
 Document(page_content='This is just a random text.'),
 Document(page_content='Fly me to the moon is one of my favourite songs.'),
 Document(page_content='Basquetball is a great sport.'),
 Document(page_content='Larry Bird was an iconic NBA player.'),
 Document(page_content='L. Kornet is one of the best Celtics players.'),
 Document(page_content='This is a document about the Boston Celtics')]

# We prepare and run a custom Stuff chain with reordered docs as context.

# Override prompts
document_prompt = PromptTemplate(
    input_variables=["page_content"], template="{page_content}"
)
document_variable_name = "context"
llm = OpenAI()
stuff_prompt_override = """Given this text extracts:
-----
{context}
-----
Please answer the following question:
{query}"""
prompt = PromptTemplate(
    template=stuff_prompt_override, input_variables=["context", "query"]
)

# Instantiate the chain
llm_chain = LLMChain(llm=llm, prompt=prompt)
chain = StuffDocumentsChain(
    llm_chain=llm_chain,
    document_prompt=document_prompt,
    document_variable_name=document_variable_name,
)
chain.run(input_documents=reordered_docs, query=query)

'\n\nThe Celtics are referenced in four of the nine text extracts. They are mentioned as the favorite team of the author, the winner of a basketball game, a team with one of the best players, and a team with a specific player. Additionally, the last extract states that the document is about the Boston Celtics. This suggests that the Celtics are a basketball team, possibly from Boston, that is well-known and has had successful players and games in the past. '

Help us out by providing feedback on this documentation page:
""" to improve my code's output. How can I merge it?