| import gradio as gr |
| import openai |
| import json |
| import os |
| from datetime import datetime |
|
|
| |
| SUPPORTED_LANGUAGES = { |
| "English": "en", |
| "Spanish": "es", |
| "French": "fr", |
| "German": "de", |
| "Chinese": "zh", |
| "Japanese": "ja", |
| "Italian": "it", |
| "Portuguese": "pt", |
| "Russian": "ru", |
| "Arabic": "ar" |
| } |
|
|
| PROFICIENCY_LEVELS = ["Beginner", "Intermediate", "Advanced"] |
|
|
| |
| class LearningSession: |
| def __init__(self): |
| self.conversation_history = [] |
| self.vocabulary = set() |
| self.session_start = datetime.now() |
| self.api_key = None |
| self.language = "Spanish" |
| self.language_code = "es" |
| self.proficiency = "Beginner" |
| self.thinking_mode = False |
| |
| def add_message(self, role, content): |
| self.conversation_history.append({"role": role, "content": content}) |
| if role == "assistant": |
| |
| words = content.lower().replace('.', ' ').replace(',', ' ').replace('!', ' ').replace('?', ' ').split() |
| self.vocabulary.update(words) |
| |
| def get_messages(self): |
| |
| formatted = [] |
| for msg in self.conversation_history: |
| if msg["role"] == "system": |
| continue |
| speaker = "👤 You:" if msg["role"] == "user" else f"🤖 {self.language} Tutor:" |
| formatted.append(f"{speaker} {msg['content']}") |
| return "\n\n".join(formatted) |
| |
| def get_openai_messages(self): |
| |
| system_prompt = self._generate_system_prompt() |
| messages = [{"role": "system", "content": system_prompt}] |
| |
| |
| messages.extend(self.conversation_history[-10:]) |
| return messages |
| |
| def _generate_system_prompt(self): |
| if self.thinking_mode: |
| mode_instruction = ( |
| "Use a thinking/reasoning mode where you carefully analyze the user's language, " |
| "provide corrections, and explain grammar concepts in detail." |
| ) |
| else: |
| mode_instruction = ( |
| "Maintain a natural conversational flow. Only correct critical errors that " |
| "would impede understanding, and do so gently within the conversation." |
| ) |
| |
| language_level_map = { |
| "Beginner": "Use simple vocabulary and short sentences. Frequently introduce basic vocabulary and simple grammar constructions.", |
| "Intermediate": "Use moderate vocabulary and varied sentence structures. Introduce idioms occasionally and more complex grammar patterns.", |
| "Advanced": "Use rich vocabulary, complex sentences, idioms, and cultural references. Challenge the learner with sophisticated language constructs." |
| } |
| |
| level_instruction = language_level_map[self.proficiency] |
| |
| return f"""You are a friendly and patient {self.language} language tutor. |
| |
| IMPORTANT: You must respond ONLY in {self.language} except when explaining grammar concepts in thinking mode. |
| |
| {mode_instruction} |
| |
| {level_instruction} |
| |
| Keep conversations engaging, diverse, and natural. Ask questions about the learner's interests, daily life, or opinions. |
| Occasionally introduce culturally relevant topics about countries where {self.language} is spoken. |
| |
| Remember that you are helping someone learn {self.language}, so maintain an encouraging tone. |
| """ |
|
|
| |
| session = LearningSession() |
|
|
| def set_api_key(api_key): |
| """Validate and set the OpenRouter API key.""" |
| if not api_key.strip(): |
| return "❌ Please enter your OpenRouter API key" |
| |
| |
| session.api_key = api_key |
| |
| |
| if len(api_key) < 20: |
| return "❌ API key looks too short. Please check it and try again." |
| |
| return "✅ API key set! You can now start your language learning session." |
|
|
| def update_settings(language, proficiency, thinking_mode): |
| """Update session settings.""" |
| session.language = language |
| session.language_code = SUPPORTED_LANGUAGES[language] |
| session.proficiency = proficiency |
| session.thinking_mode = thinking_mode |
| |
| return f"Settings updated: Learning {language} at {proficiency} level. Thinking mode: {'On' if thinking_mode else 'Off'}" |
|
|
| def get_ai_response(user_message): |
| """Get response from Qwen3 0.6B via OpenRouter API.""" |
| if not session.api_key: |
| return "Please set your OpenRouter API key first." |
| |
| if not user_message.strip(): |
| return "Please enter a message." |
| |
| try: |
| |
| session.add_message("user", user_message) |
| |
| |
| client = openai.OpenAI( |
| base_url="https://openrouter.ai/api/v1", |
| api_key=session.api_key |
| ) |
| |
| |
| messages = session.get_openai_messages() |
| |
| |
| completion = client.chat.completions.create( |
| extra_headers={ |
| "HTTP-Referer": "Language Learning Companion App", |
| "X-Title": "Language Learning App with Qwen3" |
| }, |
| model="qwen/qwen3-0.6b-04-28:free", |
| messages=messages |
| ) |
| |
| |
| ai_response = completion.choices[0].message.content |
| |
| |
| session.add_message("assistant", ai_response) |
| |
| |
| return session.get_messages() |
| |
| except Exception as e: |
| return f"Error: {str(e)}" |
|
|
| def reset_conversation(): |
| """Reset the conversation history.""" |
| session.conversation_history = [] |
| return "Conversation has been reset." |
|
|
| def get_vocabulary_list(): |
| """Get the current vocabulary list.""" |
| if not session.vocabulary: |
| return "No vocabulary collected yet." |
| |
| vocab_list = sorted(list(session.vocabulary)) |
| return ", ".join(vocab_list) |
|
|
| def get_session_stats(): |
| """Get statistics about the current session.""" |
| duration = datetime.now() - session.session_start |
| hours, remainder = divmod(duration.seconds, 3600) |
| minutes, seconds = divmod(remainder, 60) |
| |
| stats = { |
| "Language": session.language, |
| "Proficiency Level": session.proficiency, |
| "Session Duration": f"{hours}h {minutes}m {seconds}s", |
| "Messages Exchanged": len(session.conversation_history), |
| "Vocabulary Words": len(session.vocabulary) |
| } |
| |
| return json.dumps(stats, indent=2) |
|
|
| |
| with gr.Blocks(title="Language Learning Companion") as app: |
| gr.Markdown("# 🌍 Language Learning Companion") |
| gr.Markdown("Learn languages through natural conversation with AI powered by Qwen3 0.6B") |
| |
| with gr.Accordion("Setup", open=True): |
| api_key_input = gr.Textbox( |
| label="OpenRouter API Key", |
| placeholder="Enter your OpenRouter API key...", |
| type="password" |
| ) |
| api_submit = gr.Button("Set API Key") |
| api_status = gr.Textbox(label="API Status", interactive=False) |
| |
| api_submit.click(set_api_key, inputs=api_key_input, outputs=api_status) |
| |
| with gr.Accordion("Learning Settings", open=True): |
| with gr.Row(): |
| language_dropdown = gr.Dropdown( |
| choices=list(SUPPORTED_LANGUAGES.keys()), |
| value="Spanish", |
| label="Language to Learn" |
| ) |
| proficiency_dropdown = gr.Dropdown( |
| choices=PROFICIENCY_LEVELS, |
| value="Beginner", |
| label="Proficiency Level" |
| ) |
| thinking_checkbox = gr.Checkbox( |
| label="Enable Thinking Mode (Grammar Explanations)", |
| value=False |
| ) |
| |
| settings_submit = gr.Button("Update Settings") |
| settings_status = gr.Textbox(label="Settings Status", interactive=False) |
| |
| settings_submit.click( |
| update_settings, |
| inputs=[language_dropdown, proficiency_dropdown, thinking_checkbox], |
| outputs=settings_status |
| ) |
| |
| with gr.Row(): |
| with gr.Column(scale=2): |
| chat_output = gr.Textbox( |
| label="Conversation", |
| placeholder="Your conversation will appear here...", |
| lines=15, |
| interactive=False |
| ) |
| |
| user_input = gr.Textbox( |
| label="Your message", |
| placeholder=f"Type your message in any language...", |
| lines=2 |
| ) |
| |
| with gr.Row(): |
| submit_btn = gr.Button("Send", variant="primary") |
| reset_btn = gr.Button("Reset Conversation") |
| |
| with gr.Column(scale=1): |
| with gr.Accordion("Vocabulary", open=True): |
| vocab_output = gr.Textbox( |
| label="Words Encountered", |
| lines=10, |
| interactive=False |
| ) |
| vocab_btn = gr.Button("Show Vocabulary") |
| |
| with gr.Accordion("Session Stats", open=True): |
| stats_output = gr.JSON(label="Learning Statistics") |
| stats_btn = gr.Button("Update Stats") |
| |
| |
| submit_btn.click(get_ai_response, inputs=user_input, outputs=chat_output) |
| reset_btn.click(reset_conversation, outputs=chat_output) |
| vocab_btn.click(get_vocabulary_list, outputs=vocab_output) |
| stats_btn.click(get_session_stats, outputs=stats_output) |
| |
| |
| user_input.submit(get_ai_response, inputs=user_input, outputs=chat_output) |
|
|
| |
| if __name__ == "__main__": |
| app.launch() |