From 9af9bc2ad6bcef46f84323fce3877bc616e7e344 Mon Sep 17 00:00:00 2001 From: Mircea Lungu Date: Thu, 2 Apr 2026 18:13:11 +0200 Subject: [PATCH 1/6] Support suggestion_type parameter for audio lesson generation Accept "topic" or "situation" as suggestion_type alongside topic_suggestion. Use different LLM prompt phrasing: topics set a thematic context while situations structure the lesson as a roleplay. Word/char limits relaxed to 6 words / 48 chars. Co-Authored-By: Claude Opus 4.6 (1M context) --- zeeguu/api/endpoints/audio_lessons.py | 11 ++++++++--- zeeguu/core/audio_lessons/daily_lesson_generator.py | 10 +++++++++- zeeguu/core/audio_lessons/script_generator.py | 11 +++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/zeeguu/api/endpoints/audio_lessons.py b/zeeguu/api/endpoints/audio_lessons.py index 4fc5f3a6..c17a2697 100644 --- a/zeeguu/api/endpoints/audio_lessons.py +++ b/zeeguu/api/endpoints/audio_lessons.py @@ -4,6 +4,7 @@ from zeeguu.api.utils.json_result import json_result from zeeguu.api.utils.route_wrappers import cross_domain, requires_session from zeeguu.core.audio_lessons.daily_lesson_generator import DailyLessonGenerator +from zeeguu.core.audio_lessons.script_generator import VALID_SUGGESTION_TYPES from zeeguu.core.model import db, User, UserWord, AudioLessonGenerationProgress from zeeguu.logging import log from . import api @@ -58,6 +59,7 @@ def _generate_lesson_in_background(user_id, preparation): cefr_level=preparation["cefr_level"], progress=progress, topic_suggestion=preparation.get("topic_suggestion"), + suggestion_type=preparation.get("suggestion_type"), ) except Exception as e: log(f"[background_generate] Error for user {user_id}: {e}") @@ -89,13 +91,16 @@ def generate_daily_lesson(): # Get timezone offset from form data (default to 0 for UTC) timezone_offset = flask.request.form.get("timezone_offset", 0, type=int) topic_suggestion = flask.request.form.get("topic_suggestion", "").strip() + suggestion_type = flask.request.form.get("suggestion_type", "").strip() or None + if suggestion_type not in (None, *VALID_SUGGESTION_TYPES): + suggestion_type = None if topic_suggestion: - # Cap at 4 words and 24 characters to limit prompt injection surface - topic_suggestion = " ".join(topic_suggestion.split()[:4])[:24].strip() or None + # Cap at 6 words and 48 characters to limit prompt injection surface + topic_suggestion = " ".join(topic_suggestion.split()[:6])[:48].strip() or None else: topic_suggestion = None - result = generator.prepare_lesson_generation(user, timezone_offset, topic_suggestion) + result = generator.prepare_lesson_generation(user, timezone_offset, topic_suggestion, suggestion_type) # Existing lesson found — return it directly if result.get("lesson_id"): diff --git a/zeeguu/core/audio_lessons/daily_lesson_generator.py b/zeeguu/core/audio_lessons/daily_lesson_generator.py index ff9dac9a..f60dcbeb 100644 --- a/zeeguu/core/audio_lessons/daily_lesson_generator.py +++ b/zeeguu/core/audio_lessons/daily_lesson_generator.py @@ -42,7 +42,7 @@ def lesson_builder(self): self._lesson_builder = LessonBuilder() return self._lesson_builder - def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=None): + def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=None, suggestion_type=None): """ Validate and prepare for lesson generation (synchronous, fast). Returns either an error/existing-lesson dict, or a preparation dict @@ -52,6 +52,7 @@ def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=No user: The User object to generate a lesson for timezone_offset: Client's timezone offset in minutes from UTC topic_suggestion: Optional short topic hint for the LLM (max 100 chars) + suggestion_type: Optional type of suggestion ("topic" or "situation") Returns: Dictionary with either error info, existing lesson, or preparation data @@ -141,6 +142,7 @@ def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=No "cefr_level": cefr_level, "progress_id": progress.id, "topic_suggestion": topic_suggestion, + "suggestion_type": suggestion_type, } def select_words_for_lesson( @@ -170,6 +172,7 @@ def generate_audio_lesson_meaning( created_by="claude-v1", progress=None, topic_suggestion=None, + suggestion_type=None, ): """ Generate an AudioLessonMeaning for a specific user word. @@ -182,6 +185,7 @@ def generate_audio_lesson_meaning( created_by: String identifying who created this lesson progress: Optional AudioLessonGenerationProgress for tracking topic_suggestion: Optional short topic hint for the LLM + suggestion_type: Optional type ("topic" or "situation") Returns: AudioLessonMeaning object @@ -212,6 +216,7 @@ def generate_audio_lesson_meaning( translation_language=translation_language, cefr_level=cefr_level, topic_suggestion=topic_suggestion, + suggestion_type=suggestion_type, ) # Update progress: script done @@ -268,6 +273,7 @@ def generate_daily_lesson( cefr_level: str, progress: AudioLessonGenerationProgress = None, topic_suggestion: str = None, + suggestion_type: str = None, ) -> dict: """ Generate a daily audio lesson for the given user with specific words. @@ -280,6 +286,7 @@ def generate_daily_lesson( translation_language: Language code for translations (e.g. 'en') cefr_level: CEFR level for the lesson (e.g. 'A1', 'B2') topic_suggestion: Optional short topic hint for the LLM + suggestion_type: Optional type ("topic" or "situation") Returns: Dictionary with lesson details or error information @@ -330,6 +337,7 @@ def generate_daily_lesson( user_word, origin_language, translation_language, cefr_level, progress=progress, topic_suggestion=topic_suggestion, + suggestion_type=suggestion_type, ) except Exception as e: log( diff --git a/zeeguu/core/audio_lessons/script_generator.py b/zeeguu/core/audio_lessons/script_generator.py index 3bef1b79..e8253300 100644 --- a/zeeguu/core/audio_lessons/script_generator.py +++ b/zeeguu/core/audio_lessons/script_generator.py @@ -6,6 +6,8 @@ from zeeguu.core.llm_services import generate_audio_lesson_script from zeeguu.logging import log +VALID_SUGGESTION_TYPES = ("topic", "situation") + # Load the prompt template def get_prompt_template(file_name) -> str: @@ -25,6 +27,7 @@ def generate_lesson_script( cefr_level: str = "A1", generator_prompt_file="meaning_lesson--teacher_challenges_both_dialogue_and_beyond-v2.txt", topic_suggestion: str = None, + suggestion_type: str = None, ) -> str: """ Generate a lesson script using Claude API. @@ -37,6 +40,7 @@ def generate_lesson_script( cefr_level: Cefr level of the word being learned generator_prompt_file: full filename topic_suggestion: Optional short topic hint for the LLM + suggestion_type: Optional type ("topic" or "situation") Returns: Generated script text @@ -78,9 +82,12 @@ def generate_lesson_script( ) if topic_suggestion: - prompt += f'\nCONTEXT: Set the dialogue scenario in a context related to "{topic_suggestion}". The examples and challenges should use vocabulary relevant to this topic.\n' + if suggestion_type == "situation": + prompt += f'\nSITUATION: Structure the lesson as a roleplay scenario: "{topic_suggestion}". The dialogue should simulate a real conversation the learner might have in this situation.\n' + else: + prompt += f'\nTOPIC: Set the dialogue scenario in a context related to "{topic_suggestion}". The examples and challenges should use vocabulary relevant to this topic.\n' - log(f"Generating script for {origin_word} -> {translation_word} (topic: {topic_suggestion})") + log(f"Generating script for {origin_word} -> {translation_word} (topic: {topic_suggestion}, type: {suggestion_type})") try: # Use unified LLM service with automatic Anthropic -> DeepSeek fallback From b31c4ea33df68e59608cb59aa974b767b7691259 Mon Sep 17 00:00:00 2001 From: Mircea Lungu Date: Fri, 3 Apr 2026 13:28:16 +0200 Subject: [PATCH 2/6] Store suggestion_type in DB for analytics Add suggestion_type column to daily_audio_lesson and audio_lesson_meaning tables. Includes migration script. Co-Authored-By: Claude Opus 4.6 (1M context) --- tools/migrations/26-04-03--add_suggestion_type.sql | 7 +++++++ zeeguu/core/audio_lessons/daily_lesson_generator.py | 4 +++- zeeguu/core/model/audio_lesson_meaning.py | 9 ++++++--- zeeguu/core/model/daily_audio_lesson.py | 4 +++- 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 tools/migrations/26-04-03--add_suggestion_type.sql diff --git a/tools/migrations/26-04-03--add_suggestion_type.sql b/tools/migrations/26-04-03--add_suggestion_type.sql new file mode 100644 index 00000000..8ac39e81 --- /dev/null +++ b/tools/migrations/26-04-03--add_suggestion_type.sql @@ -0,0 +1,7 @@ +ALTER TABLE daily_audio_lesson +ADD COLUMN suggestion_type VARCHAR(20) DEFAULT NULL +AFTER topic_suggestion; + +ALTER TABLE audio_lesson_meaning +ADD COLUMN suggestion_type VARCHAR(20) DEFAULT NULL +AFTER topic_suggestion; diff --git a/zeeguu/core/audio_lessons/daily_lesson_generator.py b/zeeguu/core/audio_lessons/daily_lesson_generator.py index f60dcbeb..1be330c4 100644 --- a/zeeguu/core/audio_lessons/daily_lesson_generator.py +++ b/zeeguu/core/audio_lessons/daily_lesson_generator.py @@ -198,7 +198,7 @@ def generate_audio_lesson_meaning( # Check if audio lesson already exists for this meaning, teacher language, and topic existing_lesson = AudioLessonMeaning.find( - meaning=meaning, teacher_language=teacher_lang, topic_suggestion=topic_suggestion + meaning=meaning, teacher_language=teacher_lang, topic_suggestion=topic_suggestion, suggestion_type=suggestion_type ) if existing_lesson: return existing_lesson @@ -232,6 +232,7 @@ def generate_audio_lesson_meaning( difficulty_level=cefr_level, teacher_language=teacher_lang, topic_suggestion=topic_suggestion, + suggestion_type=suggestion_type, ) db.session.add(audio_lesson_meaning) db.session.flush() # Get the ID @@ -304,6 +305,7 @@ def generate_daily_lesson( created_by="generate_daily_lesson_v1", language=user.learned_language, topic_suggestion=topic_suggestion, + suggestion_type=suggestion_type, ) db.session.add(daily_lesson) log(f"[generate_daily_lesson] Created daily lesson object") diff --git a/zeeguu/core/model/audio_lesson_meaning.py b/zeeguu/core/model/audio_lesson_meaning.py index f2a630cc..427fe2f7 100644 --- a/zeeguu/core/model/audio_lesson_meaning.py +++ b/zeeguu/core/model/audio_lesson_meaning.py @@ -25,6 +25,7 @@ class AudioLessonMeaning(db.Model): script = Column(Text, nullable=False) voice_config = Column(JSON) topic_suggestion = Column(String(100), nullable=True) + suggestion_type = Column(String(20), nullable=True) teacher_language_id = Column(Integer, ForeignKey(Language.id), nullable=True) teacher_language = relationship(Language) @@ -47,6 +48,7 @@ def __init__( duration_seconds=None, teacher_language=None, topic_suggestion=None, + suggestion_type=None, ): self.meaning_id = meaning.id self.script = script @@ -56,6 +58,7 @@ def __init__( self.voice_config = voice_config self.duration_seconds = duration_seconds self.topic_suggestion = topic_suggestion + self.suggestion_type = suggestion_type if teacher_language: self.teacher_language_id = teacher_language.id @@ -69,10 +72,10 @@ def audio_file_path(self): return f"/audio/lessons/{self.meaning_id}-{lang_code}.mp3" @classmethod - def find(cls, meaning, teacher_language=None, topic_suggestion=None): - """Find audio lesson for a specific meaning, teacher language, and topic""" + def find(cls, meaning, teacher_language=None, topic_suggestion=None, suggestion_type=None): + """Find audio lesson for a specific meaning, teacher language, topic, and type""" query = cls.query.filter_by(meaning=meaning) if teacher_language: query = query.filter_by(teacher_language_id=teacher_language.id) - query = query.filter_by(topic_suggestion=topic_suggestion) + query = query.filter_by(topic_suggestion=topic_suggestion, suggestion_type=suggestion_type) return query.first() diff --git a/zeeguu/core/model/daily_audio_lesson.py b/zeeguu/core/model/daily_audio_lesson.py index 83706fe8..34d97404 100644 --- a/zeeguu/core/model/daily_audio_lesson.py +++ b/zeeguu/core/model/daily_audio_lesson.py @@ -42,6 +42,7 @@ class DailyAudioLesson(db.Model): # Optional topic suggestion that themed the lesson topic_suggestion = Column(db.String(100), nullable=True) + suggestion_type = Column(db.String(20), nullable=True) # Relationship to segments (individual meaning lessons) segments = relationship( @@ -51,13 +52,14 @@ class DailyAudioLesson(db.Model): cascade="all, delete-orphan", ) - def __init__(self, user, created_by, voice_config=None, duration_seconds=None, language=None, topic_suggestion=None): + def __init__(self, user, created_by, voice_config=None, duration_seconds=None, language=None, topic_suggestion=None, suggestion_type=None): self.user = user self.created_by = created_by self.voice_config = voice_config self.duration_seconds = duration_seconds self.language = language or user.learned_language self.topic_suggestion = topic_suggestion + self.suggestion_type = suggestion_type self.listened_count = 0 self.pause_position_seconds = 0 From 2bb32a91ec3b66c1ccdf9a4d9f20477993810050 Mon Sep 17 00:00:00 2001 From: Mircea Lungu Date: Fri, 3 Apr 2026 13:35:23 +0200 Subject: [PATCH 3/6] Fix: regenerate lesson when suggestion_type changes Previously only topic_suggestion text was compared when checking whether to regenerate. Now suggestion_type is also compared, and included in the lesson response dict. Co-Authored-By: Claude Opus 4.6 (1M context) --- zeeguu/core/audio_lessons/daily_lesson_generator.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zeeguu/core/audio_lessons/daily_lesson_generator.py b/zeeguu/core/audio_lessons/daily_lesson_generator.py index 1be330c4..6edc22c6 100644 --- a/zeeguu/core/audio_lessons/daily_lesson_generator.py +++ b/zeeguu/core/audio_lessons/daily_lesson_generator.py @@ -67,9 +67,12 @@ def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=No # If the user provided a topic that differs from the existing lesson's, # delete the old lesson so a new one can be generated with the topic. existing_topic = existing_lesson.get("topic_suggestion") - if topic_suggestion and topic_suggestion != existing_topic: + existing_type = existing_lesson.get("suggestion_type") + topic_changed = topic_suggestion and topic_suggestion != existing_topic + type_changed = suggestion_type and suggestion_type != existing_type + if topic_changed or type_changed: log( - f"[prepare_lesson_generation] Topic changed ('{existing_topic}' -> '{topic_suggestion}'), deleting existing lesson" + f"[prepare_lesson_generation] Suggestion changed ('{existing_topic}/{existing_type}' -> '{topic_suggestion}/{suggestion_type}'), deleting existing lesson" ) self.delete_todays_lesson_for_user(user, timezone_offset) else: @@ -491,6 +494,7 @@ def _format_lesson_response(self, lesson): "is_completed": lesson.is_completed, "listened_count": lesson.listened_count, "topic_suggestion": lesson.topic_suggestion, + "suggestion_type": lesson.suggestion_type, } def get_daily_lesson_for_user(self, user, lesson_id=None): From 6c9dc93c7b3b9edee14165f55448d410ed9da043 Mon Sep 17 00:00:00 2001 From: Mircea Lungu Date: Fri, 3 Apr 2026 13:52:08 +0200 Subject: [PATCH 4/6] Bump topic_suggestion cap to 80 chars, drop word-count limit Co-Authored-By: Claude Opus 4.6 (1M context) --- zeeguu/api/endpoints/audio_lessons.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zeeguu/api/endpoints/audio_lessons.py b/zeeguu/api/endpoints/audio_lessons.py index c17a2697..58c89f20 100644 --- a/zeeguu/api/endpoints/audio_lessons.py +++ b/zeeguu/api/endpoints/audio_lessons.py @@ -95,8 +95,7 @@ def generate_daily_lesson(): if suggestion_type not in (None, *VALID_SUGGESTION_TYPES): suggestion_type = None if topic_suggestion: - # Cap at 6 words and 48 characters to limit prompt injection surface - topic_suggestion = " ".join(topic_suggestion.split()[:6])[:48].strip() or None + topic_suggestion = topic_suggestion[:80].strip() or None else: topic_suggestion = None From 8b2657614a526fcbcee1dba2223e7ec596ac171c Mon Sep 17 00:00:00 2001 From: Mircea Lungu Date: Fri, 3 Apr 2026 14:41:50 +0200 Subject: [PATCH 5/6] Simplify topic_suggestion parsing to one line Co-Authored-By: Claude Opus 4.6 (1M context) --- zeeguu/api/endpoints/audio_lessons.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/zeeguu/api/endpoints/audio_lessons.py b/zeeguu/api/endpoints/audio_lessons.py index 58c89f20..fb59c855 100644 --- a/zeeguu/api/endpoints/audio_lessons.py +++ b/zeeguu/api/endpoints/audio_lessons.py @@ -90,14 +90,10 @@ def generate_daily_lesson(): # Get timezone offset from form data (default to 0 for UTC) timezone_offset = flask.request.form.get("timezone_offset", 0, type=int) - topic_suggestion = flask.request.form.get("topic_suggestion", "").strip() + topic_suggestion = flask.request.form.get("topic_suggestion", "").strip()[:80].strip() or None suggestion_type = flask.request.form.get("suggestion_type", "").strip() or None if suggestion_type not in (None, *VALID_SUGGESTION_TYPES): suggestion_type = None - if topic_suggestion: - topic_suggestion = topic_suggestion[:80].strip() or None - else: - topic_suggestion = None result = generator.prepare_lesson_generation(user, timezone_offset, topic_suggestion, suggestion_type) From 23ba6459163ba3114ec8c4ea435b1aba5e87414b Mon Sep 17 00:00:00 2001 From: Mircea Lungu Date: Fri, 3 Apr 2026 14:47:33 +0200 Subject: [PATCH 6/6] Rename topic_suggestion to suggestion in DB, models, and API Rename column and all references from topic_suggestion to suggestion since the field now covers both topics and situations. Includes migration to rename the DB column. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...-rename_topic_suggestion_to_suggestion.sql | 5 +++ zeeguu/api/endpoints/audio_lessons.py | 6 ++-- .../audio_lessons/daily_lesson_generator.py | 32 +++++++++---------- zeeguu/core/audio_lessons/script_generator.py | 12 +++---- zeeguu/core/model/audio_lesson_meaning.py | 10 +++--- zeeguu/core/model/daily_audio_lesson.py | 6 ++-- 6 files changed, 38 insertions(+), 33 deletions(-) create mode 100644 tools/migrations/26-04-03-b--rename_topic_suggestion_to_suggestion.sql diff --git a/tools/migrations/26-04-03-b--rename_topic_suggestion_to_suggestion.sql b/tools/migrations/26-04-03-b--rename_topic_suggestion_to_suggestion.sql new file mode 100644 index 00000000..479b05b7 --- /dev/null +++ b/tools/migrations/26-04-03-b--rename_topic_suggestion_to_suggestion.sql @@ -0,0 +1,5 @@ +ALTER TABLE daily_audio_lesson +CHANGE COLUMN topic_suggestion suggestion VARCHAR(100) DEFAULT NULL; + +ALTER TABLE audio_lesson_meaning +CHANGE COLUMN topic_suggestion suggestion VARCHAR(100) DEFAULT NULL; diff --git a/zeeguu/api/endpoints/audio_lessons.py b/zeeguu/api/endpoints/audio_lessons.py index fb59c855..f9aab488 100644 --- a/zeeguu/api/endpoints/audio_lessons.py +++ b/zeeguu/api/endpoints/audio_lessons.py @@ -58,7 +58,7 @@ def _generate_lesson_in_background(user_id, preparation): translation_language=preparation["translation_language"], cefr_level=preparation["cefr_level"], progress=progress, - topic_suggestion=preparation.get("topic_suggestion"), + suggestion=preparation.get("suggestion"), suggestion_type=preparation.get("suggestion_type"), ) except Exception as e: @@ -90,12 +90,12 @@ def generate_daily_lesson(): # Get timezone offset from form data (default to 0 for UTC) timezone_offset = flask.request.form.get("timezone_offset", 0, type=int) - topic_suggestion = flask.request.form.get("topic_suggestion", "").strip()[:80].strip() or None + suggestion = flask.request.form.get("suggestion", "").strip()[:80].strip() or None suggestion_type = flask.request.form.get("suggestion_type", "").strip() or None if suggestion_type not in (None, *VALID_SUGGESTION_TYPES): suggestion_type = None - result = generator.prepare_lesson_generation(user, timezone_offset, topic_suggestion, suggestion_type) + result = generator.prepare_lesson_generation(user, timezone_offset, suggestion, suggestion_type) # Existing lesson found — return it directly if result.get("lesson_id"): diff --git a/zeeguu/core/audio_lessons/daily_lesson_generator.py b/zeeguu/core/audio_lessons/daily_lesson_generator.py index 6edc22c6..eb42d172 100644 --- a/zeeguu/core/audio_lessons/daily_lesson_generator.py +++ b/zeeguu/core/audio_lessons/daily_lesson_generator.py @@ -42,7 +42,7 @@ def lesson_builder(self): self._lesson_builder = LessonBuilder() return self._lesson_builder - def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=None, suggestion_type=None): + def prepare_lesson_generation(self, user, timezone_offset=0, suggestion=None, suggestion_type=None): """ Validate and prepare for lesson generation (synchronous, fast). Returns either an error/existing-lesson dict, or a preparation dict @@ -51,7 +51,7 @@ def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=No Args: user: The User object to generate a lesson for timezone_offset: Client's timezone offset in minutes from UTC - topic_suggestion: Optional short topic hint for the LLM (max 100 chars) + suggestion: Optional short topic hint for the LLM (max 100 chars) suggestion_type: Optional type of suggestion ("topic" or "situation") Returns: @@ -66,13 +66,13 @@ def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=No if existing_lesson.get("lesson_id"): # If the user provided a topic that differs from the existing lesson's, # delete the old lesson so a new one can be generated with the topic. - existing_topic = existing_lesson.get("topic_suggestion") + existing_topic = existing_lesson.get("suggestion") existing_type = existing_lesson.get("suggestion_type") - topic_changed = topic_suggestion and topic_suggestion != existing_topic + topic_changed = suggestion and suggestion != existing_topic type_changed = suggestion_type and suggestion_type != existing_type if topic_changed or type_changed: log( - f"[prepare_lesson_generation] Suggestion changed ('{existing_topic}/{existing_type}' -> '{topic_suggestion}/{suggestion_type}'), deleting existing lesson" + f"[prepare_lesson_generation] Suggestion changed ('{existing_topic}/{existing_type}' -> '{suggestion}/{suggestion_type}'), deleting existing lesson" ) self.delete_todays_lesson_for_user(user, timezone_offset) else: @@ -144,7 +144,7 @@ def prepare_lesson_generation(self, user, timezone_offset=0, topic_suggestion=No "translation_language": translation_language, "cefr_level": cefr_level, "progress_id": progress.id, - "topic_suggestion": topic_suggestion, + "suggestion": suggestion, "suggestion_type": suggestion_type, } @@ -174,7 +174,7 @@ def generate_audio_lesson_meaning( cefr_level, created_by="claude-v1", progress=None, - topic_suggestion=None, + suggestion=None, suggestion_type=None, ): """ @@ -187,7 +187,7 @@ def generate_audio_lesson_meaning( cefr_level: CEFR level for the lesson created_by: String identifying who created this lesson progress: Optional AudioLessonGenerationProgress for tracking - topic_suggestion: Optional short topic hint for the LLM + suggestion: Optional short topic hint for the LLM suggestion_type: Optional type ("topic" or "situation") Returns: @@ -201,7 +201,7 @@ def generate_audio_lesson_meaning( # Check if audio lesson already exists for this meaning, teacher language, and topic existing_lesson = AudioLessonMeaning.find( - meaning=meaning, teacher_language=teacher_lang, topic_suggestion=topic_suggestion, suggestion_type=suggestion_type + meaning=meaning, teacher_language=teacher_lang, suggestion=suggestion, suggestion_type=suggestion_type ) if existing_lesson: return existing_lesson @@ -218,7 +218,7 @@ def generate_audio_lesson_meaning( origin_language=origin_language, translation_language=translation_language, cefr_level=cefr_level, - topic_suggestion=topic_suggestion, + suggestion=suggestion, suggestion_type=suggestion_type, ) @@ -234,7 +234,7 @@ def generate_audio_lesson_meaning( created_by=created_by, difficulty_level=cefr_level, teacher_language=teacher_lang, - topic_suggestion=topic_suggestion, + suggestion=suggestion, suggestion_type=suggestion_type, ) db.session.add(audio_lesson_meaning) @@ -276,7 +276,7 @@ def generate_daily_lesson( translation_language: str, cefr_level: str, progress: AudioLessonGenerationProgress = None, - topic_suggestion: str = None, + suggestion: str = None, suggestion_type: str = None, ) -> dict: """ @@ -289,7 +289,7 @@ def generate_daily_lesson( origin_language: Language code for the words being learned (e.g. 'es', 'da') translation_language: Language code for translations (e.g. 'en') cefr_level: CEFR level for the lesson (e.g. 'A1', 'B2') - topic_suggestion: Optional short topic hint for the LLM + suggestion: Optional short topic hint for the LLM suggestion_type: Optional type ("topic" or "situation") Returns: @@ -307,7 +307,7 @@ def generate_daily_lesson( user=user, created_by="generate_daily_lesson_v1", language=user.learned_language, - topic_suggestion=topic_suggestion, + suggestion=suggestion, suggestion_type=suggestion_type, ) db.session.add(daily_lesson) @@ -341,7 +341,7 @@ def generate_daily_lesson( audio_lesson_meaning = self.generate_audio_lesson_meaning( user_word, origin_language, translation_language, cefr_level, progress=progress, - topic_suggestion=topic_suggestion, + suggestion=suggestion, suggestion_type=suggestion_type, ) except Exception as e: @@ -493,7 +493,7 @@ def _format_lesson_response(self, lesson): "is_paused": lesson.is_paused, "is_completed": lesson.is_completed, "listened_count": lesson.listened_count, - "topic_suggestion": lesson.topic_suggestion, + "suggestion": lesson.suggestion, "suggestion_type": lesson.suggestion_type, } diff --git a/zeeguu/core/audio_lessons/script_generator.py b/zeeguu/core/audio_lessons/script_generator.py index e8253300..9bb60b70 100644 --- a/zeeguu/core/audio_lessons/script_generator.py +++ b/zeeguu/core/audio_lessons/script_generator.py @@ -26,7 +26,7 @@ def generate_lesson_script( translation_language: str, cefr_level: str = "A1", generator_prompt_file="meaning_lesson--teacher_challenges_both_dialogue_and_beyond-v2.txt", - topic_suggestion: str = None, + suggestion: str = None, suggestion_type: str = None, ) -> str: """ @@ -39,7 +39,7 @@ def generate_lesson_script( translation_language: Language code of the translation (e.g., 'en') cefr_level: Cefr level of the word being learned generator_prompt_file: full filename - topic_suggestion: Optional short topic hint for the LLM + suggestion: Optional short topic hint for the LLM suggestion_type: Optional type ("topic" or "situation") Returns: @@ -81,13 +81,13 @@ def generate_lesson_script( cefr_level=cefr_level, ) - if topic_suggestion: + if suggestion: if suggestion_type == "situation": - prompt += f'\nSITUATION: Structure the lesson as a roleplay scenario: "{topic_suggestion}". The dialogue should simulate a real conversation the learner might have in this situation.\n' + prompt += f'\nSITUATION: Structure the lesson as a roleplay scenario: "{suggestion}". The dialogue should simulate a real conversation the learner might have in this situation.\n' else: - prompt += f'\nTOPIC: Set the dialogue scenario in a context related to "{topic_suggestion}". The examples and challenges should use vocabulary relevant to this topic.\n' + prompt += f'\nTOPIC: Set the dialogue scenario in a context related to "{suggestion}". The examples and challenges should use vocabulary relevant to this topic.\n' - log(f"Generating script for {origin_word} -> {translation_word} (topic: {topic_suggestion}, type: {suggestion_type})") + log(f"Generating script for {origin_word} -> {translation_word} (topic: {suggestion}, type: {suggestion_type})") try: # Use unified LLM service with automatic Anthropic -> DeepSeek fallback diff --git a/zeeguu/core/model/audio_lesson_meaning.py b/zeeguu/core/model/audio_lesson_meaning.py index 427fe2f7..aa1dfb89 100644 --- a/zeeguu/core/model/audio_lesson_meaning.py +++ b/zeeguu/core/model/audio_lesson_meaning.py @@ -24,7 +24,7 @@ class AudioLessonMeaning(db.Model): script = Column(Text, nullable=False) voice_config = Column(JSON) - topic_suggestion = Column(String(100), nullable=True) + suggestion = Column(String(100), nullable=True) suggestion_type = Column(String(20), nullable=True) teacher_language_id = Column(Integer, ForeignKey(Language.id), nullable=True) @@ -47,7 +47,7 @@ def __init__( voice_config=None, duration_seconds=None, teacher_language=None, - topic_suggestion=None, + suggestion=None, suggestion_type=None, ): self.meaning_id = meaning.id @@ -57,7 +57,7 @@ def __init__( self.lesson_type = lesson_type self.voice_config = voice_config self.duration_seconds = duration_seconds - self.topic_suggestion = topic_suggestion + self.suggestion = suggestion self.suggestion_type = suggestion_type if teacher_language: self.teacher_language_id = teacher_language.id @@ -72,10 +72,10 @@ def audio_file_path(self): return f"/audio/lessons/{self.meaning_id}-{lang_code}.mp3" @classmethod - def find(cls, meaning, teacher_language=None, topic_suggestion=None, suggestion_type=None): + def find(cls, meaning, teacher_language=None, suggestion=None, suggestion_type=None): """Find audio lesson for a specific meaning, teacher language, topic, and type""" query = cls.query.filter_by(meaning=meaning) if teacher_language: query = query.filter_by(teacher_language_id=teacher_language.id) - query = query.filter_by(topic_suggestion=topic_suggestion, suggestion_type=suggestion_type) + query = query.filter_by(suggestion=suggestion, suggestion_type=suggestion_type) return query.first() diff --git a/zeeguu/core/model/daily_audio_lesson.py b/zeeguu/core/model/daily_audio_lesson.py index 34d97404..85fb6f51 100644 --- a/zeeguu/core/model/daily_audio_lesson.py +++ b/zeeguu/core/model/daily_audio_lesson.py @@ -41,7 +41,7 @@ class DailyAudioLesson(db.Model): pause_position_seconds = Column(Integer, default=0) # Optional topic suggestion that themed the lesson - topic_suggestion = Column(db.String(100), nullable=True) + suggestion = Column(db.String(100), nullable=True) suggestion_type = Column(db.String(20), nullable=True) # Relationship to segments (individual meaning lessons) @@ -52,13 +52,13 @@ class DailyAudioLesson(db.Model): cascade="all, delete-orphan", ) - def __init__(self, user, created_by, voice_config=None, duration_seconds=None, language=None, topic_suggestion=None, suggestion_type=None): + def __init__(self, user, created_by, voice_config=None, duration_seconds=None, language=None, suggestion=None, suggestion_type=None): self.user = user self.created_by = created_by self.voice_config = voice_config self.duration_seconds = duration_seconds self.language = language or user.learned_language - self.topic_suggestion = topic_suggestion + self.suggestion = suggestion self.suggestion_type = suggestion_type self.listened_count = 0 self.pause_position_seconds = 0