Spaces:
Runtime error
Runtime error
ENH: Update bot
Browse files- .gitignore +4 -1
- app.py +111 -41
- bot_gradio.ipynb +195 -86
- openai_prompts/__init__.py +1 -0
- openai_prompts/contextualize.py +56 -0
.gitignore
CHANGED
@@ -1,3 +1,6 @@
|
|
1 |
# ignore old_files folder
|
2 |
old_files
|
3 |
-
audio
|
|
|
|
|
|
|
|
1 |
# ignore old_files folder
|
2 |
old_files
|
3 |
+
audio
|
4 |
+
conversations
|
5 |
+
# ignore pyc files
|
6 |
+
*.pyc
|
app.py
CHANGED
@@ -1,16 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
|
3 |
import openai
|
4 |
|
5 |
from audio_utils import text_to_speech, text_to_speech_polly
|
|
|
6 |
from openai_utils import get_embedding, whisper_transcription
|
7 |
from vector_db import LanceVectorDb, QnA
|
8 |
|
9 |
db = LanceVectorDb("qna_db")
|
10 |
-
|
11 |
OPENAI_KEY = os.environ["OPENAI_KEY"]
|
12 |
openai.api_key = OPENAI_KEY
|
13 |
|
|
|
14 |
if len(db.table.to_pandas()) == 0:
|
15 |
print("Empty db, trying to load qna's from json file")
|
16 |
try:
|
@@ -19,6 +29,7 @@ if len(db.table.to_pandas()) == 0:
|
|
19 |
except Exception as exception:
|
20 |
raise Exception("Failed to initialize db from json file") from exception
|
21 |
|
|
|
22 |
import os
|
23 |
|
24 |
|
@@ -29,53 +40,103 @@ def ensure_dir(directory):
|
|
29 |
|
30 |
ensure_dir("audio")
|
31 |
|
|
|
|
|
32 |
from langdetect import detect
|
33 |
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
"You are a question answering assistant.\n"
|
36 |
-
"You answer questions from users based on information in our database provided as context.\n"
|
37 |
-
"
|
38 |
-
"You
|
39 |
"You answer in the language that the question was asked in.\n"
|
40 |
"You speak german and english.\n"
|
41 |
)
|
42 |
|
|
|
43 |
|
44 |
-
def bot_respond(user_query, chat_messages: list):
|
45 |
-
embedding = get_embedding(user_query)
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
64 |
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
completion = openai.ChatCompletion.create(
|
69 |
-
model="gpt-3.5-turbo",
|
70 |
-
messages=chat_messages,
|
71 |
)
|
72 |
-
text = completion.choices[0].message.content
|
73 |
|
74 |
-
|
75 |
-
|
76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
-
|
|
|
|
|
79 |
|
80 |
|
81 |
import random
|
@@ -90,14 +151,17 @@ def display_history(conversation):
|
|
90 |
return conversation_string
|
91 |
|
92 |
|
93 |
-
def handle_audiofile(audio_filepath: str,
|
94 |
user_question = whisper_transcription(audio_filepath)
|
95 |
print("Transcription", user_question)
|
96 |
|
97 |
-
bot_response_text, context_prompt = bot_respond(user_question,
|
98 |
|
99 |
-
|
100 |
-
|
|
|
|
|
|
|
101 |
|
102 |
if lang not in ["en", "de"]:
|
103 |
lang = "en"
|
@@ -111,9 +175,9 @@ def handle_audiofile(audio_filepath: str, chat_messages: list):
|
|
111 |
return (
|
112 |
user_question,
|
113 |
bot_response_text,
|
114 |
-
|
115 |
context_prompt,
|
116 |
-
display_history(chat_messages),
|
117 |
output_filepath,
|
118 |
)
|
119 |
|
@@ -122,7 +186,11 @@ import gradio as gr
|
|
122 |
|
123 |
with gr.Blocks() as demo:
|
124 |
# initialize the state that will be used to store the chat messages
|
125 |
-
chat_messages = gr.State(
|
|
|
|
|
|
|
|
|
126 |
|
127 |
with gr.Row():
|
128 |
audio_input = gr.Audio(source="microphone", type="filepath", format="mp3")
|
@@ -133,7 +201,9 @@ with gr.Blocks() as demo:
|
|
133 |
assistant_answer = gr.Textbox(label="PhoneBot Answer")
|
134 |
|
135 |
with gr.Row():
|
136 |
-
context_info = gr.Textbox(
|
|
|
|
|
137 |
conversation_history = gr.Textbox(label="Conversation history")
|
138 |
|
139 |
# when the audio input is stopped, run the transcribe function
|
|
|
1 |
+
import json
|
2 |
+
import os
|
3 |
+
|
4 |
+
with open("env.json") as f:
|
5 |
+
env_vars = json.load(f)
|
6 |
+
|
7 |
+
for k, v in env_vars.items():
|
8 |
+
os.environ[k] = v
|
9 |
+
|
10 |
import os
|
11 |
|
12 |
import openai
|
13 |
|
14 |
from audio_utils import text_to_speech, text_to_speech_polly
|
15 |
+
from openai_prompts import contextualize_question
|
16 |
from openai_utils import get_embedding, whisper_transcription
|
17 |
from vector_db import LanceVectorDb, QnA
|
18 |
|
19 |
db = LanceVectorDb("qna_db")
|
|
|
20 |
OPENAI_KEY = os.environ["OPENAI_KEY"]
|
21 |
openai.api_key = OPENAI_KEY
|
22 |
|
23 |
+
|
24 |
if len(db.table.to_pandas()) == 0:
|
25 |
print("Empty db, trying to load qna's from json file")
|
26 |
try:
|
|
|
29 |
except Exception as exception:
|
30 |
raise Exception("Failed to initialize db from json file") from exception
|
31 |
|
32 |
+
|
33 |
import os
|
34 |
|
35 |
|
|
|
40 |
|
41 |
ensure_dir("audio")
|
42 |
|
43 |
+
import random
|
44 |
+
|
45 |
from langdetect import detect
|
46 |
|
47 |
+
|
48 |
+
def red(text):
|
49 |
+
return f'\x1b[31m"{text}"\x1b[0m'
|
50 |
+
|
51 |
+
|
52 |
+
def query_database(prompt: str):
|
53 |
+
print("Querying database for question:", prompt)
|
54 |
+
embedding = get_embedding(prompt)
|
55 |
+
qnas = db.get_qna(embedding, lang="en", limit=3)
|
56 |
+
print("Total_qnas:", len(qnas), [qna.score for qna in qnas])
|
57 |
+
qnas = [qna for qna in qnas if qna.score < 0.49]
|
58 |
+
print("Filtered_qnas:", len(qnas))
|
59 |
+
return qnas
|
60 |
+
|
61 |
+
|
62 |
+
available_functions = {
|
63 |
+
"query_database": query_database,
|
64 |
+
}
|
65 |
+
|
66 |
+
conversation_folder = f"conversations/{random.randint(0, 10000)}"
|
67 |
+
ensure_dir(conversation_folder)
|
68 |
+
|
69 |
+
SYSTEM_PROMPT = (
|
70 |
"You are a question answering assistant.\n"
|
71 |
+
"You answer questions from users delimited by tripple dashes --- based on information in our database provided as context.\n"
|
72 |
+
"The context informtion in delimited by tripple backticks ```\n"
|
73 |
+
"You try to be concise and offer the most relevant information.\n"
|
74 |
"You answer in the language that the question was asked in.\n"
|
75 |
"You speak german and english.\n"
|
76 |
)
|
77 |
|
78 |
+
step = 0
|
79 |
|
|
|
|
|
80 |
|
81 |
+
def context_format(qnas):
|
82 |
+
context = "Context:\n\n```"
|
83 |
+
for qna in qnas:
|
84 |
+
context += f"For question: {qna.question}\nThe answer is: {qna.answer}\n"
|
85 |
+
context += "```"
|
86 |
+
return context
|
87 |
+
|
88 |
+
|
89 |
+
def bot_respond(user_query, history: list):
|
90 |
+
global step
|
91 |
+
|
92 |
+
chat_messages = history["chat_messages"]
|
93 |
+
|
94 |
+
user_query = contextualize_question(user_query, chat_messages)
|
95 |
+
|
96 |
+
path = os.path.join(conversation_folder, f"step_{step}_qna.json")
|
97 |
+
|
98 |
+
qnas = query_database(user_query)
|
99 |
+
|
100 |
+
prompt = f"The user said: ---{user_query}---\n\n"
|
101 |
|
102 |
+
context = context_format(qnas)
|
103 |
+
prompt += context
|
104 |
+
|
105 |
+
qna_messages = [
|
106 |
+
{
|
107 |
+
"role": "assistant",
|
108 |
+
"content": SYSTEM_PROMPT,
|
109 |
+
},
|
110 |
+
{
|
111 |
+
"role": "user",
|
112 |
+
"content": prompt,
|
113 |
+
},
|
114 |
+
]
|
115 |
|
116 |
completion = openai.ChatCompletion.create(
|
117 |
+
model="gpt-3.5-turbo", messages=qna_messages, temperature=0
|
|
|
118 |
)
|
|
|
119 |
|
120 |
+
response_message = completion["choices"][0]["message"]
|
121 |
+
bot_response = response_message.content
|
122 |
+
|
123 |
+
path = os.path.join(conversation_folder, f"step_{step}_qna.json")
|
124 |
+
|
125 |
+
with open(path, "w") as f:
|
126 |
+
json.dump(
|
127 |
+
{
|
128 |
+
"chat_messages": chat_messages,
|
129 |
+
"response": response_message.content,
|
130 |
+
},
|
131 |
+
f,
|
132 |
+
indent=4,
|
133 |
+
)
|
134 |
+
|
135 |
+
chat_messages.append({"role": "assistant", "content": bot_response})
|
136 |
|
137 |
+
step += 1
|
138 |
+
|
139 |
+
return bot_response, prompt
|
140 |
|
141 |
|
142 |
import random
|
|
|
151 |
return conversation_string
|
152 |
|
153 |
|
154 |
+
def handle_audiofile(audio_filepath: str, history: list):
|
155 |
user_question = whisper_transcription(audio_filepath)
|
156 |
print("Transcription", user_question)
|
157 |
|
158 |
+
bot_response_text, context_prompt = bot_respond(user_question, history)
|
159 |
|
160 |
+
if bot_response_text:
|
161 |
+
lang = detect(bot_response_text)
|
162 |
+
print("Detected language:", lang, "for text:", bot_response_text)
|
163 |
+
else:
|
164 |
+
lang = "en"
|
165 |
|
166 |
if lang not in ["en", "de"]:
|
167 |
lang = "en"
|
|
|
175 |
return (
|
176 |
user_question,
|
177 |
bot_response_text,
|
178 |
+
history,
|
179 |
context_prompt,
|
180 |
+
display_history(history["chat_messages"]),
|
181 |
output_filepath,
|
182 |
)
|
183 |
|
|
|
186 |
|
187 |
with gr.Blocks() as demo:
|
188 |
# initialize the state that will be used to store the chat messages
|
189 |
+
chat_messages = gr.State(
|
190 |
+
{
|
191 |
+
"chat_messages": [{"role": "system", "content": SYSTEM_PROMPT}],
|
192 |
+
}
|
193 |
+
)
|
194 |
|
195 |
with gr.Row():
|
196 |
audio_input = gr.Audio(source="microphone", type="filepath", format="mp3")
|
|
|
201 |
assistant_answer = gr.Textbox(label="PhoneBot Answer")
|
202 |
|
203 |
with gr.Row():
|
204 |
+
context_info = gr.Textbox(
|
205 |
+
label="Context provided to the bot + additional infos for debugging"
|
206 |
+
)
|
207 |
conversation_history = gr.Textbox(label="Conversation history")
|
208 |
|
209 |
# when the audio input is stopped, run the transcribe function
|
bot_gradio.ipynb
CHANGED
@@ -2,7 +2,22 @@
|
|
2 |
"cells": [
|
3 |
{
|
4 |
"cell_type": "code",
|
5 |
-
"execution_count":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
"metadata": {},
|
7 |
"outputs": [],
|
8 |
"source": [
|
@@ -10,16 +25,17 @@
|
|
10 |
"from vector_db import LanceVectorDb, QnA\n",
|
11 |
"from openai_utils import get_embedding, whisper_transcription\n",
|
12 |
"from audio_utils import text_to_speech, text_to_speech_polly\n",
|
|
|
13 |
"import os\n",
|
14 |
"\n",
|
15 |
"db = LanceVectorDb(\"qna_db\")\n",
|
16 |
-
"OPENAI_KEY = os.environ
|
17 |
"openai.api_key = OPENAI_KEY"
|
18 |
]
|
19 |
},
|
20 |
{
|
21 |
"cell_type": "code",
|
22 |
-
"execution_count":
|
23 |
"metadata": {},
|
24 |
"outputs": [],
|
25 |
"source": [
|
@@ -34,96 +50,153 @@
|
|
34 |
},
|
35 |
{
|
36 |
"cell_type": "code",
|
37 |
-
"execution_count":
|
38 |
"metadata": {},
|
39 |
"outputs": [],
|
40 |
"source": [
|
41 |
"import os\n",
|
42 |
"\n",
|
|
|
43 |
"def ensure_dir(directory):\n",
|
44 |
" if not os.path.exists(directory):\n",
|
45 |
" os.makedirs(directory)\n",
|
46 |
"\n",
|
|
|
47 |
"ensure_dir(\"audio\")"
|
48 |
]
|
49 |
},
|
50 |
{
|
51 |
"cell_type": "code",
|
52 |
-
"execution_count":
|
53 |
"metadata": {},
|
54 |
"outputs": [],
|
55 |
"source": [
|
56 |
"from langdetect import detect\n",
|
|
|
|
|
|
|
|
|
|
|
57 |
"\n",
|
58 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
" \"You are a question answering assistant.\\n\"\n",
|
60 |
-
" \"You answer questions from users based on information in our database provided as context.\\n\"\n",
|
61 |
-
" \"
|
62 |
-
" \"You
|
63 |
" \"You answer in the language that the question was asked in.\\n\"\n",
|
64 |
" \"You speak german and english.\\n\"\n",
|
65 |
")\n",
|
66 |
"\n",
|
67 |
-
"
|
68 |
-
" embedding = get_embedding(user_query)\n",
|
69 |
"\n",
|
70 |
-
"
|
71 |
-
"
|
72 |
-
"
|
73 |
-
"
|
74 |
-
"
|
75 |
-
"\n",
|
76 |
-
"
|
77 |
-
"
|
78 |
-
"
|
79 |
-
"
|
80 |
-
"
|
81 |
-
"
|
82 |
-
"
|
83 |
-
"\n",
|
84 |
-
"
|
85 |
-
"
|
86 |
-
"
|
87 |
-
"
|
|
|
88 |
"\n",
|
89 |
-
"
|
90 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
"\n",
|
92 |
" completion = openai.ChatCompletion.create(\n",
|
93 |
-
" model=\"gpt-3.5-turbo\"
|
94 |
-
" messages=chat_messages,\n",
|
95 |
" )\n",
|
96 |
-
" text = completion.choices[0].message.content\n",
|
97 |
"\n",
|
98 |
-
"
|
99 |
-
"
|
100 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
"\n",
|
102 |
-
"
|
|
|
|
|
|
|
|
|
103 |
]
|
104 |
},
|
105 |
{
|
106 |
"cell_type": "code",
|
107 |
-
"execution_count":
|
108 |
"metadata": {},
|
109 |
"outputs": [],
|
110 |
"source": [
|
111 |
"import random\n",
|
112 |
"\n",
|
|
|
113 |
"def display_history(conversation):\n",
|
114 |
" conversation_string = \"\"\n",
|
115 |
" for message in conversation:\n",
|
116 |
-
" conversation_string +=
|
|
|
|
|
117 |
" return conversation_string\n",
|
118 |
"\n",
|
119 |
-
"
|
|
|
120 |
" user_question = whisper_transcription(audio_filepath)\n",
|
121 |
" print(\"Transcription\", user_question)\n",
|
122 |
"\n",
|
123 |
-
" bot_response_text, context_prompt = bot_respond(user_question,
|
124 |
"\n",
|
125 |
-
"
|
126 |
-
"
|
|
|
|
|
|
|
127 |
"\n",
|
128 |
" if lang not in [\"en\", \"de\"]:\n",
|
129 |
" lang = \"en\"\n",
|
@@ -134,41 +207,94 @@
|
|
134 |
" context_prompt += f\"<<tts language>> : {lang}\\n\"\n",
|
135 |
" context_prompt += f\"<<tts text>> : {bot_response_text}\\n\"\n",
|
136 |
"\n",
|
137 |
-
" return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
]
|
139 |
},
|
140 |
{
|
141 |
"cell_type": "code",
|
142 |
-
"execution_count":
|
143 |
"metadata": {},
|
144 |
-
"outputs": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
"source": [
|
146 |
"import gradio as gr\n",
|
147 |
"\n",
|
148 |
"\n",
|
149 |
"with gr.Blocks() as demo:\n",
|
150 |
" # initialize the state that will be used to store the chat messages\n",
|
151 |
-
" chat_messages = gr.State(
|
|
|
|
|
|
|
|
|
152 |
"\n",
|
153 |
" with gr.Row():\n",
|
154 |
" audio_input = gr.Audio(source=\"microphone\", type=\"filepath\", format=\"mp3\")\n",
|
155 |
" # autoplay=True => run the output audio file automatically\n",
|
156 |
-
" output_audio = gr.Audio(\n",
|
157 |
-
" label=\"PhoneBot Answer TTS\", autoplay=True\n",
|
158 |
-
" )\n",
|
159 |
" with gr.Row():\n",
|
160 |
" user_query_textbox = gr.Textbox(label=\"User Query\")\n",
|
161 |
" assistant_answer = gr.Textbox(label=\"PhoneBot Answer\")\n",
|
162 |
"\n",
|
163 |
" with gr.Row():\n",
|
164 |
-
" context_info = gr.Textbox(
|
|
|
|
|
165 |
" conversation_history = gr.Textbox(label=\"Conversation history\")\n",
|
166 |
"\n",
|
167 |
" # when the audio input is stopped, run the transcribe function\n",
|
168 |
" audio_input.stop_recording(\n",
|
169 |
" handle_audiofile,\n",
|
170 |
" inputs=[audio_input, chat_messages],\n",
|
171 |
-
" outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
" )\n",
|
173 |
"\n",
|
174 |
"demo.launch(share=True, inbrowser=True, inline=False)"
|
@@ -176,34 +302,16 @@
|
|
176 |
},
|
177 |
{
|
178 |
"cell_type": "code",
|
179 |
-
"execution_count":
|
180 |
"metadata": {},
|
181 |
"outputs": [],
|
182 |
"source": [
|
183 |
"import json\n",
|
|
|
184 |
"with open(\"all_questions.json\", encoding=\"utf-8\") as f:\n",
|
185 |
" all_questions = json.load(f)[\"qna\"]"
|
186 |
]
|
187 |
},
|
188 |
-
{
|
189 |
-
"cell_type": "code",
|
190 |
-
"execution_count": 2,
|
191 |
-
"metadata": {},
|
192 |
-
"outputs": [],
|
193 |
-
"source": [
|
194 |
-
"all_questions = [{\"question\": qna[\"question\"], \"answer\": qna[\"answer\"]} for qna in all_questions]"
|
195 |
-
]
|
196 |
-
},
|
197 |
-
{
|
198 |
-
"cell_type": "code",
|
199 |
-
"execution_count": 5,
|
200 |
-
"metadata": {},
|
201 |
-
"outputs": [],
|
202 |
-
"source": [
|
203 |
-
"with open(\"test.json\", \"w\", encoding=\"utf-8\") as f:\n",
|
204 |
-
" json.dump(all_questions, f, indent=4, ensure_ascii=False)"
|
205 |
-
]
|
206 |
-
},
|
207 |
{
|
208 |
"cell_type": "code",
|
209 |
"execution_count": null,
|
@@ -215,22 +323,23 @@
|
|
215 |
},
|
216 |
{
|
217 |
"cell_type": "code",
|
218 |
-
"execution_count":
|
219 |
"metadata": {},
|
220 |
"outputs": [],
|
221 |
"source": [
|
222 |
-
"
|
223 |
-
"
|
224 |
-
"
|
225 |
-
"
|
226 |
-
"
|
227 |
-
"\n",
|
228 |
-
"
|
229 |
-
"\n",
|
230 |
-
"
|
231 |
-
"
|
232 |
-
"
|
233 |
-
")"
|
|
|
234 |
]
|
235 |
}
|
236 |
],
|
|
|
2 |
"cells": [
|
3 |
{
|
4 |
"cell_type": "code",
|
5 |
+
"execution_count": 11,
|
6 |
+
"metadata": {},
|
7 |
+
"outputs": [],
|
8 |
+
"source": [
|
9 |
+
"import json, os\n",
|
10 |
+
"\n",
|
11 |
+
"with open(\"env.json\") as f:\n",
|
12 |
+
" env_vars = json.load(f)\n",
|
13 |
+
"\n",
|
14 |
+
"for k, v in env_vars.items():\n",
|
15 |
+
" os.environ[k] = v\n"
|
16 |
+
]
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"cell_type": "code",
|
20 |
+
"execution_count": 12,
|
21 |
"metadata": {},
|
22 |
"outputs": [],
|
23 |
"source": [
|
|
|
25 |
"from vector_db import LanceVectorDb, QnA\n",
|
26 |
"from openai_utils import get_embedding, whisper_transcription\n",
|
27 |
"from audio_utils import text_to_speech, text_to_speech_polly\n",
|
28 |
+
"from openai_prompts import contextualize_question\n",
|
29 |
"import os\n",
|
30 |
"\n",
|
31 |
"db = LanceVectorDb(\"qna_db\")\n",
|
32 |
+
"OPENAI_KEY = os.environ[\"OPENAI_KEY\"]\n",
|
33 |
"openai.api_key = OPENAI_KEY"
|
34 |
]
|
35 |
},
|
36 |
{
|
37 |
"cell_type": "code",
|
38 |
+
"execution_count": 13,
|
39 |
"metadata": {},
|
40 |
"outputs": [],
|
41 |
"source": [
|
|
|
50 |
},
|
51 |
{
|
52 |
"cell_type": "code",
|
53 |
+
"execution_count": 14,
|
54 |
"metadata": {},
|
55 |
"outputs": [],
|
56 |
"source": [
|
57 |
"import os\n",
|
58 |
"\n",
|
59 |
+
"\n",
|
60 |
"def ensure_dir(directory):\n",
|
61 |
" if not os.path.exists(directory):\n",
|
62 |
" os.makedirs(directory)\n",
|
63 |
"\n",
|
64 |
+
"\n",
|
65 |
"ensure_dir(\"audio\")"
|
66 |
]
|
67 |
},
|
68 |
{
|
69 |
"cell_type": "code",
|
70 |
+
"execution_count": 25,
|
71 |
"metadata": {},
|
72 |
"outputs": [],
|
73 |
"source": [
|
74 |
"from langdetect import detect\n",
|
75 |
+
"import random\n",
|
76 |
+
"\n",
|
77 |
+
"def red(text):\n",
|
78 |
+
" return f'\\x1b[31m\"{text}\"\\x1b[0m'\n",
|
79 |
+
"\n",
|
80 |
"\n",
|
81 |
+
"def query_database(prompt: str):\n",
|
82 |
+
" print(\"Querying database for question:\", prompt)\n",
|
83 |
+
" embedding = get_embedding(prompt)\n",
|
84 |
+
" qnas = db.get_qna(embedding, lang=\"en\", limit=3)\n",
|
85 |
+
" print(\"Total_qnas:\", len(qnas), [qna.score for qna in qnas])\n",
|
86 |
+
" qnas = [qna for qna in qnas if qna.score < 0.49]\n",
|
87 |
+
" print(\"Filtered_qnas:\", len(qnas))\n",
|
88 |
+
" return qnas\n",
|
89 |
+
"\n",
|
90 |
+
"\n",
|
91 |
+
"available_functions = {\n",
|
92 |
+
" \"query_database\": query_database,\n",
|
93 |
+
"}\n",
|
94 |
+
"\n",
|
95 |
+
"conversation_folder = f\"conversations/{random.randint(0, 10000)}\"\n",
|
96 |
+
"ensure_dir(conversation_folder)\n",
|
97 |
+
"\n",
|
98 |
+
"SYSTEM_PROMPT = (\n",
|
99 |
" \"You are a question answering assistant.\\n\"\n",
|
100 |
+
" \"You answer questions from users delimited by tripple dashes --- based on information in our database provided as context.\\n\"\n",
|
101 |
+
" \"The context informtion in delimited by tripple backticks ```\\n\"\n",
|
102 |
+
" \"You try to be concise and offer the most relevant information.\\n\"\n",
|
103 |
" \"You answer in the language that the question was asked in.\\n\"\n",
|
104 |
" \"You speak german and english.\\n\"\n",
|
105 |
")\n",
|
106 |
"\n",
|
107 |
+
"step = 0\n",
|
|
|
108 |
"\n",
|
109 |
+
"def context_format(qnas):\n",
|
110 |
+
" context = \"Context:\\n\\n```\"\n",
|
111 |
+
" for qna in qnas:\n",
|
112 |
+
" context += f\"For question: {qna.question}\\nThe answer is: {qna.answer}\\n\"\n",
|
113 |
+
" context += \"```\"\n",
|
114 |
+
" return context\n",
|
115 |
+
"\n",
|
116 |
+
"\n",
|
117 |
+
"def bot_respond(user_query, history: list):\n",
|
118 |
+
" global step\n",
|
119 |
+
"\n",
|
120 |
+
" chat_messages = history[\"chat_messages\"]\n",
|
121 |
+
"\n",
|
122 |
+
" user_query = contextualize_question(user_query, chat_messages)\n",
|
123 |
+
"\n",
|
124 |
+
" path = os.path.join(conversation_folder, f\"step_{step}_qna.json\")\n",
|
125 |
+
"\n",
|
126 |
+
"\n",
|
127 |
+
" qnas = query_database(user_query)\n",
|
128 |
"\n",
|
129 |
+
" prompt = f\"The user said: ---{user_query}---\\n\\n\"\n",
|
130 |
+
"\n",
|
131 |
+
" context = context_format(qnas)\n",
|
132 |
+
" prompt += context\n",
|
133 |
+
"\n",
|
134 |
+
" qna_messages = [\n",
|
135 |
+
" {\n",
|
136 |
+
" \"role\": \"assistant\",\n",
|
137 |
+
" \"content\": SYSTEM_PROMPT,\n",
|
138 |
+
" },\n",
|
139 |
+
" {\n",
|
140 |
+
" \"role\": \"user\",\n",
|
141 |
+
" \"content\": prompt,\n",
|
142 |
+
" },\n",
|
143 |
+
" ]\n",
|
144 |
"\n",
|
145 |
" completion = openai.ChatCompletion.create(\n",
|
146 |
+
" model=\"gpt-3.5-turbo\", messages=qna_messages, temperature=0\n",
|
|
|
147 |
" )\n",
|
|
|
148 |
"\n",
|
149 |
+
" response_message = completion[\"choices\"][0][\"message\"]\n",
|
150 |
+
" bot_response = response_message.content\n",
|
151 |
+
"\n",
|
152 |
+
" path = os.path.join(conversation_folder, f\"step_{step}_qna.json\")\n",
|
153 |
+
"\n",
|
154 |
+
" with open(path, \"w\") as f:\n",
|
155 |
+
" json.dump(\n",
|
156 |
+
" {\n",
|
157 |
+
" \"chat_messages\": chat_messages,\n",
|
158 |
+
" \"response\": response_message.content,\n",
|
159 |
+
" },\n",
|
160 |
+
" f,\n",
|
161 |
+
" indent=4,\n",
|
162 |
+
" )\n",
|
163 |
"\n",
|
164 |
+
" chat_messages.append({\"role\": \"assistant\", \"content\": bot_response})\n",
|
165 |
+
"\n",
|
166 |
+
" step += 1\n",
|
167 |
+
"\n",
|
168 |
+
" return bot_response, prompt"
|
169 |
]
|
170 |
},
|
171 |
{
|
172 |
"cell_type": "code",
|
173 |
+
"execution_count": 26,
|
174 |
"metadata": {},
|
175 |
"outputs": [],
|
176 |
"source": [
|
177 |
"import random\n",
|
178 |
"\n",
|
179 |
+
"\n",
|
180 |
"def display_history(conversation):\n",
|
181 |
" conversation_string = \"\"\n",
|
182 |
" for message in conversation:\n",
|
183 |
+
" conversation_string += (\n",
|
184 |
+
" f\"<<{message['role']}>>:\\n{message['content']}\\n<<{message['role']}>>\\n\\n\"\n",
|
185 |
+
" )\n",
|
186 |
" return conversation_string\n",
|
187 |
"\n",
|
188 |
+
"\n",
|
189 |
+
"def handle_audiofile(audio_filepath: str, history: list):\n",
|
190 |
" user_question = whisper_transcription(audio_filepath)\n",
|
191 |
" print(\"Transcription\", user_question)\n",
|
192 |
"\n",
|
193 |
+
" bot_response_text, context_prompt = bot_respond(user_question, history)\n",
|
194 |
"\n",
|
195 |
+
" if bot_response_text:\n",
|
196 |
+
" lang = detect(bot_response_text)\n",
|
197 |
+
" print(\"Detected language:\", lang, \"for text:\", bot_response_text)\n",
|
198 |
+
" else:\n",
|
199 |
+
" lang = \"en\"\n",
|
200 |
"\n",
|
201 |
" if lang not in [\"en\", \"de\"]:\n",
|
202 |
" lang = \"en\"\n",
|
|
|
207 |
" context_prompt += f\"<<tts language>> : {lang}\\n\"\n",
|
208 |
" context_prompt += f\"<<tts text>> : {bot_response_text}\\n\"\n",
|
209 |
"\n",
|
210 |
+
" return (\n",
|
211 |
+
" user_question,\n",
|
212 |
+
" bot_response_text,\n",
|
213 |
+
" history,\n",
|
214 |
+
" context_prompt,\n",
|
215 |
+
" display_history(history[\"chat_messages\"]),\n",
|
216 |
+
" output_filepath,\n",
|
217 |
+
" )"
|
218 |
]
|
219 |
},
|
220 |
{
|
221 |
"cell_type": "code",
|
222 |
+
"execution_count": 27,
|
223 |
"metadata": {},
|
224 |
+
"outputs": [
|
225 |
+
{
|
226 |
+
"name": "stdout",
|
227 |
+
"output_type": "stream",
|
228 |
+
"text": [
|
229 |
+
"Running on local URL: http://127.0.0.1:7880\n",
|
230 |
+
"Running on public URL: https://80ddf0a828a1240987.gradio.live\n",
|
231 |
+
"\n",
|
232 |
+
"This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)\n"
|
233 |
+
]
|
234 |
+
},
|
235 |
+
{
|
236 |
+
"data": {
|
237 |
+
"text/plain": []
|
238 |
+
},
|
239 |
+
"execution_count": 27,
|
240 |
+
"metadata": {},
|
241 |
+
"output_type": "execute_result"
|
242 |
+
},
|
243 |
+
{
|
244 |
+
"name": "stdout",
|
245 |
+
"output_type": "stream",
|
246 |
+
"text": [
|
247 |
+
"Transcription What locations are available for measurement?\n",
|
248 |
+
"Querying database for question: What locations are available for measurement?\n",
|
249 |
+
"Total_qnas: 3 [0.43219417333602905, 0.43802204728126526, 0.4490761160850525]\n",
|
250 |
+
"Filtered_qnas: 3\n",
|
251 |
+
"Detected language: en for text: The available locations for measurement are Salzburg and Hallein. In Salzburg, there is an underground car park and individual parking spaces available. The Salzburg location is also easily accessible by O-Bus. In Hallein, there are plenty of parking spaces directly in front of the building.\n",
|
252 |
+
"Transcription How much time does the measurement take?\n",
|
253 |
+
"Querying database for question: How much time does the measurement at each location take?\n",
|
254 |
+
"Total_qnas: 3 [0.3830512762069702, 0.383282870054245, 0.38819530606269836]\n",
|
255 |
+
"Filtered_qnas: 3\n",
|
256 |
+
"Detected language: en for text: The measurement at each location takes about 5-15 minutes, depending on the question and number of examination areas.\n"
|
257 |
+
]
|
258 |
+
}
|
259 |
+
],
|
260 |
"source": [
|
261 |
"import gradio as gr\n",
|
262 |
"\n",
|
263 |
"\n",
|
264 |
"with gr.Blocks() as demo:\n",
|
265 |
" # initialize the state that will be used to store the chat messages\n",
|
266 |
+
" chat_messages = gr.State(\n",
|
267 |
+
" {\n",
|
268 |
+
" \"chat_messages\": [{\"role\": \"system\", \"content\": SYSTEM_PROMPT}],\n",
|
269 |
+
" }\n",
|
270 |
+
" )\n",
|
271 |
"\n",
|
272 |
" with gr.Row():\n",
|
273 |
" audio_input = gr.Audio(source=\"microphone\", type=\"filepath\", format=\"mp3\")\n",
|
274 |
" # autoplay=True => run the output audio file automatically\n",
|
275 |
+
" output_audio = gr.Audio(label=\"PhoneBot Answer TTS\", autoplay=True)\n",
|
|
|
|
|
276 |
" with gr.Row():\n",
|
277 |
" user_query_textbox = gr.Textbox(label=\"User Query\")\n",
|
278 |
" assistant_answer = gr.Textbox(label=\"PhoneBot Answer\")\n",
|
279 |
"\n",
|
280 |
" with gr.Row():\n",
|
281 |
+
" context_info = gr.Textbox(\n",
|
282 |
+
" label=\"Context provided to the bot + additional infos for debugging\"\n",
|
283 |
+
" )\n",
|
284 |
" conversation_history = gr.Textbox(label=\"Conversation history\")\n",
|
285 |
"\n",
|
286 |
" # when the audio input is stopped, run the transcribe function\n",
|
287 |
" audio_input.stop_recording(\n",
|
288 |
" handle_audiofile,\n",
|
289 |
" inputs=[audio_input, chat_messages],\n",
|
290 |
+
" outputs=[\n",
|
291 |
+
" user_query_textbox,\n",
|
292 |
+
" assistant_answer,\n",
|
293 |
+
" chat_messages,\n",
|
294 |
+
" context_info,\n",
|
295 |
+
" conversation_history,\n",
|
296 |
+
" output_audio,\n",
|
297 |
+
" ],\n",
|
298 |
" )\n",
|
299 |
"\n",
|
300 |
"demo.launch(share=True, inbrowser=True, inline=False)"
|
|
|
302 |
},
|
303 |
{
|
304 |
"cell_type": "code",
|
305 |
+
"execution_count": 109,
|
306 |
"metadata": {},
|
307 |
"outputs": [],
|
308 |
"source": [
|
309 |
"import json\n",
|
310 |
+
"\n",
|
311 |
"with open(\"all_questions.json\", encoding=\"utf-8\") as f:\n",
|
312 |
" all_questions = json.load(f)[\"qna\"]"
|
313 |
]
|
314 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
315 |
{
|
316 |
"cell_type": "code",
|
317 |
"execution_count": null,
|
|
|
323 |
},
|
324 |
{
|
325 |
"cell_type": "code",
|
326 |
+
"execution_count": 7,
|
327 |
"metadata": {},
|
328 |
"outputs": [],
|
329 |
"source": [
|
330 |
+
"from genson import SchemaBuilder\n",
|
331 |
+
"import json\n",
|
332 |
+
"\n",
|
333 |
+
"builder = SchemaBuilder()\n",
|
334 |
+
"obj = {\n",
|
335 |
+
" \"response_type\": \"Which city do you want to book an appointment in?\",\n",
|
336 |
+
" \"content\": \"The user wants to book an appointment\",\n",
|
337 |
+
"}\n",
|
338 |
+
"builder.add_object(obj)\n",
|
339 |
+
"\n",
|
340 |
+
"schema = builder.to_schema()\n",
|
341 |
+
"schema.pop(\"$schema\")\n",
|
342 |
+
"schema_str = json.dumps(schema, indent=4)"
|
343 |
]
|
344 |
}
|
345 |
],
|
openai_prompts/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
from .contextualize import contextualize_question
|
openai_prompts/contextualize.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
|
3 |
+
import openai
|
4 |
+
|
5 |
+
functions = [
|
6 |
+
{
|
7 |
+
"name": "with_context",
|
8 |
+
"description": "Updates the question with context from the conversation to make it more clear, adds details, replaces general words like 'there', 'that', pronouns with the values mentioned in conversation.",
|
9 |
+
"parameters": {
|
10 |
+
"type": "object",
|
11 |
+
"properties": {
|
12 |
+
"contextualized_question": {
|
13 |
+
"type": "string",
|
14 |
+
"description": "The contextualized question",
|
15 |
+
}
|
16 |
+
},
|
17 |
+
"required": ["contextualized_question"],
|
18 |
+
},
|
19 |
+
}
|
20 |
+
]
|
21 |
+
|
22 |
+
|
23 |
+
def contextualize_question(user_query: str, messages: list[str]):
|
24 |
+
prompt = (
|
25 |
+
f"The user said: {user_query}\n\n"
|
26 |
+
"If the user is asking a question, make sure you contextualize it using the replies exchanged before, add details to the question.\n"
|
27 |
+
"The previous messages are:\n"
|
28 |
+
)
|
29 |
+
|
30 |
+
messages_for_context = messages[1:]
|
31 |
+
for message in messages_for_context:
|
32 |
+
prompt += f"<<{message['role']}>>{message['content']}<<{message['role']}>>\n"
|
33 |
+
|
34 |
+
res = openai.ChatCompletion.create(
|
35 |
+
model="gpt-3.5-turbo",
|
36 |
+
messages=[
|
37 |
+
{
|
38 |
+
"role": "system",
|
39 |
+
"content": "You are a helpful assistant adds context to vague questions.",
|
40 |
+
},
|
41 |
+
{"role": "user", "content": prompt},
|
42 |
+
],
|
43 |
+
functions=functions,
|
44 |
+
function_call={"name": "with_context"}, # force the function to be called
|
45 |
+
temperature=0.2,
|
46 |
+
)
|
47 |
+
|
48 |
+
try:
|
49 |
+
arguments = res["choices"][0]["message"]["function_call"]["arguments"]
|
50 |
+
|
51 |
+
result_data = json.loads(arguments)
|
52 |
+
|
53 |
+
return result_data["contextualized_question"]
|
54 |
+
except Exception as error:
|
55 |
+
print(error)
|
56 |
+
return last_reply
|