Wynand du Plessis commited on
Commit
7367e75
·
1 Parent(s): 9d8f76c

updated ui to focus on chatbot interface

Browse files
Files changed (3) hide show
  1. planning/Prompts Planning.txt +3 -0
  2. requirements.txt +2 -1
  3. stream_app.py +88 -86
planning/Prompts Planning.txt CHANGED
@@ -65,3 +65,6 @@ Let's start with Warm-up Begin by greeting the student and briefly discussing th
65
  # CEFR levels:
66
 
67
  ## A1
 
 
 
 
65
  # CEFR levels:
66
 
67
  ## A1
68
+
69
+ # Conversation based on photo
70
+ Act as a Spanish dialogue writer for student textbooks. The student is B1. Generate a dialogue with as many or as few characters as you need. Keep the conversation only in Spanish. Discuss the photo. Make it interesting.
requirements.txt CHANGED
@@ -2,4 +2,5 @@ gradio>=4.1
2
  openai>=1.0.0
3
  openai-whisper
4
  pyautogen
5
- ollama
 
 
2
  openai>=1.0.0
3
  openai-whisper
4
  pyautogen
5
+ ollama
6
+ ffmpeg
stream_app.py CHANGED
@@ -41,8 +41,8 @@ def run_gradio(config:dict):
41
 
42
  def get_teacher_prompt(language_input, cefr_level_input, is_initial_image):
43
  global teacher_prompt
44
- teacher_prompt = f"Act as a {language_input} teacher only speaking in {language_input}. The student is still learning {language_input}, so explain topics in simple words and ask questions to continue the conversation. Repeat and restate what the student says when they respond. Keep it highly conversational because you're talking with the student. The student is just starting to learn, so keep it simple. The speaker is able to speak and understand at {cefr_level_input}."
45
- teacher_image_prompt = f"Your job is to understand the following image and create a {language_input} lesson around it. Describe the exercise or situation that you see. Provide the student with instructions, a simple example, and then ask the student to participate. Only speak in {language_input} at a {cefr_level_input} level."
46
  if is_initial_image is True:
47
  return teacher_prompt+teacher_image_prompt
48
  return teacher_prompt
@@ -88,22 +88,32 @@ def run_gradio(config:dict):
88
  # audio = whisper.clear?
89
  return "", None # return empty, clear prior file
90
 
91
- def clear_inputs(input_audio, input_text):
92
- return None, None
 
 
 
 
 
 
 
 
93
 
94
  # speak input text
95
- def audio_speak(input_text, speaker_name, input_done=True, offset_prior=0, path_prior=None, auto_speak=None):
96
  # alternate on-device? - https://github.com/suno-ai/bark?tab=readme-ov-file
97
  # print(f"Speak: {input_text}, {offset_prior} of {len(input_text)}")
98
 
 
 
99
 
100
  if not input_text: # empty string on conclusion (when streaming)
101
- return gr.Audio(), None, 0
102
  elif auto_speak is not None:
103
  if "manual" in auto_speak.lower(): # don't proceed if manual
104
- return gr.Audio(), None, 0
105
  elif (not input_done) and ("stream" not in auto_speak.lower()): # stream, not done
106
- return gr.Audio(), None, 0
107
 
108
  if (path_prior is None) or (offset_prior > len(input_text)):
109
  temp_file = tempfile.NamedTemporaryFile(delete=False)
@@ -120,7 +130,10 @@ def run_gradio(config:dict):
120
  with open(path_prior, 'ab') as file_append:
121
  for chunk in response.iter_bytes(chunk_size=4096):
122
  file_append.write(chunk)
123
- return path_prior, path_prior, offset_prior
 
 
 
124
 
125
  # Define Gradio interface
126
  def start_initial_conversation(language_input, cefr_level_input, input_image, model_target=None):
@@ -183,15 +196,10 @@ def run_gradio(config:dict):
183
  # def initial_upload_complete():
184
  # return gr.update(visible=True), gr.update(visible=True)
185
  def add_message(history, message, ai_response=False):
186
- # type is either AI or human
187
- # if type not in ["AI","human"]:
188
- # raise ValueError("type must be either AI or human")
189
- # if type == "human":
190
- message_type = str(type(message))
191
- logger.info(f"message input type: {message_type}")
192
 
193
- if "PIL.Image.Image" in message_type:
194
- # TODO: Figure out image upload
 
195
 
196
  # # Save the image to a buffer
197
  # buffer = io.BytesIO()
@@ -202,7 +210,10 @@ def run_gradio(config:dict):
202
  # history.append((input_image_base64,None))
203
  # return history
204
 
205
- message = "Initial image uploaded"
 
 
 
206
 
207
  if ai_response is True:
208
  history[-1][1] = message
@@ -226,7 +237,6 @@ def run_gradio(config:dict):
226
 
227
  logger.info(f"Prompt: {prompt}")
228
 
229
- # TODO: Add in full chat history again
230
  messages=[
231
  {"role": "system", "content": system_prompt+teacher_prompt},
232
  {"role": "user", "content": prompt},
@@ -269,49 +279,43 @@ def run_gradio(config:dict):
269
  generate_done = gr.State(False) # is last genai content chunked?
270
  path_prior = gr.State(None) # retain prior file for audio playback
271
  offset_prior = gr.State(0) # track textual offset in genrated content
 
272
  # initial_image_uploaded = gr.State(False) # visibility of chat sections
273
 
274
  gr.Markdown("""
275
- # Capture an image, and our AI guides you through a conversation in the language of your choice at your level
276
  """)
277
  with gr.Row():
278
  with gr.Column():
279
- with gr.Group():
280
- with gr.Row():
281
- language_input = gr.Dropdown(
282
- ["English","French","Mandarin","Spanish","German","Italian"], value="Spanish", label="Target Language", info="Select the language you're learning", interactive=True
283
- )
284
- cefr_level_input = gr.Dropdown(
285
- ["A0 - brand new","A1 - basic phrases","A2 - basic interactions","B1 - basic conversation","B2 - conversational"], value="A0 - brand new", label="Your CEFR Level", info="Your currently ability in the language", interactive=True
286
- )
287
-
288
- with gr.Group():
289
- image_input = gr.Image(
290
- label="Image Input",
291
- type="pil",
292
- )
293
- # image_submit_button = gr.Button("Start conversation", variant='primary') # trigger automatically instead of trigger
294
 
 
 
 
 
 
295
 
296
  with gr.Group() as chat_response_section:
297
  chatbot = gr.Chatbot(
298
  elem_id="chatbot",
299
  bubble_full_width=True,
300
  scale=1,
 
301
  )
302
  audio_input = gr.Audio(
303
  label="Speech Input",
304
  # streaming=True, # true for stream to text
305
  sources="microphone",
306
  type="filepath",
 
307
  )
308
  input_text = gr.Textbox(
309
  label="Text Input",
310
  placeholder="Enter your prompt here or use speech recognition to generate it.",
311
  lines=5,
312
  max_lines=5,
 
313
  )
314
- submit_button = gr.Button("Send to teacher", variant='primary')
315
 
316
  # with gr.Group():
317
  # chat_interface = gr.ChatInterface(yes_man,
@@ -322,9 +326,9 @@ def run_gradio(config:dict):
322
 
323
  with gr.Row() as input_details_section:
324
  with gr.Group():
325
- with gr.Accordion("Teacher Response Details", open=True):
326
  audio_playback = gr.Audio(
327
- label="Speech", autoplay=True, streaming=False,
328
  type="filepath", sources=None,
329
  )
330
  output_text = gr.Textbox(
@@ -336,12 +340,19 @@ def run_gradio(config:dict):
336
 
337
  with gr.Group():
338
  with gr.Accordion("Settings", open=False):
339
- # teacher_text = gr.Textbox(
340
- # label="Teacher Prompt",
341
- # lines=5,
342
- # max_lines=5,
343
- # interactive=False
344
- # )
 
 
 
 
 
 
 
345
  prompt_model = gr.Radio(
346
  label="Textual Model", show_label=False,
347
  choices=[online_text_model, offline_text_model],
@@ -364,72 +375,64 @@ def run_gradio(config:dict):
364
  with gr.Row():
365
  combo_autospeak = gr.Radio(
366
  choices=["Auto-speak", "Auto-speak (stream)", "Manual"], show_label=False,
367
- value="Auto-speak", interactive=True,
368
  )
369
 
370
 
371
 
372
  # language_input.change() # can update the teacher prompt
373
  # cefr_level_input.change() # can update the teacher prompt
 
374
  initial_image_uploaded = image_input.upload(add_message, # uploaded image, add to chat
375
  inputs=[chatbot, image_input],
376
  outputs=[chatbot])
 
 
 
 
377
  text_response_generate = initial_image_uploaded.then(start_initial_conversation, # uploaded image, start response
378
  inputs=[language_input,cefr_level_input, image_input, prompt_model],
379
  outputs=[output_text, generate_done])
380
 
381
- # TODO: Be able to log audio response to chat
382
- # audio_response_generate.then(add_message, # generated response, add to chat
383
- # inputs=[chatbot, audio_playback],
384
- # outputs=[chatbot])
385
-
386
-
387
- # audio_input.stream(audio_transcribe, # started streaming to transcribe
388
- # inputs=[audio_input_model, audio_input, audio_threshold, input_text],
389
- # outputs=input_text)
390
  audio_input.clear(audio_reset, # cleared audio
391
  inputs=[input_text, path_prior],
392
  outputs=[input_text, path_prior])
393
- audio_input.start_recording(audio_reset, # started a new speech->text
394
  inputs=[input_text, path_prior],
395
  outputs=[input_text, path_prior])
396
- # TODO: Can I just send the audio file to the chatbot component
397
- audio_input.stop_recording(audio_transcribe, # stop recording, create text
 
 
398
  inputs=[audio_input_model, audio_input, audio_threshold, input_text],
399
  outputs=input_text)
400
- # audio_input.stop_recording(get_ai_response, # stopped recording, start response
401
- # inputs=[input_text, full_chat_context, prompt_model],
402
- # outputs=[output_text, full_chat_context, generate_done])
403
 
404
- # TODO: This needs to take in the audio file
405
- student_input = submit_button.click(add_message, # send in sound / text file
406
- inputs=[chatbot, input_text],
407
- outputs=[chatbot])
 
408
 
409
- student_input.then(clear_inputs, # send in sound / text file
410
- inputs=[audio_input, input_text],
411
- outputs=[audio_input, input_text])
412
 
413
- student_input.then(get_ai_response, # send in sound / text file
 
 
 
 
 
 
414
  inputs=[input_text, chatbot, prompt_model],
415
  outputs=[output_text, generate_done])
416
-
417
- speak_button.click(audio_speak, # click for speak trigger
418
- inputs=[output_text, combo_speaker],
419
- outputs=[audio_playback, path_prior, offset_prior])
420
-
421
- output_text_logged = output_text.change(add_message, # generated response, add to chat
422
- inputs=[chatbot, output_text, gr.State(value=True)],
423
- outputs=[chatbot])
424
 
425
- output_text_logged.then(audio_speak, # streaming response from generate
426
- inputs=[output_text, combo_speaker, generate_done, offset_prior, path_prior, combo_autospeak],
427
- outputs=[audio_playback, path_prior, offset_prior])
428
-
429
- # output_text_logged.then(audio_speak, # streaming response from generate
430
- # inputs=[output_text, combo_speaker, generate_done, offset_prior, path_prior, combo_autospeak],
431
- # outputs=[audio_playback, path_prior, offset_prior])
432
-
433
 
434
  # demo.set_api_mode(enabled=False) # Disable API exposure
435
  # demo.set_footer(enabled=False) # Disable Gradio footers
@@ -478,5 +481,4 @@ if __name__ == "__main__":
478
  raise ValueError("OPENAI_API_KEY environment variable not set as environment variable or as a setting in `.env`. (see https://platform.openai.com/docs/quickstart/step-2-set-up-your-api-key)")
479
 
480
  config = parse_args()
481
- run_gradio(config)
482
-
 
41
 
42
  def get_teacher_prompt(language_input, cefr_level_input, is_initial_image):
43
  global teacher_prompt
44
+ teacher_prompt = f"Act as a {language_input} teacher only speaking in {language_input}. Help me learn spanish."
45
+ teacher_image_prompt = f"Here is a photo to start the conversation."
46
  if is_initial_image is True:
47
  return teacher_prompt+teacher_image_prompt
48
  return teacher_prompt
 
88
  # audio = whisper.clear?
89
  return "", None # return empty, clear prior file
90
 
91
+ def reset_inputs(input_audio,input_audio_2,input_text):
92
+ return None, gr.Audio(interactive=True),gr.Text(visible=False)
93
+ def reset_audio_generate(audio_generate_done):
94
+ return False
95
+ def hide_image_input(image_input):
96
+ return gr.Image(visible=False)
97
+ def show_chatbot(chatbot,audio_input,submit_button):
98
+ return gr.Chatbot(visible=True),gr.Audio(visible=True)
99
+ def stop_recording(audio_input, text_input):
100
+ return gr.Audio(interactive=False),gr.Text(visible=True)
101
 
102
  # speak input text
103
+ def audio_speak(input_text, speaker_name, input_done=True, offset_prior=0, path_prior=None, auto_speak=None, audio_generate_done=False):
104
  # alternate on-device? - https://github.com/suno-ai/bark?tab=readme-ov-file
105
  # print(f"Speak: {input_text}, {offset_prior} of {len(input_text)}")
106
 
107
+ logger.info(f"Speak: {input_text}, {offset_prior} of {len(input_text)}")
108
+
109
 
110
  if not input_text: # empty string on conclusion (when streaming)
111
+ return gr.Audio(), None, 0, False
112
  elif auto_speak is not None:
113
  if "manual" in auto_speak.lower(): # don't proceed if manual
114
+ return gr.Audio(), None, 0, False
115
  elif (not input_done) and ("stream" not in auto_speak.lower()): # stream, not done
116
+ return gr.Audio(), None, 0, False
117
 
118
  if (path_prior is None) or (offset_prior > len(input_text)):
119
  temp_file = tempfile.NamedTemporaryFile(delete=False)
 
130
  with open(path_prior, 'ab') as file_append:
131
  for chunk in response.iter_bytes(chunk_size=4096):
132
  file_append.write(chunk)
133
+
134
+ logger.info(f"audio processed")
135
+
136
+ return path_prior, path_prior, offset_prior, True
137
 
138
  # Define Gradio interface
139
  def start_initial_conversation(language_input, cefr_level_input, input_image, model_target=None):
 
196
  # def initial_upload_complete():
197
  # return gr.update(visible=True), gr.update(visible=True)
198
  def add_message(history, message, ai_response=False):
 
 
 
 
 
 
199
 
200
+ logger.info(f"adding message to chat")
201
+ logger.info(f"message: {message}")
202
+
203
 
204
  # # Save the image to a buffer
205
  # buffer = io.BytesIO()
 
210
  # history.append((input_image_base64,None))
211
  # return history
212
 
213
+ if ".wav" in str(message):
214
+ message = gr.Audio(message,autoplay=True,label="Speech", streaming=False, type="filepath", sources=None,)
215
+ if "PIL.Image.Image" in str(message):
216
+ message = gr.Image(message)
217
 
218
  if ai_response is True:
219
  history[-1][1] = message
 
237
 
238
  logger.info(f"Prompt: {prompt}")
239
 
 
240
  messages=[
241
  {"role": "system", "content": system_prompt+teacher_prompt},
242
  {"role": "user", "content": prompt},
 
279
  generate_done = gr.State(False) # is last genai content chunked?
280
  path_prior = gr.State(None) # retain prior file for audio playback
281
  offset_prior = gr.State(0) # track textual offset in genrated content
282
+ audio_generate_done = gr.State(False) # track textual offset in genrated content
283
  # initial_image_uploaded = gr.State(False) # visibility of chat sections
284
 
285
  gr.Markdown("""
286
+ # Capture an image to start a conversation with our AI language tutor.
287
  """)
288
  with gr.Row():
289
  with gr.Column():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
+ image_input = gr.Image(
292
+ label="Image Input",
293
+ type="pil",
294
+ )
295
+ # image_submit_button = gr.Button("Start conversation", variant='primary') # trigger automatically instead of trigger
296
 
297
  with gr.Group() as chat_response_section:
298
  chatbot = gr.Chatbot(
299
  elem_id="chatbot",
300
  bubble_full_width=True,
301
  scale=1,
302
+ visible=False
303
  )
304
  audio_input = gr.Audio(
305
  label="Speech Input",
306
  # streaming=True, # true for stream to text
307
  sources="microphone",
308
  type="filepath",
309
+ visible=False
310
  )
311
  input_text = gr.Textbox(
312
  label="Text Input",
313
  placeholder="Enter your prompt here or use speech recognition to generate it.",
314
  lines=5,
315
  max_lines=5,
316
+ visible=False
317
  )
318
+ # submit_button = gr.Button("Send Response", variant='primary',visible=False)
319
 
320
  # with gr.Group():
321
  # chat_interface = gr.ChatInterface(yes_man,
 
326
 
327
  with gr.Row() as input_details_section:
328
  with gr.Group():
329
+ with gr.Accordion("Transcription and Audio Details", open=False):
330
  audio_playback = gr.Audio(
331
+ label="Speech", autoplay=False, streaming=False,
332
  type="filepath", sources=None,
333
  )
334
  output_text = gr.Textbox(
 
340
 
341
  with gr.Group():
342
  with gr.Accordion("Settings", open=False):
343
+ with gr.Row():
344
+ language_input = gr.Dropdown(
345
+ ["English","French","Mandarin","Spanish","German","Italian"], value="Spanish", label="Target Language", info="Select the language you're learning", interactive=True
346
+ )
347
+ cefr_level_input = gr.Dropdown(
348
+ ["A0 - brand new","A1 - basic phrases","A2 - basic interactions","B1 - basic conversation","B2 - conversational"], value="A0 - brand new", label="Your CEFR Level", info="Your currently ability in the language", interactive=True
349
+ )
350
+ teacher_text = gr.Textbox(
351
+ label="Teacher Prompt",
352
+ lines=5,
353
+ max_lines=5,
354
+ interactive=False
355
+ )
356
  prompt_model = gr.Radio(
357
  label="Textual Model", show_label=False,
358
  choices=[online_text_model, offline_text_model],
 
375
  with gr.Row():
376
  combo_autospeak = gr.Radio(
377
  choices=["Auto-speak", "Auto-speak (stream)", "Manual"], show_label=False,
378
+ value="Auto-speak", interactive=False,
379
  )
380
 
381
 
382
 
383
  # language_input.change() # can update the teacher prompt
384
  # cefr_level_input.change() # can update the teacher prompt
385
+
386
  initial_image_uploaded = image_input.upload(add_message, # uploaded image, add to chat
387
  inputs=[chatbot, image_input],
388
  outputs=[chatbot])
389
+ initial_image_uploaded.then(show_chatbot,
390
+ inputs=[chatbot,audio_input],
391
+ outputs=[chatbot,audio_input])
392
+ initial_image_uploaded.then(hide_image_input,image_input,image_input)
393
  text_response_generate = initial_image_uploaded.then(start_initial_conversation, # uploaded image, start response
394
  inputs=[language_input,cefr_level_input, image_input, prompt_model],
395
  outputs=[output_text, generate_done])
396
 
 
 
 
 
 
 
 
 
 
397
  audio_input.clear(audio_reset, # cleared audio
398
  inputs=[input_text, path_prior],
399
  outputs=[input_text, path_prior])
400
+ audio_input.start_recording(audio_reset, # started a new speech recording
401
  inputs=[input_text, path_prior],
402
  outputs=[input_text, path_prior])
403
+ stop_input_recording = audio_input.stop_recording(stop_recording, # stop recording, create text
404
+ inputs=[audio_input,input_text],
405
+ outputs=[audio_input,input_text])
406
+ stop_input_recording.then(audio_transcribe, # stop recording, create text
407
  inputs=[audio_input_model, audio_input, audio_threshold, input_text],
408
  outputs=input_text)
 
 
 
409
 
410
+ #TODO: Submit button before text generation complete
411
+ #TODO: Handle submit button press still recording
412
+ # input_text.change(get_ai_response, # transcription done, submit to bot
413
+ # inputs=[input_text, chatbot, prompt_model],
414
+ # outputs=[output_text, generate_done]))
415
 
416
+ # output_text_logged = output_text.change(add_message, # generated response, add to chat
417
+ # inputs=[chatbot, output_text, gr.State(value=True)],
418
+ # outputs=[chatbot])
419
 
420
+ student_submit = input_text.change(add_message, # submit, update chatbot
421
+ inputs=[chatbot, audio_input],
422
+ outputs=[chatbot])
423
+ student_submit.then(reset_inputs, # then clear speech input
424
+ inputs=[audio_input,audio_input,input_text],
425
+ outputs=[audio_input,audio_input,input_text])
426
+ student_submit.then(get_ai_response, # then get ai response
427
  inputs=[input_text, chatbot, prompt_model],
428
  outputs=[output_text, generate_done])
 
 
 
 
 
 
 
 
429
 
430
+ output_text_generated = output_text.change(audio_speak, # streaming response from generate
431
+ inputs=[output_text, combo_speaker, generate_done, offset_prior, path_prior, combo_autospeak, audio_generate_done],
432
+ outputs=[audio_playback, path_prior, offset_prior, audio_generate_done])
433
+ audio_playback.change(add_message, # generated audio, add to chat
434
+ inputs=[chatbot, audio_playback, gr.State(value=True)],
435
+ outputs=[chatbot]).then(reset_audio_generate,audio_generate_done,audio_generate_done)
 
 
436
 
437
  # demo.set_api_mode(enabled=False) # Disable API exposure
438
  # demo.set_footer(enabled=False) # Disable Gradio footers
 
481
  raise ValueError("OPENAI_API_KEY environment variable not set as environment variable or as a setting in `.env`. (see https://platform.openai.com/docs/quickstart/step-2-set-up-your-api-key)")
482
 
483
  config = parse_args()
484
+ run_gradio(config)