dawood/PDFChatGpt
1
1import urllib.request2import fitz3import re4import numpy as np5import tensorflow_hub as hub6import openai7import gradio as gr8import os9from sklearn.neighbors import NearestNeighbors10 11def download_pdf(url, output_path):12 urllib.request.urlretrieve(url, output_path)13 14 15def preprocess(text):16 text = text.replace('\n', ' ')17 text = re.sub('\s+', ' ', text)18 return text19 20 21def pdf_to_text(path, start_page=1, end_page=None):22 doc = fitz.open(path)23 total_pages = doc.page_count24 25 if end_page is None:26 end_page = total_pages27 28 text_list = []29 30 for i in range(start_page-1, end_page):31 text = doc.load_page(i).get_text("text")32 text = preprocess(text)33 text_list.append(text)34 35 doc.close()36 return text_list37 38 39def text_to_chunks(texts, word_length=150, start_page=1):40 text_toks = [t.split(' ') for t in texts]41 page_nums = []42 chunks = []43 44 for idx, words in enumerate(text_toks):45 for i in range(0, len(words), word_length):46 chunk = words[i:i+word_length]47 if (i+word_length) > len(words) and (len(chunk) < word_length) and (48 len(text_toks) != (idx+1)):49 text_toks[idx+1] = chunk + text_toks[idx+1]50 continue51 chunk = ' '.join(chunk).strip()52 chunk = f'[{idx+start_page}]' + ' ' + '"' + chunk + '"'53 chunks.append(chunk)54 return chunks55 56 57class SemanticSearch:58 59 def __init__(self):60 self.use = hub.load('https://tfhub.dev/google/universal-sentence-encoder/4')61 self.fitted = False62 63 64 def fit(self, data, batch=1000, n_neighbors=5):65 self.data = data66 self.embeddings = self.get_text_embedding(data, batch=batch)67 n_neighbors = min(n_neighbors, len(self.embeddings))68 self.nn = NearestNeighbors(n_neighbors=n_neighbors)69 self.nn.fit(self.embeddings)70 self.fitted = True71 72 73 def __call__(self, text, return_data=True):74 inp_emb = self.use([text])75 neighbors = self.nn.kneighbors(inp_emb, return_distance=False)[0]76 77 if return_data:78 return [self.data[i] for i in neighbors]79 else:80 return neighbors81 82 83 def get_text_embedding(self, texts, batch=1000):84 embeddings = []85 for i in range(0, len(texts), batch):86 text_batch = texts[i:(i+batch)]87 emb_batch = self.use(text_batch)88 embeddings.append(emb_batch)89 embeddings = np.vstack(embeddings)90 return embeddings91 92 93 94def load_recommender(path, start_page=1):95 global recommender96 texts = pdf_to_text(path, start_page=start_page)97 chunks = text_to_chunks(texts, start_page=start_page)98 recommender.fit(chunks)99 return 'Corpus Loaded.'100 101 102def generate_text(openAI_key,prompt, engine="text-davinci-003"):103 openai.api_key = openAI_key104 completions = openai.Completion.create(105 engine=engine,106 prompt=prompt,107 max_tokens=512,108 n=1,109 stop=None,110 temperature=0.7,111 )112 message = completions.choices[0].text113 return message114 115 116def generate_answer(question,openAI_key):117 topn_chunks = recommender(question)118 prompt = ""119 prompt += 'search results:\n\n'120 for c in topn_chunks:121 prompt += c + '\n\n'122 123 prompt += "Instructions: Compose a comprehensive reply to the query using the search results given. "\124 "Cite each reference using [number] notation (every result has this number at the beginning). "\125 "Citation should be done at the end of each sentence. If the search results mention multiple subjects "\126 "with the same name, create separate answers for each. Only include information found in the results and "\127 "don't add any additional information. Make sure the answer is correct and don't output false content. "\128 "If the text does not relate to the query, simply state 'Found Nothing'. Ignore outlier "\129 "search results which has nothing to do with the question. Only answer what is asked. The "\130 "answer should be short and concise.\n\nQuery: {question}\nAnswer: "131 132 prompt += f"Query: {question}\nAnswer:"133 answer = generate_text(openAI_key, prompt,"text-davinci-003")134 return answer135 136 137def question_answer(url, question,openAI_key):138 if openAI_key.strip()=='':139 return '[ERROR]: Please enter you Open AI Key. Get your key here : https://platform.openai.com/account/api-keys'140 if url.strip() != '':141 glob_url = url142 download_pdf(glob_url, 'corpus.pdf')143 load_recommender('corpus.pdf')144 145 if question.strip() == '':146 return '[ERROR]: Question field is empty'147 148 return generate_answer(question,openAI_key)149 150 151recommender = SemanticSearch()152 153title = 'PDF GPT'154description = """ What is PDF GPT ?1551. The problem is that Open AI has a 4K token limit and cannot take an entire PDF file as input. Additionally, it sometimes returns irrelevant responses due to poor embeddings. ChatGPT cannot directly talk to external data. The solution is PDF GPT, which allows you to chat with an uploaded PDF file using GPT functionalities. The application breaks the document into smaller chunks and generates embeddings using a powerful Deep Averaging Network Encoder. A semantic search is performed on your query, and the top relevant chunks are used to generate a response.1562. The returned response can even cite the page number in square brackets([]) where the information is located, adding credibility to the responses and helping to locate pertinent information quickly. The Responses are much better than the naive responses by Open AI."""157 158with gr.Blocks() as demo:159 160 gr.Markdown(f'<center><h1>{title}</h1></center>')161 gr.Markdown(description)162 163 with gr.Row():164 165 with gr.Group():166 gr.Markdown(f'<p style="text-align:center">Get your Open AI API key <a href="https://platform.openai.com/account/api-keys">here</a></p>')167 openAI_key=gr.Textbox(label='Enter your OpenAI API key here')168 url = gr.Textbox(label='Enter PDF URL here')169 gr.Markdown("<center><h4>OR<h4></center>")170 question = gr.Textbox(label='Enter your question here')171 btn = gr.Button(value='Submit')172 btn.style(full_width=True)173 174 with gr.Group():175 answer = gr.Textbox(label='The answer to your question is :')176 177 btn.click(question_answer, inputs=[url, question,openAI_key], outputs=[answer], api_name="ask")178#openai.api_key = os.getenv('Your_Key_Here') 179demo.launch()180 181 182# import streamlit as st183 184# #Define the app layout185# st.markdown(f'<center><h1>{title}</h1></center>', unsafe_allow_html=True)186# st.markdown(description)187 188# col1, col2 = st.columns(2)189 190# # Define the inputs in the first column191# with col1:192# url = st.text_input('URL')193# st.markdown("<center><h6>or<h6></center>", unsafe_allow_html=True)194# file = st.file_uploader('PDF', type='pdf')195# question = st.text_input('question')196# btn = st.button('Submit')197 198# # Define the output in the second column199# with col2:200# answer = st.text_input('answer')201 202# # Define the button action203# if btn:204# answer_value = question_answer(url, file, question)205# answer.value = answer_value