WizLab-LessonPlan / llms_handler.py
dhanvanth183's picture
Upload llms_handler.py
560c197 verified
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."
}