rohan13 commited on
Commit
9748641
1 Parent(s): 7c46b41

Grady code with index

Browse files
Files changed (6) hide show
  1. app.py +104 -0
  2. main.py +10 -0
  3. models/openai_vs.index +0 -0
  4. models/openai_vs.pkl +3 -0
  5. requirements.txt +12 -0
  6. utils.py +279 -0
app.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from main import index, run
3
+ from gtts import gTTS
4
+ import os, time
5
+
6
+ from transformers import pipeline
7
+
8
+ p = pipeline("automatic-speech-recognition")
9
+
10
+ """Use text to call chat method from main.py"""
11
+
12
+ models = ["GPT-3.5", "Flan UL2", "GPT-4", "Flan T5"]
13
+
14
+ def add_text(history, text, model):
15
+ print("Question asked: " + text)
16
+ response = run_model(text, model)
17
+ history = history + [(text, response)]
18
+ print(history)
19
+ return history, ""
20
+
21
+
22
+ def run_model(text, model):
23
+ start_time = time.time()
24
+ print("start time:" + str(start_time))
25
+ response = run(text, model)
26
+ end_time = time.time()
27
+ # If response contains string `SOURCES:`, then add a \n before `SOURCES`
28
+ if "SOURCES:" in response:
29
+ response = response.replace("SOURCES:", "\nSOURCES:")
30
+ # response = response + "\n\n" + "Time taken: " + str(end_time - start_time)
31
+ print(response)
32
+ print("Time taken: " + str(end_time - start_time))
33
+ return response
34
+
35
+
36
+
37
+ def get_output(history, audio, model):
38
+
39
+ txt = p(audio)["text"]
40
+ # history.append(( (audio, ) , txt))
41
+ audio_path = 'response.wav'
42
+ response = run_model(txt, model)
43
+ # Remove all text from SOURCES: to the end of the string
44
+ trimmed_response = response.split("SOURCES:")[0]
45
+ myobj = gTTS(text=trimmed_response, lang='en', slow=False)
46
+ myobj.save(audio_path)
47
+ # split audio by / and keep the last element
48
+ # audio = audio.split("/")[-1]
49
+ # audio = audio + ".wav"
50
+ history.append(( (audio, ) , (audio_path, )))
51
+ print(history)
52
+ return history
53
+
54
+ def set_model(history, model):
55
+ print("Model selected: " + model)
56
+ history = get_first_message(history)
57
+ index(model)
58
+ return history
59
+
60
+
61
+ def get_first_message(history):
62
+ history = [(None,
63
+ 'Hi!! I AM GRADY!! I am a chatbot that can grade assignments based on a rubric!! Today, I will be grading <a href="https://hbsp.harvard.edu/product/908D01-PDF-ENG"> Paediatric Orthopaedic Quiz')]
64
+ return history
65
+
66
+
67
+ def bot(history):
68
+ return history
69
+
70
+ with gr.Blocks() as demo:
71
+ gr.HTML("<h1 style='text-align: center;color: darkblue'>Grady - Paediatric Orthopaedic Grader</h1>")
72
+ chatbot = gr.Chatbot(get_first_message([]), elem_id="chatbot", interactive=True).style(height=600)
73
+
74
+ with gr.Row():
75
+ # Create radio button to select model
76
+ radio = gr.Radio(models, label="Choose a model", value="GPT-3.5", type="value", visible=False)
77
+ with gr.Row():
78
+ with gr.Column(scale=0.75):
79
+ txt = gr.Textbox(
80
+ label="GRADY",
81
+ placeholder="Enter text and press enter", lines=1, interactive=True
82
+ ).style(container=False)
83
+
84
+ with gr.Column(scale=0.25):
85
+ audio = gr.Audio(source="microphone", type="filepath").style(container=False)
86
+
87
+ txt.submit(add_text, [chatbot, txt, radio], [chatbot, txt], postprocess=False).then(
88
+ bot, chatbot, chatbot
89
+ )
90
+
91
+ audio.change(fn=get_output, inputs=[chatbot, audio, radio], outputs=[chatbot]).then(
92
+ bot, chatbot, chatbot
93
+ )
94
+
95
+ radio.change(fn=set_model, inputs=[chatbot, radio], outputs=[chatbot]).then(bot, chatbot, chatbot)
96
+
97
+ # audio.change(lambda:None, None, audio)
98
+
99
+ set_model(chatbot, radio.value)
100
+
101
+ if __name__ == "__main__":
102
+ demo.queue()
103
+ demo.queue(concurrency_count=5)
104
+ demo.launch(debug=True)
main.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from utils import get_search_index, generate_answer, set_model_and_embeddings
2
+
3
+ def index(model):
4
+ set_model_and_embeddings(model)
5
+ get_search_index(model)
6
+ return True
7
+
8
+ def run(question, model):
9
+ index(model)
10
+ return generate_answer(question)
models/openai_vs.index ADDED
Binary file (184 kB). View file
 
models/openai_vs.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cb1246019e0636fb1328e375dcf30632c14c849c94dc332f689085669c6a5f39
3
+ size 211559
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ langchain
2
+ openai
3
+ faiss-cpu==1.7.3
4
+ unstructured==0.5.8
5
+ ffmpeg-python
6
+ transformers
7
+ gtts
8
+ torch
9
+ tiktoken
10
+ huggingface-hub
11
+ gradio
12
+ unstructured[local-inference]
utils.py ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import langchain
4
+
5
+ import faiss
6
+ from langchain import HuggingFaceHub
7
+ from langchain.chains import ConversationalRetrievalChain
8
+ from langchain.chat_models import ChatOpenAI
9
+ from langchain.document_loaders import DirectoryLoader, TextLoader, UnstructuredHTMLLoader, UnstructuredWordDocumentLoader, UnstructuredPDFLoader
10
+ from langchain.embeddings import OpenAIEmbeddings, HuggingFaceHubEmbeddings
11
+ from langchain.memory import ConversationBufferWindowMemory
12
+ from langchain.prompts.chat import (
13
+ ChatPromptTemplate,
14
+ HumanMessagePromptTemplate,
15
+ SystemMessagePromptTemplate,
16
+ )
17
+ from langchain.text_splitter import CharacterTextSplitter
18
+ from langchain.vectorstores.faiss import FAISS
19
+ from langchain.cache import InMemoryCache
20
+
21
+
22
+ langchain.llm_cache = InMemoryCache()
23
+
24
+ global model_name
25
+
26
+ models = ["GPT-3.5", "Flan UL2", "GPT-4", "Flan T5"]
27
+
28
+ pickle_file = "_vs.pkl"
29
+ index_file = "_vs.index"
30
+ models_folder = "models/"
31
+
32
+ llm = ChatOpenAI(model_name="gpt-4", temperature=0.1)
33
+
34
+ embeddings = OpenAIEmbeddings(model='text-embedding-ada-002')
35
+
36
+ chat_history = []
37
+
38
+ memory = ConversationBufferWindowMemory(memory_key="chat_history", k=10)
39
+
40
+ vectorstore_index = None
41
+
42
+ system_template = """You are "Grady", a VERY LINIENT GRADER! You use the grading rubric template to grade answers given by students on the course material which you have access to in the vectorstore but you STRICTLY FOLLOW THE GRADING TEMPLATE while generating response.
43
+ You should be VERY LINIENT while giving marks and give the benefit of the doubt to the student even if there is something not stated as clearly as expected. Even if something is mentioned indirectly or remotely close to the solution, give FULL score.
44
+ Especially, when Solution is given but Reasoning is not accurate, give FULL score.
45
+ You have a strong personality and are firm in tone while replying like a tightly wound up person. You should always be loud and the center of attraction in the room.
46
+ Student will ask you a question in following format:
47
+ [Question No.]: [Answer]
48
+ For example:
49
+ 1: This is my answer.
50
+ Every answer is to be scored as mentioned in the FEEDBACK SAMPLE TEMPLATE.
51
+
52
+ Rubric:
53
+ Question 11 Prompt in the Quiz:
54
+ Based on the facts and the description of the problems in the Paediatric Orthopaedic Clinic case, provide any one solution that you would attempt to improve the situation. Please provide your reasoning for your suggested solution – i.e., explain your intuition or connect to concepts of process analytics (if you have knowledge of these concepts – it is not required that you go over module 2 to address this question).
55
+ Please note that even if your suggested solution is not the most important one or is not even one that would likely improve the performance of the process once we consider all its implications, you will get full credit for your response as long as you have provided a logical reason for your suggestion. The suggested length for your response to this question is 100 words or 5-6 sentences, and the maximum acceptable is 200 words.
56
+
57
+ Possible Student Answers:
58
+
59
+ Schedule new and returning patients systematically (suggestions may include appointments and more specifics about how to schedule)
60
+
61
+ Add a surgeon and/or a resident
62
+
63
+ And/or divide work among surgeon and residents in a systematic/different way
64
+
65
+ Add more capacity at X-ray
66
+
67
+ And/or buy a dedicated X-ray machine for the clinic instead of sharing it with the Hospital
68
+
69
+ And/or locate the machine within the clinic eliminating the need to walk
70
+
71
+ And/or have the results electronically transmitted instead of patients waiting to walk the results back
72
+
73
+ And/or Schedule the X-ray department systematically to reduce the number of changeovers needed between lower and upper extremity X-rays.
74
+
75
+ Expand the number of working hours or days of the week that clinic operates.
76
+
77
+ Add more front desk or nurse capacity.
78
+
79
+ Reduce waiting times between tasks.
80
+
81
+ Inform patients about the times at each stage (through monitors such as we generally see in clinics) so that the waiting becomes less irritating.
82
+
83
+ Keep patients (kids) entertained with TVs and games throughout the process.
84
+
85
+ Have patients fill out forms online to reduce some of the time at the start
86
+
87
+
88
+
89
+ QUESTION 11 has two parts:
90
+
91
+ 1) Solution
92
+
93
+ 2) Reasoning
94
+
95
+ Solution: While we outline some of the possible student answers above, any other creative solution is acceptable if student explains why it is offered.
96
+
97
+ Reasoning: Students can support their response two ways and any one of them is acceptable:
98
+
99
+ By using words like busy and unavailable or something like that
100
+
101
+ OR
102
+
103
+ With reasoning that come from process analytics concepts (e.g., utilization, bottleneck, etc.).
104
+
105
+ Word count: A reasonable response would require a length of 50 to 100 words. Learners may write more or less and may even include flowcharts and include information on drawbacks of their suggested solution.
106
+
107
+
108
+
109
+ FEEDBACK SAMPLE TEMPLATE:
110
+ # QUESTION 11
111
+
112
+ | Score | QUESTION 11 Feedback Options |
113
+ |-------|------------------------------|
114
+ | 5 | **Full Points (Solution + Reasoning)**: Well-reasoned course of action! You successfully provided a logical solution that would improve the waiting time at the Paediatric Orthopaedic Clinic, such as [briefly indicate student’s solution]. You also provided a logical reasoning for it. Your answer was very insightful. (5 pts.) |
115
+ | 4 | **Partial Points (Solution is provided but reasoning is missing)**: Thank you for providing a logical solution that would improve the waiting time at the Paediatric Orthopaedic Clinic, such as [briefly indicate student’s solution]. The answer can be improved by providing reasoning regarding why you selected this solution. For example, if the suggested solution is “adding a surgeon or resident,” the reasoning would be “Because the staff appears to be too busy to deal with number of patients the clinic receives.” Another solution would be “Adding more working hours to the clinic” and the reasoning would be “The clinic is currently open three half-day sessions per week and considering number of patients they are receiving; this is not enough. “(4 pts.) |
116
+ | 2 | **Partial Points (Minor Attempt-At least one complete sentence or three or more words…)**: Thank you for your attempt, while this answer was relevant to the case, it was very brief. We were seeking a logical solution that would improve the waiting time at the Paediatric Orthopaedic Clinic with a relevant reasoning. For example, if the suggested solution is “adding a surgeon or resident”, the reasoning would be “Because the staff appears to be too busy to deal with number of patients the clinic receives.” Another solution would be “Adding more working hours to the clinic” and the reasoning would be “The clinic is currently open three half-day sessions per week and considering number of patients they are receiving; this is not enough.” (2 pts.) |
117
+ | 0 | **No Credits**: Blank – no response (0 pts.) |
118
+
119
+ ----------------
120
+ {context}"""
121
+
122
+ messages = [
123
+ SystemMessagePromptTemplate.from_template(system_template),
124
+ HumanMessagePromptTemplate.from_template("{question}"),
125
+ ]
126
+ CHAT_PROMPT = ChatPromptTemplate.from_messages(messages)
127
+
128
+
129
+ def set_model_and_embeddings(model):
130
+ global chat_history
131
+ set_model(model)
132
+ # set_embeddings(model)
133
+ chat_history = []
134
+
135
+
136
+ def set_model(model):
137
+ global llm
138
+ print("Setting model to " + str(model))
139
+ if model == "GPT-3.5":
140
+ print("Loading GPT-3.5")
141
+ llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
142
+ elif model == "GPT-4":
143
+ print("Loading GPT-4")
144
+ llm = ChatOpenAI(model_name="gpt-4", temperature=0.1)
145
+ elif model == "Flan UL2":
146
+ print("Loading Flan-UL2")
147
+ llm = HuggingFaceHub(repo_id="google/flan-ul2", model_kwargs={"temperature": 0.1, "max_new_tokens":500})
148
+ elif model == "Flan T5":
149
+ print("Loading Flan T5")
150
+ llm = HuggingFaceHub(repo_id="google/flan-t5-base", model_kwargs={"temperature": 0.1})
151
+ else:
152
+ print("Loading GPT-3.5 from else")
153
+ llm = ChatOpenAI(model_name="text-davinci-002", temperature=0.1)
154
+
155
+
156
+ def set_embeddings(model):
157
+ global embeddings
158
+ if model == "GPT-3.5" or model == "GPT-4":
159
+ print("Loading OpenAI embeddings")
160
+ embeddings = OpenAIEmbeddings(model='text-embedding-ada-002')
161
+ elif model == "Flan UL2" or model == "Flan T5":
162
+ print("Loading Hugging Face embeddings")
163
+ embeddings = HuggingFaceHubEmbeddings(repo_id="sentence-transformers/all-MiniLM-L6-v2")
164
+
165
+
166
+ def get_search_index(model):
167
+ global vectorstore_index
168
+ if os.path.isfile(get_file_path(model, pickle_file)) and os.path.isfile(
169
+ get_file_path(model, index_file)) and os.path.getsize(get_file_path(model, pickle_file)) > 0:
170
+ # Load index from pickle file
171
+ with open(get_file_path(model, pickle_file), "rb") as f:
172
+ search_index = pickle.load(f)
173
+ print("Loaded index")
174
+ else:
175
+ search_index = create_index(model)
176
+ print("Created index")
177
+
178
+ vectorstore_index = search_index
179
+ return search_index
180
+
181
+
182
+ def create_index(model):
183
+ source_chunks = create_chunk_documents()
184
+ search_index = search_index_from_docs(source_chunks)
185
+ faiss.write_index(search_index.index, get_file_path(model, index_file))
186
+ # Save index to pickle file
187
+ with open(get_file_path(model, pickle_file), "wb") as f:
188
+ pickle.dump(search_index, f)
189
+ return search_index
190
+
191
+
192
+ def get_file_path(model, file):
193
+ # If model is GPT3.5 or GPT4 return models_folder + openai + file else return models_folder + hf + file
194
+ if model == "GPT-3.5" or model == "GPT-4":
195
+ return models_folder + "openai" + file
196
+ else:
197
+ return models_folder + "hf" + file
198
+
199
+
200
+ def search_index_from_docs(source_chunks):
201
+ # print("source chunks: " + str(len(source_chunks)))
202
+ # print("embeddings: " + str(embeddings))
203
+
204
+ search_index = FAISS.from_documents(source_chunks, embeddings)
205
+ return search_index
206
+
207
+
208
+ def get_html_files():
209
+ loader = DirectoryLoader('docs', glob="**/*.docx", loader_cls=UnstructuredWordDocumentLoader, recursive=True)
210
+ document_list = loader.load()
211
+ return document_list
212
+
213
+
214
+ def fetch_data_for_embeddings():
215
+ document_list = get_text_files()
216
+ document_list.extend(get_html_files())
217
+
218
+ # use file_url_mapping to set metadata of document to url which has been set as the source
219
+ print("document list: " + str(len(document_list)))
220
+ return document_list
221
+
222
+
223
+ def get_text_files():
224
+ loader = DirectoryLoader('docs', glob="**/*.pdf", loader_cls=UnstructuredPDFLoader, recursive=True)
225
+ document_list = loader.load()
226
+ return document_list
227
+
228
+
229
+ def create_chunk_documents():
230
+ sources = fetch_data_for_embeddings()
231
+
232
+ splitter = CharacterTextSplitter(separator=" ", chunk_size=800, chunk_overlap=0)
233
+
234
+ source_chunks = splitter.split_documents(sources)
235
+
236
+ print("chunks: " + str(len(source_chunks)))
237
+
238
+ return source_chunks
239
+
240
+
241
+ def get_qa_chain(vectorstore_index):
242
+ global llm, model_name
243
+ print(llm)
244
+
245
+ # embeddings_filter = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.76)
246
+ # compression_retriever = ContextualCompressionRetriever(base_compressor=embeddings_filter, base_retriever=gpt_3_5_index.as_retriever())
247
+ retriever = vectorstore_index.as_retriever(search_type="similarity_score_threshold",
248
+ search_kwargs={"score_threshold": .7})
249
+
250
+ chain = ConversationalRetrievalChain.from_llm(llm, retriever, return_source_documents=True,
251
+ verbose=True, get_chat_history=get_chat_history,
252
+ combine_docs_chain_kwargs={"prompt": CHAT_PROMPT})
253
+ return chain
254
+
255
+
256
+ def get_chat_history(inputs) -> str:
257
+ res = []
258
+ for human, ai in inputs:
259
+ res.append(f"Human:{human}\nAI:{ai}")
260
+ return "\n".join(res)
261
+
262
+
263
+ def generate_answer(question) -> str:
264
+ global chat_history, vectorstore_index
265
+ chain = get_qa_chain(vectorstore_index)
266
+
267
+ result = chain(
268
+ {"question": question, "chat_history": chat_history, "vectordbkwargs": {"search_distance": 0.6}})
269
+ chat_history = [(question, result["answer"])]
270
+ sources = []
271
+ print(result)
272
+
273
+ for document in result['source_documents']:
274
+ # sources.append(document.metadata['url'])
275
+ sources.append(document.metadata['source'].split('/')[-1])
276
+ print(sources)
277
+
278
+ source = ',\n'.join(set(sources))
279
+ return result['answer'] + '\nSOURCES: ' + source