HRPBloom commited on
Commit
019eaa1
·
verified ·
1 Parent(s): 24d4caa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -51
app.py CHANGED
@@ -57,10 +57,8 @@ Your role:
57
 
58
  Always respond in the same language as the user's question, except when translation is requested.
59
  When providing Mandarin text, include pinyin pronunciation in parentheses if helpful.
60
- Be encouraging and patient – the user is learning.
61
- """
62
 
63
- # Voice configuration options (mapped from React components)
64
  ACCENT_EMOJI = {
65
  "american": "🇺🇸", "british": "🇬🇧", "australian": "🇦🇺", "canadian": "🇨🇦",
66
  "irish": "🇮🇪", "scottish": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", "indian": "🇮🇳", "south-african": "🇿🇦",
@@ -97,17 +95,11 @@ def transcribe_audio(audio_path, token=None):
97
  return f"[STT Error: {str(e)}]"
98
 
99
  def synthesize_speech(text, token=None, accent="chinese", gender="female", age=30):
100
- """Convert text to speech with voice characteristics."""
101
  if not text:
102
  return None
103
  client = get_client(token)
104
- # Construct a speaker prompt that Bark understands.
105
- # Bark can be guided with a description like "A young female Chinese speaker".
106
- # We'll combine the options into a natural description.
107
  age_desc = f"{age} year old" if age else ""
108
  voice_desc = f"{age_desc} {gender} {accent} speaker".strip()
109
- # Prepend the description to the text, separated by a special token or just as context.
110
- # Bark often uses a prompt like: "[Voice description] Text to speak."
111
  prompt = f"[{voice_desc}] {text}"
112
  try:
113
  audio_bytes = client.text_to_speech(prompt, model="suno/bark")
@@ -118,21 +110,28 @@ def synthesize_speech(text, token=None, accent="chinese", gender="female", age=3
118
  print(f"TTS error: {e}")
119
  return None
120
 
121
- def generate_response(message, history, model_name, profile, native_lang, token=None):
 
122
  if not message.strip():
123
- yield "Please enter a message."
124
- return
 
125
 
 
 
 
 
 
 
126
  client = get_client(token)
127
  system_prompt = SYSTEM_PROMPT_TEMPLATE.format(**profile)
128
  messages = [{"role": "system", "content": system_prompt}]
129
- for user_msg, assistant_msg in history:
130
- if user_msg:
131
- messages.append({"role": "user", "content": user_msg})
132
- if assistant_msg:
133
- messages.append({"role": "assistant", "content": assistant_msg})
134
- messages.append({"role": "user", "content": message})
135
-
136
  model_id = MODEL_MAP.get(model_name, MODEL_MAP[DEFAULT_MODEL])
137
  try:
138
  stream = client.chat_completion(
@@ -145,14 +144,19 @@ def generate_response(message, history, model_name, profile, native_lang, token=
145
  for chunk in stream:
146
  if chunk.choices and chunk.choices[0].delta.content:
147
  partial += chunk.choices[0].delta.content
148
- yield partial
 
 
149
  except Exception as e:
150
  error_msg = f"Error: {str(e)}"
151
  if "401" in str(e) or "Authorization" in str(e):
152
  error_msg = "⚠️ Authentication failed. Please provide a valid Hugging Face token."
153
- yield error_msg
 
 
154
 
155
- def voice_input(audio, history, token=None):
 
156
  if audio is None:
157
  return history, "", None
158
  transcript = transcribe_audio(audio, token)
@@ -161,14 +165,45 @@ def voice_input(audio, history, token=None):
161
  return history, "", None
162
  return history, transcript, audio
163
 
164
- def send_message(message, history):
165
- if not message.strip():
166
- return history, ""
167
- history.append((message, None))
168
- return history, ""
169
-
170
- def quick_action_clicked(action, history):
171
- return action, history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  def update_profile(name, native_lang, target_job, skills, location):
174
  return {
@@ -192,10 +227,16 @@ def toggle_ui_language(lang):
192
  def clear_chat():
193
  return [], None
194
 
 
 
 
 
 
 
195
  # ----------------------------------------------------------------------
196
  # Gradio Interface
197
  # ----------------------------------------------------------------------
198
- with gr.Blocks(title="Mandarin Job Application Assistant", theme=gr.themes.Soft()) as demo:
199
  gr.Markdown("""
200
  # 🗣️ Mandarin Job Application Assistant
201
  **For non‑Mandarin speakers applying to jobs that require Mandarin.**
@@ -250,14 +291,13 @@ with gr.Blocks(title="Mandarin Job Application Assistant", theme=gr.themes.Soft(
250
  interactive=True
251
  )
252
 
253
- # Voice Configuration (inspired by React components)
254
  gr.Markdown("### 🎙️ Voice Settings (for spoken responses)")
255
  with gr.Row():
256
  accent = gr.Dropdown(
257
  choices=list(ACCENT_EMOJI.keys()),
258
  value="chinese",
259
- label="Accent",
260
- info="Choose the accent for the spoken response"
261
  )
262
  gender = gr.Radio(
263
  choices=["male", "female", "non-binary", "other"],
@@ -313,10 +353,10 @@ with gr.Blocks(title="Mandarin Job Application Assistant", theme=gr.themes.Soft(
313
 
314
  # Send message (text input)
315
  send_btn.click(
316
- send_message, inputs=[msg, chat_state], outputs=[chatbot, msg]
317
  ).then(
318
- generate_response,
319
- inputs=[msg, chat_state, model, profile_state, native_lang_input, token_state],
320
  outputs=chatbot
321
  )
322
 
@@ -324,8 +364,10 @@ with gr.Blocks(title="Mandarin Job Application Assistant", theme=gr.themes.Soft(
324
  voice_btn.click(
325
  voice_input, inputs=[audio_input, chat_state, token_state], outputs=[chatbot, msg, audio_input]
326
  ).then(
327
- generate_response,
328
- inputs=[msg, chat_state, model, profile_state, native_lang_input, token_state],
 
 
329
  outputs=chatbot
330
  )
331
 
@@ -333,11 +375,11 @@ with gr.Blocks(title="Mandarin Job Application Assistant", theme=gr.themes.Soft(
333
  all_quick_btns = [btn_en1, btn_en2, btn_en3, btn_en4, btn_ms1, btn_ms2, btn_ms3, btn_ms4]
334
  for btn in all_quick_btns:
335
  btn.click(
336
- quick_action_clicked, inputs=[btn, chat_state], outputs=[msg, chat_state]
337
- ).then(
338
- generate_response,
339
- inputs=[msg, chat_state, model, profile_state, native_lang_input, token_state],
340
  outputs=chatbot
 
 
341
  )
342
 
343
  # UI language toggle
@@ -347,13 +389,7 @@ with gr.Blocks(title="Mandarin Job Application Assistant", theme=gr.themes.Soft(
347
  outputs=[btn_en1, btn_en2, btn_en3, btn_en4, btn_ms1, btn_ms2, btn_ms3, btn_ms4]
348
  )
349
 
350
- # TTS for last response (with voice settings)
351
- def speak_last_response(history, token, accent, gender, age):
352
- if not history or not history[-1][1]:
353
- return None
354
- text = history[-1][1]
355
- return synthesize_speech(text, token, accent, gender, age)
356
-
357
  tts_btn.click(
358
  speak_last_response,
359
  inputs=[chat_state, token_state, accent, gender, age],
@@ -365,5 +401,8 @@ with gr.Blocks(title="Mandarin Job Application Assistant", theme=gr.themes.Soft(
365
  # Clear chat
366
  clear_btn.click(clear_chat, outputs=[chatbot, audio_output])
367
 
 
 
 
368
  if __name__ == "__main__":
369
- demo.launch()
 
57
 
58
  Always respond in the same language as the user's question, except when translation is requested.
59
  When providing Mandarin text, include pinyin pronunciation in parentheses if helpful.
60
+ Be encouraging and patient – the user is learning."""
 
61
 
 
62
  ACCENT_EMOJI = {
63
  "american": "🇺🇸", "british": "🇬🇧", "australian": "🇦🇺", "canadian": "🇨🇦",
64
  "irish": "🇮🇪", "scottish": "🏴󠁧󠁢󠁳󠁣󠁴󠁿", "indian": "🇮🇳", "south-african": "🇿🇦",
 
95
  return f"[STT Error: {str(e)}]"
96
 
97
  def synthesize_speech(text, token=None, accent="chinese", gender="female", age=30):
 
98
  if not text:
99
  return None
100
  client = get_client(token)
 
 
 
101
  age_desc = f"{age} year old" if age else ""
102
  voice_desc = f"{age_desc} {gender} {accent} speaker".strip()
 
 
103
  prompt = f"[{voice_desc}] {text}"
104
  try:
105
  audio_bytes = client.text_to_speech(prompt, model="suno/bark")
 
110
  print(f"TTS error: {e}")
111
  return None
112
 
113
+ def user_message(message, history):
114
+ """Append user message to history and clear input."""
115
  if not message.strip():
116
+ return history, ""
117
+ history.append((message, None))
118
+ return history, ""
119
 
120
+ def bot_response(history, model_name, profile, native_lang, token):
121
+ """Stream assistant response and update history."""
122
+ if not history or history[-1][1] is not None:
123
+ yield history
124
+ return
125
+ user_msg = history[-1][0]
126
  client = get_client(token)
127
  system_prompt = SYSTEM_PROMPT_TEMPLATE.format(**profile)
128
  messages = [{"role": "system", "content": system_prompt}]
129
+ for u, a in history[:-1]:
130
+ if u:
131
+ messages.append({"role": "user", "content": u})
132
+ if a:
133
+ messages.append({"role": "assistant", "content": a})
134
+ messages.append({"role": "user", "content": user_msg})
 
135
  model_id = MODEL_MAP.get(model_name, MODEL_MAP[DEFAULT_MODEL])
136
  try:
137
  stream = client.chat_completion(
 
144
  for chunk in stream:
145
  if chunk.choices and chunk.choices[0].delta.content:
146
  partial += chunk.choices[0].delta.content
147
+ new_history = history.copy()
148
+ new_history[-1] = (user_msg, partial)
149
+ yield new_history
150
  except Exception as e:
151
  error_msg = f"Error: {str(e)}"
152
  if "401" in str(e) or "Authorization" in str(e):
153
  error_msg = "⚠️ Authentication failed. Please provide a valid Hugging Face token."
154
+ new_history = history.copy()
155
+ new_history[-1] = (user_msg, error_msg)
156
+ yield new_history
157
 
158
+ def voice_input(audio, history, token):
159
+ """Transcribe audio and return updated history + transcript."""
160
  if audio is None:
161
  return history, "", None
162
  transcript = transcribe_audio(audio, token)
 
165
  return history, "", None
166
  return history, transcript, audio
167
 
168
+ def quick_action_send(action, history, model, profile, lang, token):
169
+ """Handle quick action button: append message and stream response."""
170
+ if not action.strip():
171
+ yield history
172
+ return
173
+ history.append((action, None))
174
+ yield history # show user message immediately
175
+ # Then stream bot response
176
+ client = get_client(token)
177
+ system_prompt = SYSTEM_PROMPT_TEMPLATE.format(**profile)
178
+ messages = [{"role": "system", "content": system_prompt}]
179
+ for u, a in history[:-1]:
180
+ if u:
181
+ messages.append({"role": "user", "content": u})
182
+ if a:
183
+ messages.append({"role": "assistant", "content": a})
184
+ messages.append({"role": "user", "content": action})
185
+ model_id = MODEL_MAP.get(model, MODEL_MAP[DEFAULT_MODEL])
186
+ try:
187
+ stream = client.chat_completion(
188
+ messages=messages,
189
+ model=model_id,
190
+ max_tokens=1024,
191
+ stream=True,
192
+ )
193
+ partial = ""
194
+ for chunk in stream:
195
+ if chunk.choices and chunk.choices[0].delta.content:
196
+ partial += chunk.choices[0].delta.content
197
+ new_history = history.copy()
198
+ new_history[-1] = (action, partial)
199
+ yield new_history
200
+ except Exception as e:
201
+ error_msg = f"Error: {str(e)}"
202
+ if "401" in str(e) or "Authorization" in str(e):
203
+ error_msg = "⚠️ Authentication failed. Please provide a valid Hugging Face token."
204
+ new_history = history.copy()
205
+ new_history[-1] = (action, error_msg)
206
+ yield new_history
207
 
208
  def update_profile(name, native_lang, target_job, skills, location):
209
  return {
 
227
  def clear_chat():
228
  return [], None
229
 
230
+ def speak_last_response(history, token, accent, gender, age):
231
+ if not history or not history[-1][1]:
232
+ return None
233
+ text = history[-1][1]
234
+ return synthesize_speech(text, token, accent, gender, age)
235
+
236
  # ----------------------------------------------------------------------
237
  # Gradio Interface
238
  # ----------------------------------------------------------------------
239
+ with gr.Blocks(title="Mandarin Job Application Assistant") as demo:
240
  gr.Markdown("""
241
  # 🗣️ Mandarin Job Application Assistant
242
  **For non‑Mandarin speakers applying to jobs that require Mandarin.**
 
291
  interactive=True
292
  )
293
 
294
+ # Voice Configuration
295
  gr.Markdown("### 🎙️ Voice Settings (for spoken responses)")
296
  with gr.Row():
297
  accent = gr.Dropdown(
298
  choices=list(ACCENT_EMOJI.keys()),
299
  value="chinese",
300
+ label="Accent"
 
301
  )
302
  gender = gr.Radio(
303
  choices=["male", "female", "non-binary", "other"],
 
353
 
354
  # Send message (text input)
355
  send_btn.click(
356
+ user_message, inputs=[msg, chat_state], outputs=[chatbot, msg]
357
  ).then(
358
+ bot_response,
359
+ inputs=[chat_state, model, profile_state, native_lang_input, token_state],
360
  outputs=chatbot
361
  )
362
 
 
364
  voice_btn.click(
365
  voice_input, inputs=[audio_input, chat_state, token_state], outputs=[chatbot, msg, audio_input]
366
  ).then(
367
+ user_message, inputs=[msg, chat_state], outputs=[chatbot, msg]
368
+ ).then(
369
+ bot_response,
370
+ inputs=[chat_state, model, profile_state, native_lang_input, token_state],
371
  outputs=chatbot
372
  )
373
 
 
375
  all_quick_btns = [btn_en1, btn_en2, btn_en3, btn_en4, btn_ms1, btn_ms2, btn_ms3, btn_ms4]
376
  for btn in all_quick_btns:
377
  btn.click(
378
+ quick_action_send,
379
+ inputs=[btn, chat_state, model, profile_state, native_lang_input, token_state],
 
 
380
  outputs=chatbot
381
+ ).then(
382
+ lambda: "", None, msg # clear message box
383
  )
384
 
385
  # UI language toggle
 
389
  outputs=[btn_en1, btn_en2, btn_en3, btn_en4, btn_ms1, btn_ms2, btn_ms3, btn_ms4]
390
  )
391
 
392
+ # TTS for last response
 
 
 
 
 
 
393
  tts_btn.click(
394
  speak_last_response,
395
  inputs=[chat_state, token_state, accent, gender, age],
 
401
  # Clear chat
402
  clear_btn.click(clear_chat, outputs=[chatbot, audio_output])
403
 
404
+ # ----------------------------------------------------------------------
405
+ # Launch
406
+ # ----------------------------------------------------------------------
407
  if __name__ == "__main__":
408
+ demo.launch(theme=gr.themes.Soft())