hf-hrpbloom / app.py
HRPBloom's picture
Update app.py
019eaa1 verified
import gradio as gr
from huggingface_hub import InferenceClient
import os
import tempfile
# ----------------------------------------------------------------------
# Configuration
# ----------------------------------------------------------------------
HF_TOKEN = os.getenv("HF_TOKEN")
if HF_TOKEN is None:
print("โš ๏ธ HF_TOKEN not set. Some models may not be accessible. Please add your token as a secret.")
MODEL_MAP = {
"GPT-4o (Multilingual)": "mistralai/Mistral-7B-Instruct-v0.2",
"Claude (Translation)": "mistralai/Mixtral-8x7B-Instruct-v0.1",
"Gemini (Job Coach)": "google/gemma-2-9b-it",
}
DEFAULT_MODEL = "GPT-4o (Multilingual)"
JOB_SEEKER_PROFILE = {
"name": "John Tan",
"native_language": "English",
"target_job": "Software Engineer (Mandarinโ€‘required)",
"current_skills": "Python, JavaScript, 3 years experience",
"target_location": "Kuala Lumpur / Singapore",
}
QUICK_ACTIONS = {
"en": [
"Translate 'I have 5 years of experience in marketing' to Mandarin",
"Common interview questions for a software engineer (with Mandarin answers)",
"How to write a cover letter in Mandarin?",
"What are the key phrases for a Mandarin resume?",
],
"ms": [
"Terjemahkan 'Saya ada 5 tahun pengalaman dalam pemasaran' ke Mandarin",
"Soalan temuduga biasa untuk jurutera perisian (dengan jawapan Mandarin)",
"Bagaimana menulis surat lamaran dalam Mandarin?",
"Apakah frasa utama untuk resume Mandarin?",
]
}
SYSTEM_PROMPT_TEMPLATE = """You are a friendly assistant helping a nonโ€‘Mandarin speaker apply for jobs that require Mandarin.
User profile:
- Name: {name}
- Native language: {native_language}
- Target job: {target_job}
- Current skills: {current_skills}
- Target location: {target_location}
Your role:
1. Translate between the user's native language and Mandarin (simplified Chinese).
2. Help draft resumes, cover letters, and emails in Mandarin.
3. Provide common interview questions and sample answers in Mandarin.
4. Explain cultural nuances and workplace etiquette in Mandarinโ€‘speaking companies.
5. Practice conversations: you can speak in Mandarin and ask the user to respond.
Always respond in the same language as the user's question, except when translation is requested.
When providing Mandarin text, include pinyin pronunciation in parentheses if helpful.
Be encouraging and patient โ€“ the user is learning."""
ACCENT_EMOJI = {
"american": "๐Ÿ‡บ๐Ÿ‡ธ", "british": "๐Ÿ‡ฌ๐Ÿ‡ง", "australian": "๐Ÿ‡ฆ๐Ÿ‡บ", "canadian": "๐Ÿ‡จ๐Ÿ‡ฆ",
"irish": "๐Ÿ‡ฎ๐Ÿ‡ช", "scottish": "๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ", "indian": "๐Ÿ‡ฎ๐Ÿ‡ณ", "south-african": "๐Ÿ‡ฟ๐Ÿ‡ฆ",
"new-zealand": "๐Ÿ‡ณ๐Ÿ‡ฟ", "spanish": "๐Ÿ‡ช๐Ÿ‡ธ", "french": "๐Ÿ‡ซ๐Ÿ‡ท", "german": "๐Ÿ‡ฉ๐Ÿ‡ช",
"italian": "๐Ÿ‡ฎ๐Ÿ‡น", "portuguese": "๐Ÿ‡ต๐Ÿ‡น", "brazilian": "๐Ÿ‡ง๐Ÿ‡ท", "mexican": "๐Ÿ‡ฒ๐Ÿ‡ฝ",
"argentinian": "๐Ÿ‡ฆ๐Ÿ‡ท", "japanese": "๐Ÿ‡ฏ๐Ÿ‡ต", "chinese": "๐Ÿ‡จ๐Ÿ‡ณ", "korean": "๐Ÿ‡ฐ๐Ÿ‡ท",
"russian": "๐Ÿ‡ท๐Ÿ‡บ", "arabic": "๐Ÿ‡ธ๐Ÿ‡ฆ", "dutch": "๐Ÿ‡ณ๐Ÿ‡ฑ", "swedish": "๐Ÿ‡ธ๐Ÿ‡ช",
"norwegian": "๐Ÿ‡ณ๐Ÿ‡ด", "danish": "๐Ÿ‡ฉ๐Ÿ‡ฐ", "finnish": "๐Ÿ‡ซ๐Ÿ‡ฎ", "polish": "๐Ÿ‡ต๐Ÿ‡ฑ",
"turkish": "๐Ÿ‡น๐Ÿ‡ท", "greek": "๐Ÿ‡ฌ๐Ÿ‡ท",
}
GENDER_ICON = {
"male": "โ™‚๏ธ", "female": "โ™€๏ธ", "non-binary": "โšง๏ธ", "other": "๐Ÿ”น"
}
# ----------------------------------------------------------------------
# Helper functions
# ----------------------------------------------------------------------
def get_client(token=None):
if token:
return InferenceClient(token=token)
if HF_TOKEN:
return InferenceClient(token=HF_TOKEN)
return InferenceClient()
def transcribe_audio(audio_path, token=None):
if audio_path is None:
return ""
client = get_client(token)
try:
result = client.automatic_speech_recognition(audio_path, model="openai/whisper-large-v3")
return result["text"]
except Exception as e:
return f"[STT Error: {str(e)}]"
def synthesize_speech(text, token=None, accent="chinese", gender="female", age=30):
if not text:
return None
client = get_client(token)
age_desc = f"{age} year old" if age else ""
voice_desc = f"{age_desc} {gender} {accent} speaker".strip()
prompt = f"[{voice_desc}] {text}"
try:
audio_bytes = client.text_to_speech(prompt, model="suno/bark")
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
f.write(audio_bytes)
return f.name
except Exception as e:
print(f"TTS error: {e}")
return None
def user_message(message, history):
"""Append user message to history and clear input."""
if not message.strip():
return history, ""
history.append((message, None))
return history, ""
def bot_response(history, model_name, profile, native_lang, token):
"""Stream assistant response and update history."""
if not history or history[-1][1] is not None:
yield history
return
user_msg = history[-1][0]
client = get_client(token)
system_prompt = SYSTEM_PROMPT_TEMPLATE.format(**profile)
messages = [{"role": "system", "content": system_prompt}]
for u, a in history[:-1]:
if u:
messages.append({"role": "user", "content": u})
if a:
messages.append({"role": "assistant", "content": a})
messages.append({"role": "user", "content": user_msg})
model_id = MODEL_MAP.get(model_name, MODEL_MAP[DEFAULT_MODEL])
try:
stream = client.chat_completion(
messages=messages,
model=model_id,
max_tokens=1024,
stream=True,
)
partial = ""
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
partial += chunk.choices[0].delta.content
new_history = history.copy()
new_history[-1] = (user_msg, partial)
yield new_history
except Exception as e:
error_msg = f"Error: {str(e)}"
if "401" in str(e) or "Authorization" in str(e):
error_msg = "โš ๏ธ Authentication failed. Please provide a valid Hugging Face token."
new_history = history.copy()
new_history[-1] = (user_msg, error_msg)
yield new_history
def voice_input(audio, history, token):
"""Transcribe audio and return updated history + transcript."""
if audio is None:
return history, "", None
transcript = transcribe_audio(audio, token)
if transcript.startswith("[STT Error"):
history.append((transcript, None))
return history, "", None
return history, transcript, audio
def quick_action_send(action, history, model, profile, lang, token):
"""Handle quick action button: append message and stream response."""
if not action.strip():
yield history
return
history.append((action, None))
yield history # show user message immediately
# Then stream bot response
client = get_client(token)
system_prompt = SYSTEM_PROMPT_TEMPLATE.format(**profile)
messages = [{"role": "system", "content": system_prompt}]
for u, a in history[:-1]:
if u:
messages.append({"role": "user", "content": u})
if a:
messages.append({"role": "assistant", "content": a})
messages.append({"role": "user", "content": action})
model_id = MODEL_MAP.get(model, MODEL_MAP[DEFAULT_MODEL])
try:
stream = client.chat_completion(
messages=messages,
model=model_id,
max_tokens=1024,
stream=True,
)
partial = ""
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
partial += chunk.choices[0].delta.content
new_history = history.copy()
new_history[-1] = (action, partial)
yield new_history
except Exception as e:
error_msg = f"Error: {str(e)}"
if "401" in str(e) or "Authorization" in str(e):
error_msg = "โš ๏ธ Authentication failed. Please provide a valid Hugging Face token."
new_history = history.copy()
new_history[-1] = (action, error_msg)
yield new_history
def update_profile(name, native_lang, target_job, skills, location):
return {
"name": name,
"native_language": native_lang,
"target_job": target_job,
"current_skills": skills,
"target_location": location,
}
def toggle_ui_language(lang):
en_visible = lang == "en"
ms_visible = lang == "ms"
return (
gr.update(visible=en_visible), gr.update(visible=en_visible),
gr.update(visible=en_visible), gr.update(visible=en_visible),
gr.update(visible=ms_visible), gr.update(visible=ms_visible),
gr.update(visible=ms_visible), gr.update(visible=ms_visible)
)
def clear_chat():
return [], None
def speak_last_response(history, token, accent, gender, age):
if not history or not history[-1][1]:
return None
text = history[-1][1]
return synthesize_speech(text, token, accent, gender, age)
# ----------------------------------------------------------------------
# Gradio Interface
# ----------------------------------------------------------------------
with gr.Blocks(title="Mandarin Job Application Assistant") as demo:
gr.Markdown("""
# ๐Ÿ—ฃ๏ธ Mandarin Job Application Assistant
**For nonโ€‘Mandarin speakers applying to jobs that require Mandarin.**
Use voice or text to get translations, interview practice, and resume help.
""")
# States
chat_state = gr.State([])
profile_state = gr.State(JOB_SEEKER_PROFILE)
token_state = gr.State(HF_TOKEN)
with gr.Row():
with gr.Column(scale=1):
# Token input (optional)
with gr.Group():
gr.Markdown("### ๐Ÿ”‘ Hugging Face Token (optional)")
token_input = gr.Textbox(
label="HF Token",
type="password",
placeholder="Enter your token if you have one",
value=HF_TOKEN if HF_TOKEN else ""
)
token_btn = gr.Button("Set Token")
# Profile
with gr.Group():
gr.Markdown("### ๐Ÿ‘ค Your Profile")
name_input = gr.Textbox(label="Name", value=JOB_SEEKER_PROFILE["name"])
native_lang_input = gr.Dropdown(
choices=["English", "Malay", "Tamil", "Other"],
value=JOB_SEEKER_PROFILE["native_language"],
label="Native Language"
)
target_job_input = gr.Textbox(label="Target Job (Mandarinโ€‘required)", value=JOB_SEEKER_PROFILE["target_job"])
skills_input = gr.Textbox(label="Your Skills / Experience", value=JOB_SEEKER_PROFILE["current_skills"], lines=2)
location_input = gr.Textbox(label="Target Location", value=JOB_SEEKER_PROFILE["target_location"])
update_profile_btn = gr.Button("Update Profile")
# UI language
ui_language = gr.Radio(
choices=[("English", "en"), ("Bahasa Malaysia", "ms")],
value="en",
label="Interface Language",
interactive=True
)
# Model
model = gr.Dropdown(
choices=list(MODEL_MAP.keys()),
value=DEFAULT_MODEL,
label="AI Model",
interactive=True
)
# Voice Configuration
gr.Markdown("### ๐ŸŽ™๏ธ Voice Settings (for spoken responses)")
with gr.Row():
accent = gr.Dropdown(
choices=list(ACCENT_EMOJI.keys()),
value="chinese",
label="Accent"
)
gender = gr.Radio(
choices=["male", "female", "non-binary", "other"],
value="female",
label="Gender"
)
age = gr.Slider(minimum=18, maximum=80, value=30, step=1, label="Age")
# Quick actions
gr.Markdown("### โšก Quick Actions")
with gr.Row():
btn_en1 = gr.Button(QUICK_ACTIONS["en"][0], visible=True)
btn_en2 = gr.Button(QUICK_ACTIONS["en"][1], visible=True)
with gr.Row():
btn_en3 = gr.Button(QUICK_ACTIONS["en"][2], visible=True)
btn_en4 = gr.Button(QUICK_ACTIONS["en"][3], visible=True)
btn_ms1 = gr.Button(QUICK_ACTIONS["ms"][0], visible=False)
btn_ms2 = gr.Button(QUICK_ACTIONS["ms"][1], visible=False)
btn_ms3 = gr.Button(QUICK_ACTIONS["ms"][2], visible=False)
btn_ms4 = gr.Button(QUICK_ACTIONS["ms"][3], visible=False)
# Voice input
gr.Markdown("### ๐ŸŽค Voice Input (Speak in your native language)")
audio_input = gr.Audio(sources=["microphone"], type="filepath", label="Record your question")
voice_btn = gr.Button("Transcribe and Send")
with gr.Column(scale=2):
chatbot = gr.Chatbot(label="Conversation", height=500)
with gr.Row():
msg = gr.Textbox(label="Your message", placeholder="Type your question here...", scale=4)
send_btn = gr.Button("Send", variant="primary", scale=1)
with gr.Row():
clear_btn = gr.Button("Clear Chat")
tts_btn = gr.Button("๐Ÿ”Š Speak Last Response (with selected voice)")
audio_output = gr.Audio(label="Spoken Response", type="filepath", autoplay=True, visible=False)
# ------------------------------------------------------------------
# Event handlers
# ------------------------------------------------------------------
# Set token
def set_token(token_val):
return token_val
token_btn.click(set_token, inputs=[token_input], outputs=[token_state])
# Update profile
update_profile_btn.click(
update_profile,
inputs=[name_input, native_lang_input, target_job_input, skills_input, location_input],
outputs=[profile_state]
).then(
lambda: gr.Info("Profile updated!"), None, None
)
# Send message (text input)
send_btn.click(
user_message, inputs=[msg, chat_state], outputs=[chatbot, msg]
).then(
bot_response,
inputs=[chat_state, model, profile_state, native_lang_input, token_state],
outputs=chatbot
)
# Voice input
voice_btn.click(
voice_input, inputs=[audio_input, chat_state, token_state], outputs=[chatbot, msg, audio_input]
).then(
user_message, inputs=[msg, chat_state], outputs=[chatbot, msg]
).then(
bot_response,
inputs=[chat_state, model, profile_state, native_lang_input, token_state],
outputs=chatbot
)
# Quick actions
all_quick_btns = [btn_en1, btn_en2, btn_en3, btn_en4, btn_ms1, btn_ms2, btn_ms3, btn_ms4]
for btn in all_quick_btns:
btn.click(
quick_action_send,
inputs=[btn, chat_state, model, profile_state, native_lang_input, token_state],
outputs=chatbot
).then(
lambda: "", None, msg # clear message box
)
# UI language toggle
ui_language.change(
toggle_ui_language,
inputs=ui_language,
outputs=[btn_en1, btn_en2, btn_en3, btn_en4, btn_ms1, btn_ms2, btn_ms3, btn_ms4]
)
# TTS for last response
tts_btn.click(
speak_last_response,
inputs=[chat_state, token_state, accent, gender, age],
outputs=[audio_output]
).then(
lambda: gr.update(visible=True), None, audio_output
)
# Clear chat
clear_btn.click(clear_chat, outputs=[chatbot, audio_output])
# ----------------------------------------------------------------------
# Launch
# ----------------------------------------------------------------------
if __name__ == "__main__":
demo.launch(theme=gr.themes.Soft())