"""Tutor engine: builds system prompts with STRICT language isolation.""" from backend.language_config import LANGUAGES, LEVELS, CURRICULUM, get_vocabulary from backend.teacher_profiles import TEACHERS def build_system_prompt(target_lang, instruction_lang, level, topic_id, teacher_id): """Build the system prompt with strict language isolation rules.""" target = LANGUAGES.get(target_lang, {}) instruction = LANGUAGES.get(instruction_lang, {}) level_cfg = LEVELS.get(level, {}) teacher = TEACHERS.get(teacher_id, {}) target_name = target.get("name", target_lang) instruction_name = instruction.get("name", instruction_lang) target_script = target.get("script", "") target_native = target.get("native_name", "") topics = CURRICULUM.get(level, []) topic_info = next((t for t in topics if t["id"] == topic_id), {}) topic_title = topic_info.get("title", topic_id) vocab = get_vocabulary(target_lang, topic_id) vocab_block = "" if vocab: vocab_lines = [f" - {v['word']} ({v['transliteration']}) = {v['meaning']}" for v in vocab[:10]] vocab_block = "Key vocabulary for this lesson:\n" + "\n".join(vocab_lines) is_immersion = target_lang == instruction_lang # --- STRICT LANGUAGE ISOLATION (MOST CRITICAL RULES) --- if is_immersion: lang_rules = f""" === ABSOLUTE LANGUAGE RULES (HIGHEST PRIORITY — NEVER VIOLATE) === You are in IMMERSION MODE. The student is learning {target_name} using {target_name} itself. 1. EVERY SINGLE WORD you write MUST be in {target_name} ({target_script} script). 2. DO NOT use ANY other language. Not a single word of English or any other language. 3. Explanations, grammar notes, encouragement — ALL in {target_name}. 4. If the student writes in another language, respond ONLY in {target_name} and gently guide them back. === END LANGUAGE RULES === """ else: lang_rules = f""" === ABSOLUTE LANGUAGE RULES (HIGHEST PRIORITY — NEVER VIOLATE) === The student is learning {target_name} and already knows {instruction_name}. 1. ALL your explanations, instructions, grammar notes, encouragement, and conversation MUST be in {instruction_name}. 2. Teach {target_name} words/phrases using {target_script} script. 3. NEVER use English unless the instruction language IS English. 4. NEVER use any language other than {target_name} (for teaching) and {instruction_name} (for explaining). 5. When presenting vocabulary, use this format: [{target_name} word in {target_script}] ([transliteration]) — [{instruction_name} meaning] 6. Even small words like "means", "is called", "for example" MUST be in {instruction_name}. 7. If the student writes in {instruction_name}, respond in {instruction_name} while teaching {target_name} content. 8. If the student attempts {target_name}, praise them in {instruction_name} and provide corrections in {instruction_name}. === END LANGUAGE RULES === """ # Teacher personality teacher_traits = teacher.get("system_prompt_traits", "You are a helpful language teacher.") greeting_style = teacher.get("greeting_style", "friendly") # Honorifics honorifics_note = "" if target.get("has_honorifics"): honorifics_note = f"\n{target_name} has formal/informal address. At {level} level, teach the polite/formal forms primarily." prompt = f"""{lang_rules} {teacher_traits} You are teaching {target_name} ({target_native}) to a {level_cfg.get('name', level)} student. Topic: {topic_title} {honorifics_note} Teaching guidelines for {level} level: - Vocabulary limit: ~{level_cfg.get('vocab_limit', 500)} words - Grammar: {level_cfg.get('grammar_complexity', 'simple')} - Corrections: {level_cfg.get('correction_style', 'gentle')} - Response length: {level_cfg.get('response_length', '3-6 sentences')} {vocab_block} Format rules: - When introducing a new {target_name} word, always write it in {target_script} script first, then transliteration in parentheses. - Use short, clear sentences. - After teaching 2-3 new words, give a quick practice exercise. - When sharing cultural context, wrap it in [CULTURAL NOTE: your note here] format. - For mini-dialogues, use [DIALOGUE] and [/DIALOGUE] markers. - For sentence builder exercises, use [SENTENCE_BUILDER: word1 | word2 | word3 | ...] format with the correct order. Remember: The language rules above are ABSOLUTE. Never break them under any circumstances. """ return prompt.strip() def build_greeting_prompt(target_lang, instruction_lang, level, topic_id, teacher_id): """Build the first greeting message prompt.""" target = LANGUAGES.get(target_lang, {}) instruction = LANGUAGES.get(instruction_lang, {}) teacher = TEACHERS.get(teacher_id, {}) target_name = target.get("name", target_lang) instruction_name = instruction.get("name", instruction_lang) greeting_style = teacher.get("greeting_style", "friendly") topics = CURRICULUM.get(level, []) topic_info = next((t for t in topics if t["id"] == topic_id), {}) topic_title = topic_info.get("title", topic_id) is_immersion = target_lang == instruction_lang if is_immersion: return ( f"Greet the student in {target_name} only. Be {greeting_style}. " f"Introduce yourself as {teacher.get('name', 'Teacher')} and say you'll be teaching " f"'{topic_title}' today. Start with a warm greeting in {target_name} and " f"introduce 1-2 basic words from the topic. Keep it short and encouraging. " f"Remember: EVERYTHING must be in {target_name}." ) else: return ( f"Greet the student in {instruction_name}. Be {greeting_style}. " f"Introduce yourself as {teacher.get('name', 'Teacher')} and say you'll be teaching " f"{target_name} today, specifically '{topic_title}'. " f"Say a common greeting in {target_name} with its {instruction_name} meaning. " f"Keep it short and welcoming. Remember: explain everything in {instruction_name}, " f"teach words in {target_name}." ) def evaluate_pronunciation(user_text, expected_text): """Simple pronunciation scoring by comparing transcribed text to expected.""" if not user_text or not expected_text: return {"score": 0, "feedback": "No text to compare"} user_words = user_text.lower().strip().split() expected_words = expected_text.lower().strip().split() if not expected_words: return {"score": 0, "feedback": "No expected text"} matches = 0 for uw in user_words: if uw in expected_words: matches += 1 score = min(100, int((matches / len(expected_words)) * 100)) if score >= 90: feedback = "Excellent pronunciation!" elif score >= 70: feedback = "Good attempt! A few sounds need practice." elif score >= 50: feedback = "Decent try! Keep practicing the tricky sounds." else: feedback = "Let's practice this phrase more. Listen carefully and try again." return {"score": score, "feedback": feedback, "expected": expected_text, "heard": user_text}