root commited on
Commit
45628db
·
1 Parent(s): 7d35116

Add application file

Browse files
Files changed (2) hide show
  1. Dockerfile +7 -0
  2. api.py +175 -0
Dockerfile ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ FROM python:3.8-slim-buster as langchain-serve-img
2
+
3
+ RUN pip3 install langchain-serve
4
+ RUN pip3 install api
5
+
6
+
7
+ CMD [ "lc-serve", "deploy", "local", "--port", "7860", "api" ]
api.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import shutil
4
+ import urllib.request
5
+ from pathlib import Path
6
+ from tempfile import NamedTemporaryFile
7
+
8
+ import fitz
9
+ import numpy as np
10
+ import openai
11
+ import tensorflow_hub as hub
12
+ from fastapi import UploadFile
13
+ from lcserve import serving
14
+ from sklearn.neighbors import NearestNeighbors
15
+
16
+
17
+ recommender = None
18
+
19
+
20
+ def download_pdf(url, output_path):
21
+ urllib.request.urlretrieve(url, output_path)
22
+
23
+
24
+ def preprocess(text):
25
+ text = text.replace('\n', ' ')
26
+ text = re.sub('\s+', ' ', text)
27
+ return text
28
+
29
+
30
+ def pdf_to_text(path, start_page=1, end_page=None):
31
+ doc = fitz.open(path)
32
+ total_pages = doc.page_count
33
+
34
+ if end_page is None:
35
+ end_page = total_pages
36
+
37
+ text_list = []
38
+
39
+ for i in range(start_page - 1, end_page):
40
+ text = doc.load_page(i).get_text("text")
41
+ text = preprocess(text)
42
+ text_list.append(text)
43
+
44
+ doc.close()
45
+ return text_list
46
+
47
+
48
+ def text_to_chunks(texts, word_length=150, start_page=1):
49
+ text_toks = [t.split(' ') for t in texts]
50
+ page_nums = []
51
+ chunks = []
52
+
53
+ for idx, words in enumerate(text_toks):
54
+ for i in range(0, len(words), word_length):
55
+ chunk = words[i : i + word_length]
56
+ if (
57
+ (i + word_length) > len(words)
58
+ and (len(chunk) < word_length)
59
+ and (len(text_toks) != (idx + 1))
60
+ ):
61
+ text_toks[idx + 1] = chunk + text_toks[idx + 1]
62
+ continue
63
+ chunk = ' '.join(chunk).strip()
64
+ chunk = f'[Page no. {idx+start_page}]' + ' ' + '"' + chunk + '"'
65
+ chunks.append(chunk)
66
+ return chunks
67
+
68
+
69
+ class SemanticSearch:
70
+ def __init__(self):
71
+ self.use = hub.load('https://tfhub.dev/google/universal-sentence-encoder/4')
72
+ self.fitted = False
73
+
74
+ def fit(self, data, batch=1000, n_neighbors=5):
75
+ self.data = data
76
+ self.embeddings = self.get_text_embedding(data, batch=batch)
77
+ n_neighbors = min(n_neighbors, len(self.embeddings))
78
+ self.nn = NearestNeighbors(n_neighbors=n_neighbors)
79
+ self.nn.fit(self.embeddings)
80
+ self.fitted = True
81
+
82
+ def __call__(self, text, return_data=True):
83
+ inp_emb = self.use([text])
84
+ neighbors = self.nn.kneighbors(inp_emb, return_distance=False)[0]
85
+
86
+ if return_data:
87
+ return [self.data[i] for i in neighbors]
88
+ else:
89
+ return neighbors
90
+
91
+ def get_text_embedding(self, texts, batch=1000):
92
+ embeddings = []
93
+ for i in range(0, len(texts), batch):
94
+ text_batch = texts[i : (i + batch)]
95
+ emb_batch = self.use(text_batch)
96
+ embeddings.append(emb_batch)
97
+ embeddings = np.vstack(embeddings)
98
+ return embeddings
99
+
100
+
101
+ def load_recommender(path, start_page=1):
102
+ global recommender
103
+ if recommender is None:
104
+ recommender = SemanticSearch()
105
+
106
+ texts = pdf_to_text(path, start_page=start_page)
107
+ chunks = text_to_chunks(texts, start_page=start_page)
108
+ recommender.fit(chunks)
109
+ return 'Corpus Loaded.'
110
+
111
+
112
+ def generate_text(openAI_key, prompt, engine="text-davinci-003"):
113
+ openai.api_key = openAI_key
114
+ completions = openai.Completion.create(
115
+ engine=engine,
116
+ prompt=prompt,
117
+ max_tokens=512,
118
+ n=1,
119
+ stop=None,
120
+ temperature=0.7,
121
+ )
122
+ message = completions.choices[0].text
123
+ return message
124
+
125
+
126
+ def generate_answer(question, openAI_key):
127
+ topn_chunks = recommender(question)
128
+ prompt = ""
129
+ prompt += 'search results:\n\n'
130
+ for c in topn_chunks:
131
+ prompt += c + '\n\n'
132
+
133
+ prompt += (
134
+ "Instructions: Compose a comprehensive reply to the query using the search results given. "
135
+ "Cite each reference using [ Page Number] notation (every result has this number at the beginning). "
136
+ "Citation should be done at the end of each sentence. If the search results mention multiple subjects "
137
+ "with the same name, create separate answers for each. Only include information found in the results and "
138
+ "don't add any additional information. Make sure the answer is correct and don't output false content. "
139
+ "If the text does not relate to the query, simply state 'Text Not Found in PDF'. Ignore outlier "
140
+ "search results which has nothing to do with the question. Only answer what is asked. The "
141
+ "answer should be short and concise. Answer step-by-step. \n\nQuery: {question}\nAnswer: "
142
+ )
143
+
144
+ prompt += f"Query: {question}\nAnswer:"
145
+ answer = generate_text(openAI_key, prompt, "text-davinci-003")
146
+ return answer
147
+
148
+
149
+ def load_openai_key() -> str:
150
+ key = os.environ.get("OPENAI_API_KEY")
151
+ if key is None:
152
+ raise ValueError(
153
+ "[ERROR]: Please pass your OPENAI_API_KEY. Get your key here : https://platform.openai.com/account/api-keys"
154
+ )
155
+ return key
156
+
157
+
158
+ @serving
159
+ def ask_url(url: str, question: str):
160
+ download_pdf(url, 'corpus.pdf')
161
+ load_recommender('corpus.pdf')
162
+ openAI_key = load_openai_key()
163
+ return generate_answer(question, openAI_key)
164
+
165
+
166
+ @serving
167
+ async def ask_file(file: UploadFile, question: str) -> str:
168
+ suffix = Path(file.filename).suffix
169
+ with NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
170
+ shutil.copyfileobj(file.file, tmp)
171
+ tmp_path = Path(tmp.name)
172
+
173
+ load_recommender(str(tmp_path))
174
+ openAI_key = load_openai_key()
175
+ return generate_answer(question, openAI_key)