|
import gradio as gr |
|
import os |
|
from huggingface_hub import InferenceClient, __version__ as hf_version |
|
import random |
|
from typing import Generator, Dict, List, Tuple, Optional |
|
import logging |
|
|
|
logging.basicConfig( |
|
level=logging.DEBUG, |
|
format='%(asctime)s - %(levelname)s - %(message)s' |
|
) |
|
logging.debug(f"Using huggingface_hub version: {hf_version}") |
|
|
|
|
|
hf_token = os.environ.get("QWEN_BOT_TOKEN") |
|
client = InferenceClient("Qwen/Qwen2.5-72B-Instruct", token=hf_token) |
|
|
|
TOPIC_EXAMPLES = { |
|
"Daily Life": { |
|
"beginner": [ |
|
"What time do you wake up?", |
|
"Do you go to school or work?", |
|
"What do you eat for breakfast?" |
|
], |
|
"intermediate": [ |
|
"What do you usually do after work?", |
|
"Do you like cooking? What’s your favorite dish?", |
|
"Tell me about your morning routine." |
|
], |
|
"advanced": [ |
|
"How do you balance personal and professional responsibilities?", |
|
"What does a productive day look like for you?", |
|
"How has your daily routine changed over the years?" |
|
] |
|
}, |
|
"Travel": { |
|
"beginner": [ |
|
"Do you like to travel?", |
|
"Have you been to another city?", |
|
"Do you like airplanes?" |
|
], |
|
"intermediate": [ |
|
"Have you ever been to another country?", |
|
"What's your dream vacation?", |
|
"What do you like to pack in your suitcase?" |
|
], |
|
"advanced": [ |
|
"How has travel influenced your worldview?", |
|
"What do you think makes a destination culturally significant?", |
|
"Describe your most challenging travel experience." |
|
] |
|
}, |
|
"Food": { |
|
"beginner": [ |
|
"Do you like apples?", |
|
"What is your favorite snack?", |
|
"Do you eat rice or noodles?" |
|
], |
|
"intermediate": [ |
|
"Can you describe how to cook your favorite dish?", |
|
"What is your go-to comfort food and why?", |
|
"Have you ever tried food from another country?" |
|
], |
|
"advanced": [ |
|
"How does food reflect a culture’s values and traditions?", |
|
"Compare the cuisines of two different countries.", |
|
"What’s the most unique food you’ve ever tasted?" |
|
] |
|
}, |
|
"Work & School": { |
|
"beginner": [ |
|
"Do you go to school or work?", |
|
"What is your teacher’s name?", |
|
"Do you have homework?" |
|
], |
|
"intermediate": [ |
|
"What do you do at your job or school?", |
|
"What’s your favorite subject or task?", |
|
"Do you like working with other people?" |
|
], |
|
"advanced": [ |
|
"How do you stay motivated in your work or studies?", |
|
"What are the challenges of remote learning or working?", |
|
"How do education systems differ around the world?" |
|
] |
|
}, |
|
"Hobbies": { |
|
"beginner": [ |
|
"Do you like music?", |
|
"What games do you play?", |
|
"Can you draw or paint?" |
|
], |
|
"intermediate": [ |
|
"What hobbies do you enjoy in your free time?", |
|
"When did you start your favorite hobby?", |
|
"Do you prefer indoor or outdoor hobbies?" |
|
], |
|
"advanced": [ |
|
"How can hobbies contribute to personal growth?", |
|
"What’s a hobby you would like to master and why?", |
|
"How has technology changed the way we pursue hobbies?" |
|
] |
|
}, |
|
"Shopping": { |
|
"beginner": [ |
|
"Do you like shopping?", |
|
"What do you buy at the store?", |
|
"Do you go shopping alone?" |
|
], |
|
"intermediate": [ |
|
"Do you prefer shopping online or in stores?", |
|
"Tell me about your last shopping trip.", |
|
"What kinds of things do you usually buy?" |
|
], |
|
"advanced": [ |
|
"How has consumer behavior changed over time?", |
|
"What are the pros and cons of online shopping?", |
|
"Do advertisements affect your shopping decisions?" |
|
] |
|
}, |
|
"Weather": { |
|
"beginner": [ |
|
"Is it sunny today?", |
|
"Do you like rain?", |
|
"What is your favorite season?" |
|
], |
|
"intermediate": [ |
|
"What’s the weather like where you are?", |
|
"Do you like hot or cold weather?", |
|
"How do you prepare for a rainy day?" |
|
], |
|
"advanced": [ |
|
"How does climate affect daily life in your region?", |
|
"What are the consequences of global climate change?", |
|
"How does weather influence culture and traditions?" |
|
] |
|
} |
|
} |
|
|
|
MAX_HISTORY_LENGTH = 20 |
|
MEMORY_WINDOW = 5 |
|
MAX_TOKENS = 1024 |
|
TEMPERATURE = 0.7 |
|
TOP_P = 0.95 |
|
|
|
def get_examples_for_topic(topic, difficulty): |
|
return TOPIC_EXAMPLES.get(topic, {}).get(difficulty, []) |
|
|
|
def get_conversation_prompt(): |
|
return """You are JoJo, a friendly and supportive AI who helps people practice speaking English through fun, casual conversation. |
|
|
|
Always keep your replies short (2–4 sentences) and speak naturally, like a friendly tutor or partner. Ask engaging, open-ended questions to keep the conversation going. |
|
|
|
If the user makes a grammar or vocabulary mistake, gently correct them by repeating the corrected sentence, followed by a kind note like: \"You can also say it like this!\" |
|
|
|
Do not teach grammar rules. Just offer corrections through example. Never lecture or explain unless asked. |
|
|
|
Keep the tone warm, encouraging, and non-judgmental.""" |
|
|
|
def respond(message: str, chat_history: List[Tuple[str, str]], topic: Optional[str] = None, use_full_memory: bool = True) -> Tuple[str, List[Tuple[str, str]]]: |
|
if not message.strip(): |
|
return "", chat_history |
|
|
|
try: |
|
api_messages = [{"role": "system", "content": get_conversation_prompt()}] |
|
logging.debug(f"System Message: {api_messages[0]}") |
|
|
|
if chat_history and use_full_memory: |
|
for user_msg, bot_msg in chat_history[-MEMORY_WINDOW:]: |
|
api_messages.extend([ |
|
{"role": "user", "content": str(user_msg)}, |
|
{"role": "assistant", "content": str(bot_msg)} |
|
]) |
|
|
|
api_messages.append({"role": "user", "content": str(message)}) |
|
response = client.chat_completion( |
|
messages=api_messages, |
|
max_tokens=MAX_TOKENS, |
|
temperature=TEMPERATURE, |
|
top_p=TOP_P |
|
) |
|
|
|
bot_message = response.choices[0].message.content |
|
updated_history = list(chat_history) |
|
updated_history.append((message, bot_message)) |
|
return "", updated_history |
|
|
|
except Exception as e: |
|
logging.error("Error in respond function", exc_info=True) |
|
error_msg = f"There was a temporary issue. Please try again. (Error: {str(e)})" |
|
return "", list(chat_history) + [(message, error_msg)] |
|
|
|
def get_avatar_url(): |
|
return "https://api.dicebear.com/7.x/bottts/svg?seed=rabbit&backgroundColor=b6e3f4" |
|
|
|
custom_css = """ |
|
.compact-btn { |
|
padding: 0.75rem !important; |
|
font-size: 1rem !important; |
|
font-weight: 500; |
|
border-radius: 8px; |
|
background-color: #2f2f2f; |
|
color: white; |
|
transition: background-color 0.3s; |
|
} |
|
.compact-btn:hover { |
|
background-color: #444; |
|
} |
|
#voice-controls { |
|
margin-top: 1em; |
|
text-align: center; |
|
opacity: 0.5; |
|
font-size: 0.9rem; |
|
font-style: italic; |
|
} |
|
""" |
|
|
|
with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo: |
|
gr.Markdown(""" |
|
# 🐰 JoJo - Your Speaking Buddy |
|
**Chat in English with JoJo — your kind and cheerful language partner.** |
|
Pick a topic, choose your level, and practice naturally. JoJo will guide you, ask questions, and gently correct you along the way! |
|
""") |
|
|
|
avatar = get_avatar_url() |
|
memory_flag = gr.State(value=True) |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=3): |
|
chatbot = gr.Chatbot( |
|
height=400, |
|
bubble_full_width=True, |
|
show_copy_button=True, |
|
avatar_images=[None, avatar], |
|
scale=1, |
|
min_width=800 |
|
) |
|
msg = gr.Textbox( |
|
placeholder="Say something to JoJo...", |
|
scale=4 |
|
) |
|
|
|
with gr.Row(): |
|
submit = gr.Button("Send", variant="primary") |
|
clear = gr.Button("New Chat") |
|
|
|
gr.Markdown(""" |
|
<div id="voice-controls"> |
|
🎤 Voice input and 🔈 playback coming soon! |
|
</div> |
|
""") |
|
|
|
with gr.Column(scale=1): |
|
gr.Markdown("""### 🎯 Conversation Settings""") |
|
topic = gr.Dropdown( |
|
choices=list(TOPIC_EXAMPLES.keys()), |
|
label="Select Topic", |
|
value="Daily Life" |
|
) |
|
|
|
difficulty = gr.Dropdown( |
|
choices=["beginner", "intermediate", "advanced"], |
|
label="Select Difficulty", |
|
value="intermediate" |
|
) |
|
|
|
gr.Markdown("""### 💬 Quick Starters""") |
|
starter_btn1 = gr.Button("Starter 1", scale=1, min_width=250, elem_classes="compact-btn") |
|
starter_btn2 = gr.Button("Starter 2", scale=1, min_width=250, elem_classes="compact-btn") |
|
starter_btn3 = gr.Button("Starter 3", scale=1, min_width=250, elem_classes="compact-btn") |
|
starter_buttons = [starter_btn1, starter_btn2, starter_btn3] |
|
|
|
def update_starters(selected_topic, selected_difficulty): |
|
examples = get_examples_for_topic(selected_topic, selected_difficulty) |
|
results = [examples[i] if i < len(examples) else "" for i in range(3)] |
|
return tuple(results) |
|
|
|
def use_starter(text: str, history: List[Tuple[str, str]], selected_topic: str, memory_flag: bool) -> Tuple[str, List[Tuple[str, str]]]: |
|
if not text: |
|
return "", history |
|
try: |
|
_, updated = respond(text, history, selected_topic, memory_flag) |
|
return "", updated |
|
except Exception as e: |
|
return "", history + [(text, f"Error: {str(e)}")] |
|
|
|
for btn in starter_buttons: |
|
btn.click(fn=use_starter, inputs=[btn, chatbot, topic, memory_flag], outputs=[msg, chatbot], queue=True) |
|
|
|
topic.change(fn=update_starters, inputs=[topic, difficulty], outputs=starter_buttons) |
|
difficulty.change(fn=update_starters, inputs=[topic, difficulty], outputs=starter_buttons) |
|
|
|
msg.submit(fn=respond, inputs=[msg, chatbot, topic, memory_flag], outputs=[msg, chatbot]) |
|
submit.click(fn=respond, inputs=[msg, chatbot, topic, memory_flag], outputs=[msg, chatbot]) |
|
clear.click(lambda: [], None, chatbot, queue=False) |
|
clear.click(lambda: "", None, msg, queue=False) |
|
|
|
default_starters = get_examples_for_topic("Daily Life", "intermediate") |
|
demo.load(fn=lambda: tuple(default_starters[:3]), outputs=starter_buttons, queue=False) |
|
|
|
if __name__ == "__main__": |
|
demo.launch(server_name="0.0.0.0", server_port=7860) |
|
|