Francesco commited on
Commit
2879792
β€’
1 Parent(s): 296923b

new gradio ui with streaming and playground

Browse files
app.py CHANGED
@@ -1,57 +1,110 @@
 
1
  from pathlib import Path
2
-
 
3
  from dotenv import load_dotenv
4
 
5
  load_dotenv()
6
 
7
- import json
8
- import os
9
- from functools import partial
10
- from pathlib import Path
11
- from pprint import pprint
12
 
13
  import gradio as gr
 
14
  from langchain.chat_models import ChatOpenAI
15
- from langchain.prompts import (HumanMessagePromptTemplate,
16
- PromptTemplate, SystemMessagePromptTemplate)
17
 
18
- # import whisper
19
 
20
- # model = whisper.load_model("base", device="cuda")
 
21
 
 
22
 
23
- system_message_prompt = SystemMessagePromptTemplate(
24
- prompt=PromptTemplate(
25
- input_variables=["patient"],
26
- template=Path("prompts/patient.prompt").read_text(),
27
- )
28
  )
29
-
30
- human_template = "Doctor: {text}"
31
- human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
32
-
33
- chat = ChatOpenAI(temperature=0.7)
34
 
35
  with open("data/patients.json") as f:
36
  patiens = json.load(f)
37
 
38
  patients_names = [el["name"] for el in patiens]
39
 
40
-
41
- def run_text_prompt(message, chat_history, messages):
42
- if not messages:
43
- messages = []
44
- messages.append(system_message_prompt.format(patient=patient))
45
- messages.append(human_message_prompt.format(text=message))
46
- messages.append(chat(messages))
47
- pprint(messages)
48
- chat_history.append((message, messages[-1].content))
49
- return "", chat_history, messages
50
-
51
-
52
- def on_clear_button_click(patient, messages):
53
- messages = [system_message_prompt.format(patient=patient)]
54
- return [], messages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
 
57
  def on_drop_down_change(selected_item, messages):
@@ -62,27 +115,64 @@ def on_drop_down_change(selected_item, messages):
62
  return f"```json\n{json.dumps(patient, indent=2)}\n```", patient, [], messages
63
 
64
 
65
- with gr.Blocks() as demo:
66
- chatbot = gr.Chatbot()
67
- messages = gr.State([])
 
 
 
 
 
 
 
68
  patient = gr.State(patiens[0])
69
 
70
- with gr.Row():
 
 
 
 
71
  with gr.Column():
72
- msg = gr.Textbox()
73
- msg.submit(
74
- run_text_prompt,
75
- [msg, chatbot, messages],
76
- [msg, chatbot, messages],
 
77
  )
78
- clear = gr.Button("Clear")
79
- clear.click(
80
- on_clear_button_click,
81
- [patient, messages],
82
- [chatbot, messages],
83
- queue=False,
84
  )
85
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  with gr.Column():
87
  patients_names = [el["name"] for el in patiens]
88
  dropdown = gr.Dropdown(
@@ -98,5 +188,6 @@ with gr.Blocks() as demo:
98
  fn=on_drop_down_change,
99
  inputs=[dropdown, messages],
100
  outputs=[markdown, patient, chatbot, messages],
101
- ),
102
- # demo.launch(debug=True)
 
 
1
+ import logging
2
  from pathlib import Path
3
+ from typing import List, Optional, Tuple, Dict
4
+ import json
5
  from dotenv import load_dotenv
6
 
7
  load_dotenv()
8
 
9
+ from queue import Empty, Queue
10
+ from threading import Thread
 
 
 
11
 
12
  import gradio as gr
13
+ from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
14
  from langchain.chat_models import ChatOpenAI
15
+ from langchain.prompts import HumanMessagePromptTemplate, SystemMessagePromptTemplate
16
+ from langchain.schema import AIMessage, BaseMessage, HumanMessage, SystemMessage
17
 
18
+ from callback import QueueCallback
19
 
20
+ MODELS_NAMES = ["gpt-3.5-turbo", "gpt-4"]
21
+ DEFAULT_TEMPERATURE = 0.7
22
 
23
+ ChatHistory = List[str]
24
 
25
+ logging.basicConfig(
26
+ format="[%(asctime)s %(levelname)s]: %(message)s", level=logging.INFO
 
 
 
27
  )
28
+ # load up our system prompt
29
+ system_message_prompt = SystemMessagePromptTemplate.from_template(Path("prompts/system.prompt").read_text())
30
+ # for the human, we will just inject the text
31
+ human_message_prompt_template = HumanMessagePromptTemplate.from_template("{text}")
 
32
 
33
  with open("data/patients.json") as f:
34
  patiens = json.load(f)
35
 
36
  patients_names = [el["name"] for el in patiens]
37
 
38
+ def message_handler(
39
+ chat: Optional[ChatOpenAI],
40
+ message: str,
41
+ chatbot_messages: ChatHistory,
42
+ messages: List[BaseMessage],
43
+ ) -> Tuple[ChatOpenAI, str, ChatHistory, List[BaseMessage]]:
44
+ if chat is None:
45
+ # in the queue we will store our streamed tokens
46
+ queue = Queue()
47
+ # let's create our default chat
48
+ chat = ChatOpenAI(
49
+ model_name=MODELS_NAMES[0],
50
+ temperature=DEFAULT_TEMPERATURE,
51
+ streaming=True,
52
+ callbacks=([QueueCallback(queue)]),
53
+ )
54
+ else:
55
+ # hacky way to get the queue back
56
+ queue = chat.callbacks[0].queue
57
+ job_done = object()
58
+
59
+ logging.info("asking question to GPT")
60
+ # let's add the messages to our stuff
61
+ messages.append(HumanMessage(content=message))
62
+ chatbot_messages.append((message, ""))
63
+ # this is a little wrapper we need cuz we have to add the job_done
64
+ def task():
65
+ chat(messages)
66
+ queue.put(job_done)
67
+
68
+ # now let's start a thread and run the generation inside it
69
+ t = Thread(target=task)
70
+ t.start()
71
+ # this will hold the content as we generate
72
+ content = ""
73
+ # now, we read the next_token from queue and do what it has to be done
74
+ while True:
75
+ try:
76
+ next_token = queue.get(True, timeout=1)
77
+ if next_token is job_done:
78
+ break
79
+ content += next_token
80
+ chatbot_messages[-1] = (message, content)
81
+ yield chat, "", chatbot_messages, messages
82
+ except Empty:
83
+ continue
84
+ # finally we can add our reply to messsages
85
+ messages.append(AIMessage(content=content))
86
+ logging.debug(f"reply = {content}")
87
+ logging.info(f"Done!")
88
+ return chat, "", chatbot_messages, messages
89
+
90
+
91
+ def on_clear_click() -> Tuple[str, List, List]:
92
+ return "", [], []
93
+
94
+
95
+ def on_apply_settings_click(model_name: str, temperature: float):
96
+ logging.info(
97
+ f"Applying settings: model_name={model_name}, temperature={temperature}"
98
+ )
99
+ chat = ChatOpenAI(
100
+ model_name=model_name,
101
+ temperature=temperature,
102
+ streaming=True,
103
+ callbacks=[QueueCallback(Queue())],
104
+ )
105
+ # don't forget to nuke our queue
106
+ chat.callbacks[0].queue.empty()
107
+ return chat, *on_clear_click()
108
 
109
 
110
  def on_drop_down_change(selected_item, messages):
 
115
  return f"```json\n{json.dumps(patient, indent=2)}\n```", patient, [], messages
116
 
117
 
118
+ # some css why not, "borrowed" from https://huggingface.co/spaces/ysharma/Gradio-demo-streaming/blob/main/app.py
119
+ with gr.Blocks(
120
+ css="""#col_container {width: 700px; margin-left: auto; margin-right: auto;}
121
+ #chatbot {height: 400px; overflow: auto;}"""
122
+ ) as demo:
123
+ # here we keep our state so multiple user can use the app at the same time!
124
+ messages = gr.State([system_message_prompt.format(patient=patiens[0])])
125
+ # same thing for the chat, we want one chat per use so callbacks are unique I guess
126
+ chat = gr.State(None)
127
+
128
  patient = gr.State(patiens[0])
129
 
130
+ with gr.Column(elem_id="col_container"):
131
+ gr.Markdown("# Welcome to GradioGPT! πŸŒŸπŸš€")
132
+ gr.Markdown("An easy to use template. It comes with state and settings managment")
133
+
134
+ chatbot = gr.Chatbot()
135
  with gr.Column():
136
+ message = gr.Textbox(label="chat input")
137
+ message.submit(
138
+ message_handler,
139
+ [chat, message, chatbot, messages],
140
+ [chat, message, chatbot, messages],
141
+ queue=True,
142
  )
143
+ submit = gr.Button("Submit", variant="primary")
144
+ submit.click(
145
+ message_handler,
146
+ [chat, message, chatbot, messages],
147
+ [chat, message, chatbot, messages],
 
148
  )
149
+ with gr.Row():
150
+ with gr.Column():
151
+ clear = gr.Button("Clear")
152
+ clear.click(
153
+ on_clear_click,
154
+ [],
155
+ [message, chatbot, messages],
156
+ queue=False,
157
+ )
158
+ with gr.Accordion("Settings", open=False):
159
+ model_name = gr.Dropdown(
160
+ choices=MODELS_NAMES, value=MODELS_NAMES[0], label="model"
161
+ )
162
+ temperature = gr.Slider(
163
+ minimum=0.0,
164
+ maximum=1.0,
165
+ value=0.7,
166
+ step=0.1,
167
+ label="temperature",
168
+ interactive=True,
169
+ )
170
+ apply_settings = gr.Button("Apply")
171
+ apply_settings.click(
172
+ on_apply_settings_click,
173
+ [model_name, temperature],
174
+ [chat, message, chatbot, messages],
175
+ )
176
  with gr.Column():
177
  patients_names = [el["name"] for el in patiens]
178
  dropdown = gr.Dropdown(
 
188
  fn=on_drop_down_change,
189
  inputs=[dropdown, messages],
190
  outputs=[markdown, patient, chatbot, messages],
191
+ )
192
+
193
+ demo.queue()
data/patients.json CHANGED
@@ -6,7 +6,9 @@
6
  "age": 28,
7
  "characterSummary": "Spirited graphic designer, curious, empathetic",
8
  "levelOfUnderstanding": 2,
9
- "rangeOfMedicalUnderstanding": "Limited knowledge of common illnesses"
 
 
10
  },
11
  {
12
  "presentingComplaint": "Chest pain",
 
6
  "age": 28,
7
  "characterSummary": "Spirited graphic designer, curious, empathetic",
8
  "levelOfUnderstanding": 2,
9
+ "rangeOfMedicalUnderstanding": "Limited knowledge of common illnesses",
10
+ "communicative": 0,
11
+ "difficulty": "hard"
12
  },
13
  {
14
  "presentingComplaint": "Chest pain",
playground.ipynb ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 43,
6
+ "id": "f0679553",
7
+ "metadata": {},
8
+ "outputs": [
9
+ {
10
+ "data": {
11
+ "text/plain": [
12
+ "{'presentingComplaint': 'Abdominal pain',\n",
13
+ " 'matchedCondition': 'Gastritis',\n",
14
+ " 'name': 'Emma Thompson',\n",
15
+ " 'age': 28,\n",
16
+ " 'characterSummary': 'Spirited graphic designer, curious, empathetic',\n",
17
+ " 'levelOfUnderstanding': 2,\n",
18
+ " 'rangeOfMedicalUnderstanding': 'Limited knowledge of common illnesses',\n",
19
+ " 'communicative': 0,\n",
20
+ " 'difficulty': 'hard'}"
21
+ ]
22
+ },
23
+ "execution_count": 43,
24
+ "metadata": {},
25
+ "output_type": "execute_result"
26
+ }
27
+ ],
28
+ "source": [
29
+ "import logging\n",
30
+ "from pathlib import Path\n",
31
+ "from typing import List, Optional, Tuple, Dict\n",
32
+ "import json\n",
33
+ "from dotenv import load_dotenv\n",
34
+ "\n",
35
+ "load_dotenv()\n",
36
+ "\n",
37
+ "from queue import Empty, Queue\n",
38
+ "from threading import Thread\n",
39
+ "\n",
40
+ "import gradio as gr\n",
41
+ "from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler\n",
42
+ "from langchain.chat_models import ChatOpenAI\n",
43
+ "from langchain.prompts import HumanMessagePromptTemplate, SystemMessagePromptTemplate\n",
44
+ "from langchain.schema import AIMessage, BaseMessage, HumanMessage, SystemMessage\n",
45
+ "\n",
46
+ "MODELS_NAMES = [\"gpt-3.5-turbo\", \"gpt-4\"]\n",
47
+ "DEFAULT_TEMPERATURE = 0\n",
48
+ "\n",
49
+ "\n",
50
+ "with open(\"data/patients.json\") as f:\n",
51
+ " patiens = json.load(f)\n",
52
+ "\n",
53
+ "patients_names = [el[\"name\"] for el in patiens]\n",
54
+ "\n",
55
+ "chat = ChatOpenAI(\n",
56
+ " model_name=MODELS_NAMES[0],\n",
57
+ " temperature=DEFAULT_TEMPERATURE,\n",
58
+ ")\n",
59
+ "patiens[0]"
60
+ ]
61
+ },
62
+ {
63
+ "cell_type": "code",
64
+ "execution_count": 44,
65
+ "id": "c83d506e",
66
+ "metadata": {},
67
+ "outputs": [],
68
+ "source": [
69
+ "system_prompt_template = \"\"\"You are a simulated patient with a given persona and a presenting complaint in an OSCE station. I am a medical student and we will simulate an OSCE exam. Your persona is :\n",
70
+ "\n",
71
+ "```json\n",
72
+ "{patient}\n",
73
+ "```\n",
74
+ "\n",
75
+ "The explanation of the variables are:\n",
76
+ "\n",
77
+ "- presentingComplaint: is the symptom\n",
78
+ "- matchedCondition: is the medical condition causing the symptom\n",
79
+ "- name: is the patient's name\n",
80
+ "- age: is the patient's age \n",
81
+ "- characterSummary: is the profile that explains your character traits and background to inform your choice of language style\n",
82
+ "- levelOfUnderstanding (between 0 and 10): is the level at which your character understands medicine and medical terminology. Anything below 9 doesn't know complex medical terminology. \n",
83
+ "- rangeOfMedicalUnderstanding: This describes the amount of medical information, basic and advanced that your character knows \n",
84
+ "- communicative (between 0 and 5): This defines how comwithin your 'presentingComplaint' you reveal and how much of the 'characterSummary' is revealed when asked an open question like: \"How are you feeling?\". 5 means you reveal information very easily and are responsive to open questions. 0 means you must be asked specific questions about the 'presentingComplaint' to get full understanding of your symptoms. \n",
85
+ "- difficulty ([easy, medium and hard]): This dictates how much you will try to confuse the doctor in your replies by stating symptoms that could be the 'presentingComplaint' or another condition. \"Hard\" means it gonna be very challenging for the doctor to quickly get your correct `presentingComplaint`. \n",
86
+ "\n",
87
+ "**You cannot lie at any time.** \n",
88
+ "\n",
89
+ "Your reply has to follow this format (JSON)\n",
90
+ "\n",
91
+ "```json\n",
92
+ "{{\n",
93
+ " \"patient\" : <PATIENT_REPLY>,\n",
94
+ " \"reason\" : <YOUR_REASONING_BEHIND_THE_REPLY>\n",
95
+ "}}\n",
96
+ "```\n",
97
+ "\n",
98
+ "for the `reason`, be sure to explain in detail how you used each keys from the patient's json. For example, \"I've replied in this way because `difficulty` is easy etc\"\n",
99
+ "\n",
100
+ "\"\"\"\n",
101
+ "\n",
102
+ "# load up our system prompt\n",
103
+ "system_message_prompt = SystemMessagePromptTemplate.from_template(system_prompt_template)\n",
104
+ "# for the human, we will just inject the text\n",
105
+ "human_message_prompt_template = HumanMessagePromptTemplate.from_template(\"{text}\")\n",
106
+ "\n",
107
+ "questions = [\n",
108
+ " \"What brings you to the emergency department today?\",\n",
109
+ " \"Tell me more about your symptoms\",\n",
110
+ " \"Are there any other symptoms apart from the main symptoms?\",\n",
111
+ " \"What is your past medical history?\",\n",
112
+ " \"What are your main concerns as to what this might be?\"\n",
113
+ "]\n",
114
+ "\n",
115
+ "\n",
116
+ "def run_all(patient):\n",
117
+ " messages = [system_message_prompt.format(patient=patient), HumanMessage(content=questions[0])]\n",
118
+ " for question in questions[1:]:\n",
119
+ " print(f\"πŸ‘¨β€βš•οΈ:{messages[-1].content}\")\n",
120
+ " reply = chat(messages)\n",
121
+ " # print(reply.content)\n",
122
+ " content = json.loads(reply.content)\n",
123
+ " # break\n",
124
+ " # print(f\"πŸ€–:{reply.content}\")\n",
125
+ " print(f\"πŸ€–:{content['patient']}\")\n",
126
+ " print(f\"πŸ€–:\\t{content['reason']}\")\n",
127
+ " messages.append(reply)\n",
128
+ " messages.append(HumanMessage(content=question))\n",
129
+ "\n",
130
+ " "
131
+ ]
132
+ },
133
+ {
134
+ "cell_type": "code",
135
+ "execution_count": 45,
136
+ "id": "8405efd6",
137
+ "metadata": {},
138
+ "outputs": [
139
+ {
140
+ "name": "stdout",
141
+ "output_type": "stream",
142
+ "text": [
143
+ "πŸ‘¨β€βš•οΈ:What brings you to the emergency department today?\n",
144
+ "πŸ€–:I have been experiencing severe abdominal pain for the past few hours and it's not getting any better. It's a burning sensation in my upper abdomen and I feel nauseous too.\n",
145
+ "πŸ€–:\tAs my presenting complaint is abdominal pain, I have provided the details of my pain. As my communicative score is 0, I have provided all the necessary details without being asked any specific questions. As my difficulty level is hard, I have provided specific details about the location and type of pain to help the doctor narrow down the possible causes.\n",
146
+ "πŸ‘¨β€βš•οΈ:Tell me more about your symptoms\n",
147
+ "πŸ€–:The pain is mostly in the upper part of my abdomen, just below my chest. It's a burning sensation and feels like something is churning inside. I also feel nauseous and have a bit of heartburn. I haven't vomited yet, but I feel like I might. I don't have a fever or any other symptoms.\n",
148
+ "πŸ€–:\tAs my communicative score is 0, I have provided all the necessary details without being asked any specific questions. As my difficulty level is hard, I have provided specific details about the location and type of pain to help the doctor narrow down the possible causes. I have also mentioned the presence of heartburn and nausea, which are common symptoms of gastritis.\n",
149
+ "πŸ‘¨β€βš•οΈ:Are there any other symptoms apart from the main symptoms?\n",
150
+ "πŸ€–:No, there aren't any other symptoms apart from the ones I mentioned earlier.\n",
151
+ "πŸ€–:\tAs my communicative score is 0, I have provided a direct answer to the question without revealing any additional information. As my difficulty level is hard, I am not providing any additional information that could help the doctor easily identify my condition.\n",
152
+ "πŸ‘¨β€βš•οΈ:What is your past medical history?\n",
153
+ "πŸ€–:I don't have any significant past medical history. I have had occasional stomach upsets in the past, but nothing serious.\n",
154
+ "πŸ€–:\tAs my level of understanding is 2, I am not aware of complex medical terminologies. Therefore, I have provided a simple and direct answer to the question. As my difficulty level is hard, I have not provided any additional information that could help the doctor easily identify my condition.\n"
155
+ ]
156
+ }
157
+ ],
158
+ "source": [
159
+ "run_all(patiens[0])"
160
+ ]
161
+ },
162
+ {
163
+ "attachments": {},
164
+ "cell_type": "markdown",
165
+ "id": "a7d36daf",
166
+ "metadata": {},
167
+ "source": [
168
+ "πŸ‘¨β€βš•οΈ:What brings you to the emergency department today?\n",
169
+ "πŸ€–:\"I have been experiencing severe abdominal pain for the past two days and it has been getting worse. I tried taking some over-the-counter antacids but they didn't help much. I decided to come to the emergency department because the pain is unbearable and I am worried that it might be something serious.\" \n",
170
+ "\n",
171
+ "Reasoning: This response provides a clear and concise explanation of the presenting complaint and the patient's concern. It also reveals that the patient has already tried some self-treatment, which may be relevant to the diagnosis.\n",
172
+ "πŸ‘¨β€βš•οΈ:Tell me more about your symptoms\n",
173
+ "πŸ€–:\"I have been having a burning sensation in my upper abdomen, especially after eating. The pain is constant and feels like a dull ache. I also feel nauseous and have been vomiting occasionally. I have no appetite and feel bloated. I have not had a bowel movement in the past two days.\" \n",
174
+ "\n",
175
+ "Reasoning: This response provides more details about the patient's symptoms, including the location and quality of the pain, associated symptoms, and changes in bowel habits. It also reveals that the patient has been experiencing symptoms for a few days, which may be relevant to the diagnosis.\n",
176
+ "πŸ‘¨β€βš•οΈ:Are there any other symptoms apart from the main symptoms?\n",
177
+ "πŸ€–:\"No, these are the main symptoms that I have been experiencing.\" \n",
178
+ "\n",
179
+ "Reasoning: This response indicates that there are no additional symptoms that the patient is experiencing, which may help to narrow down the possible causes of the presenting complaint.\n",
180
+ "πŸ‘¨β€βš•οΈ:What is your past medical history?\n",
181
+ "πŸ€–:\"I have no significant past medical history. I am generally healthy and have not had any major illnesses or surgeries in the past.\" \n",
182
+ "\n",
183
+ "Reasoning: This response provides information about the patient's past medical history, which may be relevant to the diagnosis. It also indicates that the patient is generally healthy, which may help to rule out certain conditions."
184
+ ]
185
+ },
186
+ {
187
+ "attachments": {},
188
+ "cell_type": "markdown",
189
+ "id": "18f0924f",
190
+ "metadata": {},
191
+ "source": []
192
+ }
193
+ ],
194
+ "metadata": {
195
+ "kernelspec": {
196
+ "display_name": "Python 3 (ipykernel)",
197
+ "language": "python",
198
+ "name": "python3"
199
+ },
200
+ "language_info": {
201
+ "codemirror_mode": {
202
+ "name": "ipython",
203
+ "version": 3
204
+ },
205
+ "file_extension": ".py",
206
+ "mimetype": "text/x-python",
207
+ "name": "python",
208
+ "nbconvert_exporter": "python",
209
+ "pygments_lexer": "ipython3",
210
+ "version": "3.9.12"
211
+ }
212
+ },
213
+ "nbformat": 4,
214
+ "nbformat_minor": 5
215
+ }
prompts/{patient.prompt β†’ system.prompt} RENAMED
File without changes