import json import logging import random from odoo.addons.encoach_ai.models.constants import ( CEFR_LEVELS, GPT_MODELS, TEMPERATURE, TOPICS, ) from odoo.addons.encoach_ai.services.openai_service import EncoachOpenAIService _logger = logging.getLogger(__name__) PASSAGE_DIFFICULTY = { 1: "fairly easy and consist of multiple paragraphs", 2: "fairly hard and consist of multiple paragraphs", 3: ( "very hard, present different points or theories, " "cite different sources, and consist of multiple paragraphs" ), } LISTENING_SECTION_CONFIG = { 1: { "type": "conversation", "format": '{"conversation": [{"name": "name", "gender": "gender", "text": "text"}]}', "prompt": ( "Compose an authentic conversation between two individuals " 'on the topic of "{topic}". Please include random names and genders. ' "Include misleading discourse (dates, colors, etc.) and spelling of names. " "Make sure that the generated conversation does not contain " "forbidden subjects in muslim countries." ), }, 2: { "type": "monologue", "format": '{"monologue": "monologue"}', "prompt": ( "Generate a comprehensive monologue set in the social context " 'of "{topic}". Make sure that the generated monologue does not ' "contain forbidden subjects in muslim countries." ), }, 3: { "type": "conversation", "format": '{"conversation": [{"name": "name", "gender": "gender", "text": "text"}]}', "prompt": ( "Compose an authentic and elaborate conversation between " 'up to four individuals on the topic of "{topic}". ' "Please include random names and genders. Make sure that the " "generated conversation does not contain forbidden subjects " "in muslim countries." ), }, 4: { "type": "monologue", "format": '{"monologue": "monologue"}', "prompt": ( "Generate a comprehensive and complex monologue on the academic " 'subject of "{topic}". Make sure that the generated monologue ' "does not contain forbidden subjects in muslim countries." ), }, } class EncoachGenerationService: def __init__(self, env): self.env = env self.ai = EncoachOpenAIService(env) # ------------------------------------------------------------------ # Reading # ------------------------------------------------------------------ def generate_reading_passage(self, passage_num, topic=None, word_count=500): """Generate an IELTS reading passage (passage 1, 2, or 3).""" topic = topic or random.choice(TOPICS) difficulty = PASSAGE_DIFFICULTY.get(passage_num, PASSAGE_DIFFICULTY[1]) system_msg = ( "You are a helpful assistant designed to output JSON on this format: " '{"title": "title of the text", "text": "generated text"}' ) user_msg = ( f"Generate an extensive text for IELTS Reading Passage {passage_num}, " f'of at least {word_count} words, on the topic of "{topic}". ' "The passage should offer a substantial amount of information " f"relevant to the chosen subject matter. It should be {difficulty}. " "Make sure that the generated text does not contain forbidden " "subjects in muslim countries." ) return self.ai.prediction( model=GPT_MODELS["generation"], messages=[ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ], temperature=TEMPERATURE["generation"], fields_to_check=["title", "text"], ) def generate_reading_exercises(self, text, exercises_config, difficulty): """Generate exercises for a reading passage. exercises_config: list of dicts with 'type' and 'quantity' keys. difficulty: CEFR level string. """ system_msg = ( "You are a helpful assistant designed to output JSON. " "Generate reading comprehension exercises based on the given text." ) config_str = json.dumps(exercises_config) user_msg = ( f"Based on the following text, generate exercises according to this " f"configuration: {config_str}. Target CEFR level: {difficulty}.\n\n" f'Text: "{text}"' ) return self.ai.prediction( model=GPT_MODELS["generation"], messages=[ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ], temperature=TEMPERATURE["generation"], ) # ------------------------------------------------------------------ # Listening # ------------------------------------------------------------------ def generate_listening_dialog(self, section, topic=None, difficulty=None): """Generate a listening dialog/monologue for the given section (1-4).""" topic = topic or random.choice(TOPICS) config = LISTENING_SECTION_CONFIG.get(section, LISTENING_SECTION_CONFIG[1]) system_msg = ( "You are a helpful assistant designed to output JSON on this format: " + config["format"] ) user_msg = config["prompt"].format(topic=topic) if difficulty: user_msg += f" Target CEFR level: {difficulty}." return self.ai.prediction( model=GPT_MODELS["generation"], messages=[ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ], temperature=TEMPERATURE["generation"], ) def generate_listening_exercises(self, text, exercises_config, difficulty): """Generate exercises for a listening transcript.""" system_msg = ( "You are a helpful assistant designed to output JSON. " "Generate listening comprehension exercises based on the given transcript." ) config_str = json.dumps(exercises_config) user_msg = ( f"Based on the following transcript, generate exercises according to " f"this configuration: {config_str}. Target CEFR level: {difficulty}.\n\n" f'Transcript: "{text}"' ) return self.ai.prediction( model=GPT_MODELS["generation"], messages=[ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ], temperature=TEMPERATURE["generation"], ) # ------------------------------------------------------------------ # Writing # ------------------------------------------------------------------ def generate_writing_task( self, task, topic=None, difficulty=None, task_type="general" ): """Generate a writing task prompt. task: 1 or 2 task_type: 'general' or 'academic' """ topic = topic or random.choice(TOPICS) difficulty = difficulty or random.choice(CEFR_LEVELS) system_msg = ( "You are a helpful assistant designed to output JSON on this format: " '{"question": "the generated writing task prompt"}' ) if task == 1 and task_type == "general": user_msg = ( "Craft a prompt for an IELTS Writing Task 1 General Training " "exercise that instructs the student to compose a letter based " f'on the topic of "{topic}" of {difficulty} CEFR level difficulty. ' 'The prompt should end with "In the letter you should" followed ' "by 3 bullet points. Make sure it does not contain forbidden " "subjects in muslim countries." ) elif task == 2: user_msg = ( f"Craft a comprehensive question of {difficulty} CEFR level " "difficulty like the ones for IELTS Writing Task 2 General " "Training that directs the candidate to delve into an in-depth " "analysis of contrasting perspectives on the topic of " f'"{topic}". The question should lead to an answer with either ' '"theories", "complicated information" or be "very descriptive" ' "on the topic." ) else: user_msg = ( "Analyze the uploaded image and create a detailed IELTS Writing " "Task 1 Academic prompt. Describe the visual type, context, and " f"create a prompt at {difficulty} CEFR level." ) return self.ai.prediction( model=GPT_MODELS["generation"], messages=[ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ], temperature=TEMPERATURE["generation"], fields_to_check=["question"], ) # ------------------------------------------------------------------ # Speaking # ------------------------------------------------------------------ def generate_speaking_task( self, part, topic=None, first_topic=None, second_topic=None, difficulty=None, ): """Generate a speaking task for the given part (1, 2, or 3).""" system_msg = ( "You are a helpful assistant designed to output JSON. " "Generate IELTS speaking task questions." ) if part == 1: first_topic = first_topic or random.choice(TOPICS) second_topic = second_topic or random.choice(TOPICS) user_msg = ( "Craft 5 simple and single questions of easy difficulty for " "IELTS Speaking Part 1 that encourages candidates to delve " "deeply into personal experiences, preferences, or insights " f'on the topic of "{first_topic}" and the topic of ' f'"{second_topic}". The questions should lead to the usage ' "of 4 verb tenses (present perfect, present, past and future). " "Make sure that the generated question does not contain " "forbidden subjects in muslim countries." ) elif part == 2: topic = topic or random.choice(TOPICS) user_msg = ( "Create a question of medium difficulty for IELTS Speaking " "Part 2 that encourages candidates to narrate a personal " f'experience or story related to the topic of "{topic}". ' "Include 3 prompts that guide the candidate. The prompts " "must not be questions. Also include a suffix like the ones " 'in the IELTS exams that start with "And explain why". ' "Make sure that the generated question does not contain " "forbidden subjects in muslim countries." ) else: topic = topic or random.choice(TOPICS) user_msg = ( "Formulate a set of 5 single questions of hard difficulty " "for IELTS Speaking Part 3 that encourage candidates to " "engage in a meaningful discussion on the topic of " f'"{topic}". They must be 1 single question each and not ' "be double-barreled questions. Make sure that the generated " "question does not contain forbidden subjects in muslim countries." ) if difficulty: user_msg += f" Target CEFR level: {difficulty}." return self.ai.prediction( model=GPT_MODELS["generation"], messages=[ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ], temperature=TEMPERATURE["generation"], ) # ------------------------------------------------------------------ # Level exercises # ------------------------------------------------------------------ def generate_level_exercises(self, exercises_config, difficulty): """Generate level-exam exercises (multiple choice, fill-blanks, etc.). exercises_config: list of dicts with 'type', 'quantity', and optional 'topic', 'size' keys. difficulty: CEFR level string. """ system_msg = ( "You are a helpful assistant designed to output JSON. " "Generate English level exam exercises." ) prompts = [] for cfg in exercises_config: ex_type = cfg.get("type", "multiple_choice") quantity = cfg.get("quantity", 5) topic = cfg.get("topic") or random.choice(TOPICS) size = cfg.get("size", 200) if ex_type == "multiple_choice": prompts.append( f"Generate {quantity} multiple choice questions of 4 options " f"for an english level exam of {difficulty} CEFR level. " "Ensure that the questions cover a range of topics such as " "verb tense, subject-verb agreement, pronoun usage, sentence " "structure, and punctuation. Make sure every question only has " "1 correct answer." ) elif ex_type == "fill_blanks": prompts.append( f"Generate a text of at least {size} words about the topic " f"{topic}. From the generated text choose exactly {quantity} " "words (cannot be sequential words), replace each with " "{id}, and generate a JSON object containing: the modified " "text, solutions array, words array with four options per blank." ) user_msg = "\n\n".join(prompts) return self.ai.prediction( model=GPT_MODELS["generation"], messages=[ {"role": "system", "content": system_msg}, {"role": "user", "content": user_msg}, ], temperature=TEMPERATURE["generation"], )