import os import json from openai import OpenAI from typing import Dict, List, Any, Optional from dotenv import load_dotenv class LLMHandler: def __init__(self): """Initialize the LLM handler with OpenAI client.""" load_dotenv() self.client = OpenAI(api_key=os.getenv('OPENAI_API_KEY')) self.model = "gpt-4o-mini" def input_extraction_validation(self, user_prompt: str) -> Dict[str, Any]: """ Extract and validate key information from user prompt. Returns a dictionary with extracted information and duration adequacy assessment. """ system_prompt = """ You are an expert in language education, specializing in extracting key information from lesson requests. Your task is to identify and validate crucial parameters for language lesson planning. """ user_message = f""" Extract key information from the following request for a lesson plan: User Request: {user_prompt} Extract the following information: - topic: The main subject of the lesson - age_group: Target age group (e.g., Elementary (6-11), High School (14-18), Adult) - proficiency_level: Language proficiency according to CEFR (e.g., A1, A2, B1, B2, C1, C2) - lesson_duration: Length of the lesson in minutes - tech_requirements: Any technology needed for the lesson - output_language: Language in which materials should be produced - number_of_students: Number of students in the class - skills_to_focus: Skills to emphasize (e.g., reading, writing, listening, speaking) Then, evaluate whether the provided duration is sufficient for a structured lesson plan (PPP, ESA, TTT, TBL, or UbD) based on: - The complexity of the topic - The proficiency level of the learners If duration is insufficient: set "duration_adequacy" to "Not sufficient for structured lesson plan" If duration is sufficient: set "duration_adequacy" to "Sufficient for structured lesson plan" Return ONLY a JSON object with these fields. No explanations or other text. """ try: response = self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ], response_format={"type": "json_object"} ) # Try to parse the response as JSON extracted_info = json.loads(response.choices[0].message.content) # Validate essential fields and set defaults if missing if not extracted_info.get("topic"): extracted_info["topic"] = "No context provided" if not extracted_info.get("lesson_duration"): extracted_info["lesson_duration"] = "30 minutes" if not extracted_info.get("proficiency_level"): extracted_info["proficiency_level"] = "Intermediate [B1, B2]" if not extracted_info.get("age_group"): extracted_info["age_group"] = "Elementary (6-11)" if not extracted_info.get("tech_requirements"): extracted_info["tech_requirements"] = ["White Board", "Internet Access"] if not extracted_info.get("output_language"): extracted_info["output_language"] = "English" if not extracted_info.get("number_of_students"): extracted_info["number_of_students"] = "No context provided" if not extracted_info.get("skills_to_focus"): extracted_info["skills_to_focus"] = "No context provided" if not extracted_info.get("duration_adequacy"): # Fallback if model doesn't assess duration extracted_info["duration_adequacy"] = "Sufficient for structured lesson plan" return extracted_info except Exception as e: print(f"Error in input_extraction_validation: {str(e)}") # Return default values return { "topic": "General language practice", "age_group": "Elementary (6-11)", "proficiency_level": "Intermediate [B1, B2]", "lesson_duration": "30 minutes", "tech_requirements": ["White Board", "Internet Access"], "output_language": "English", "number_of_students": "No context provided", "skills_to_focus": "No context provided", "duration_adequacy": "Sufficient for structured lesson plan" } def lesson_plan_structure(self, input_data: Dict[str, Any]) -> Dict[str, str]: """ Determine the appropriate lesson plan structure based on the extracted information. Returns a dictionary with the selected structure and description. """ system_prompt = """ You are an expert educator specializing in language lesson planning frameworks. Your task is to select the most appropriate lesson structure based on given parameters. """ user_message = f""" Based on the following information about a language lesson, determine the most appropriate lesson plan structure: Topic: {input_data.get('topic', 'No context provided')} Age Group: {input_data.get('age_group', 'No context provided')} Proficiency Level: {input_data.get('proficiency_level', 'No context provided')} Lesson Duration: {input_data.get('lesson_duration', 'No context provided')} Duration Adequacy: {input_data.get('duration_adequacy', 'No context provided')} Skills to Focus: {input_data.get('skills_to_focus', 'No context provided')} If Duration Adequacy is "Not sufficient for structured lesson plan": - Set output_structure to "Generic" - Set description to "The given duration is not sufficient to create a fully structured lesson plan. Instead, a simplified activity plan has been provided." Otherwise, select the most appropriate structure from: - PPP (Presentation, Practice, Production) - For young learners, basic comprehension, structured activities - ESA (Engage, Study, Activate) - For young learners needing flexibility (consider Boomerang ESA or Patchwork ESA variants) - TBL (Task-Based Learning) - For high school, advanced learners, or adults focusing on real-world applications - TTT (Test, Teach, Test) - For learners with prior knowledge to assess gaps, instruct, and reassess - UbD (Understanding by Design) - For long-term, curriculum-based instruction Return your response as a JSON object with these two fields: - output_structure: The selected structure (PPP, ESA, TBL, TTT, UbD, or Generic) - description: A brief explanation of why this framework structure was chosen """ try: response = self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ], response_format={"type": "json_object"} ) structure_info = json.loads(response.choices[0].message.content) # Validate required fields if not structure_info.get("output_structure"): structure_info["output_structure"] = "Generic" if not structure_info.get("description"): structure_info[ "description"] = "A simplified lesson plan structure was selected based on the available information." return structure_info except Exception as e: print(f"Error in lesson_plan_structure: {str(e)}") # Fallback if the response is not valid JSON return { "output_structure": "Generic", "description": "A simplified lesson plan structure was selected as a fallback." } def activity_template_selection(self, input_data: Dict[str, Any], structure_info: Dict[str, str]) -> Dict[ str, List[str]]: """ Select appropriate activity/worksheet templates based on lesson structure, topic, and duration. Returns a dictionary with a list of selected activity templates. """ system_prompt = """ You are an expert language instructor specializing in selecting appropriate activities for lessons. Your task is to choose the most suitable activity templates based on lesson parameters. """ user_message = f""" Select the top 5 most suitable worksheet and activity templates for a language lesson with the following characteristics: Lesson Structure: {structure_info.get('output_structure', 'Generic')} Topic: {input_data.get('topic', 'No context provided')} Duration: {input_data.get('lesson_duration', '30 minutes')} Age Group: {input_data.get('age_group', 'Elementary (6-11)')} Proficiency Level: {input_data.get('proficiency_level', 'Intermediate [B1, B2]')} Skills to Focus: {input_data.get('skills_to_focus', 'No context provided')} Choose ONLY from these activity templates: 1. Fill in the blank 2. Picture/Visual 3. Sentence Formation 4. Finding 5. Matching 6. Short Response 7. Long Response 8. Correction 9. Audio as material [suitable for activities to focus on listening tasks] Selection Guidelines: Prioritize activities that match the Skills to Focus, proficiency level, lesson structure, topic. For beginner learners (A1, A2), emphasize simple activities like Matching, Picture/Visual, and Fill in the Blank. For intermediate/advanced learners (B1, B2), incorporate Finding, Short Response, and Long Response. Ensure a balance of engagement and difficulty. Return your selection as a JSON object with this field: - activity_templates: An array containing 5 selected activity templates from the list above """ try: response = self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ], response_format={"type": "json_object"} ) templates_info = json.loads(response.choices[0].message.content) # Validate the response contains activity_templates if not templates_info.get("activity_templates"): templates_info["activity_templates"] = [ "Fill in the blank", "Picture/Visual", "Matching", "Short Response", "Correction" ] # Ensure we have exactly 5 templates if len(templates_info["activity_templates"]) < 5: # Add default templates to reach 5 default_templates = [ "Fill in the blank", "Picture/Visual", "Matching", "Short Response", "Correction" ] for template in default_templates: if template not in templates_info["activity_templates"] and len( templates_info["activity_templates"]) < 5: templates_info["activity_templates"].append(template) if len(templates_info["activity_templates"]) > 5: templates_info["activity_templates"] = templates_info["activity_templates"][:5] return templates_info except Exception as e: print(f"Error in activity_template_selection: {str(e)}") # Fallback if the response is not valid JSON return { "activity_templates": [ "Fill in the blank", "Picture/Visual", "Matching", "Short Response", "Correction" ] } def lesson_plan_generation( self, input_data: Dict[str, Any], structure_info: Dict[str, str], activity_info: Dict[str, List[str]] ) -> Dict[str, Any]: """ Generate a complete lesson plan based on all previous inputs. Returns a structured JSON lesson plan. """ lesson_structure = structure_info.get("output_structure", "Generic") structure_description = structure_info.get("description", "") activity_templates = activity_info.get("activity_templates", []) system_prompt = """ You are an expert language instructor with extensive experience in creating detailed lesson plans. Your task is to generate a comprehensive, well-structured lesson plan based on provided parameters. These are the details about the activity templates we are considering: Fill in the Blanks: This activity assesses students’ ability to apply knowledge learned during the lesson. Example: Complete the sentences using the correct verb forms. Picture/Visual: This activity tests students' ability to interpret basic pictorial information, such as icons of fruits, animals, and everyday objects. Example: Identify and write the names of the fruits and vegetables shown in the images. Sentence Formation: This activity helps students practice sentence construction using minimal context. Example: Given the words "your brother, height," form a question: "How tall is your brother?" Finding: This activity strengthens vocabulary and comprehension by requiring students to identify words based on definitions or clues. Example: What is the term for a woman on her wedding day? Answer: Bride Matching: This activity reinforces the relationship between different concepts by having students pair related elements. Example: Match the illnesses in one column with their appropriate treatments in another. Short Response: This activity helps students practice forming concise answers to questions. Example: "Which do you prefer—tea or coffee?" Answer: I prefer coffee over tea. Long Response: This activity encourages students to construct detailed responses or dialogues. Example: Write a conversation between you and a friend who has just passed an exam. Correction: This activity develops students’ ability to identify and correct grammatical or structural errors in sentences. Example: Identify and correct the mistakes in the given sentences. Audio: This activity aids students to develop their listening skills. Example: Listen to the audio of customer speaking to a tech support agent and answer to the below questions. """ if lesson_structure == "Generic": user_message = f""" Generate a Basic Activity Plan for a language lesson with the following details: Topic: {input_data.get('topic', 'No context provided')} Age Group: {input_data.get('age_group', 'Elementary (6-11)')} Proficiency Level: {input_data.get('proficiency_level', 'Intermediate [B1, B2]')} Lesson Duration: {input_data.get('lesson_duration', '30 minutes')} Tech Requirements: {input_data.get('tech_requirements', ['White Board', 'Internet Access'])} Output Language: {input_data.get('output_language', 'English')} Number of Students: {input_data.get('number_of_students', 'No context provided')} Skills to Focus: {input_data.get('skills_to_focus', 'No context provided')} The plan should focus on selecting 2 or 3 meaningful activity due to time constraints. Include a note stating: "The given duration is not sufficient to create a fully structured lesson plan. Instead, a simplified activity plan has been provided." Include only activities that match these templates: {', '.join(activity_templates)}. You must include the following sections in your JSON response: - lesson_plan_title - structure (which should be "Generic") - structure_explanation (explanation that this is a simplified plan due to time constraints) - objectives - learning_outcomes - materials_and_resources (*Avoid external platforms like Kahoot, Quizlet*) - activity_plan (with timing, e.g., "Activity [X min]") and detail description explaining the activity and its objective. - assessment_strategy Provide your complete response in JSON format. """ else: user_message = f""" Generate a structured lesson plan using the {lesson_structure} framework for a language lesson with the following details: Topic: {input_data.get('topic', 'No context provided')} Age Group: {input_data.get('age_group', 'Elementary (6-11)')} Proficiency Level: {input_data.get('proficiency_level', 'Intermediate [B1, B2]')} Lesson Duration: {input_data.get('lesson_duration', '30 minutes')} Tech Requirements: {input_data.get('tech_requirements', ['White Board', 'Internet Access'])} Output Language: {input_data.get('output_language', 'English')} Number of Students: {input_data.get('number_of_students', 'No context provided')} Skills to Focus: {input_data.get('skills_to_focus', 'No context provided')} Framework Structure: {lesson_structure} Framework Explanation: {structure_description} Include only activities that match these templates: {', '.join(activity_templates)} You must include the following sections in your JSON response: - lesson_plan_title - structure (which should be "{lesson_structure}") - structure_explanation (brief explanation of why this framework was chosen) - objectives - learning_outcomes - essential_questions (if using UbD) - materials_and_resources (avoid external platforms like Kahoot, Quizlet) - lesson_plan_structure - with all appropriate sections for {lesson_structure} framework, each containing: * key_parts (with time allocations, e.g., "Engage [20 minutes]") * objective (purpose of this section) * activityies (with timing, e.g., "Activity [X min]") and detail description explaining the activity and its objective. - assessment_strategies - additional_details Provide your complete response in JSON format. For time allocations, use brackets format (e.g., "Reflection [5 min]"). Ensure all activities have detailed descriptions. """ try: response = self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ], response_format={"type": "json_object"} ) lesson_plan = json.loads(response.choices[0].message.content) # Basic validation of the lesson plan required_fields = ["lesson_plan_title", "structure", "objectives", "learning_outcomes"] for field in required_fields: if field not in lesson_plan: if field == "lesson_plan_title": lesson_plan[field] = f"Language Lesson: {input_data.get('topic', 'General Practice')}" elif field == "structure": lesson_plan[field] = lesson_structure elif field == "objectives": lesson_plan[field] = ["Improve language skills"] elif field == "learning_outcomes": lesson_plan[field] = ["Students will be able to communicate more effectively"] return lesson_plan except Exception as e: print(f"Error in lesson_plan_generation: {str(e)}") # Create a minimal fallback lesson plan return { "lesson_plan_title": f"Language Lesson: {input_data.get('topic', 'General Practice')}", "structure": lesson_structure, "structure_explanation": structure_description or "Basic language practice structure", "objectives": ["Improve language skills"], "learning_outcomes": ["Students will be able to communicate more effectively"], "materials_and_resources": ["Whiteboard", "Handouts"], "lesson_plan_structure": { "main_activity": { "key_parts": "Main Activity [30 minutes]", "objective": "Practice language skills", "activities": "Complete worksheet activities" } }, "assessment_strategies": ["Informal assessment through observation"], "note": "This is a fallback lesson plan due to processing limitations." }