Spaces:
Running
Running
| import os | |
| from typing import Generator | |
| from dotenv import load_dotenv | |
| from openai import OpenAI | |
| # Load environment variables from .env file | |
| load_dotenv() | |
| class LLMCall: | |
| """LLM client using OpenAI API for educational tutoring.""" | |
| def __init__(self, model: str = "gpt-4o-mini"): | |
| """ | |
| Initialize the LLM client. | |
| Args: | |
| model (str): The OpenAI model to use. Defaults to "gpt-4o-mini". | |
| """ | |
| self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) | |
| self.model = model | |
| def _convert_history(self, message: str, gradio_history: list) -> list[dict]: | |
| """Convert Gradio history format to OpenAI API format.""" | |
| messages = [] | |
| for h in gradio_history: | |
| # Skip system messages in history (we'll add it separately) | |
| if h.get("role") == "system": | |
| continue | |
| messages.append( | |
| { | |
| "role": h.get("role", "user"), | |
| "content": h.get("content", ""), | |
| } | |
| ) | |
| # Add current user input | |
| messages.append({"role": "user", "content": message}) | |
| return messages | |
| def respond( | |
| self, | |
| message: str, | |
| history: list, | |
| system_prompt: str = None, | |
| tutor_name: str = None, | |
| difficulty_level: str = "beginner", | |
| ) -> Generator[str, None, None]: | |
| """ | |
| Generate a response to the user message using the OpenAI LLM. | |
| Args: | |
| message (str): The user's message. | |
| history (list): The conversation history. | |
| system_prompt (str): The system prompt (optional, will be constructed if not provided). | |
| tutor_name (str): The name of the tutor. | |
| difficulty_level (str): The difficulty level (beginner, intermediate, advanced). | |
| Yields: | |
| str: Streaming response chunks. | |
| """ | |
| # Construct messages for OpenAI API | |
| messages = [] | |
| # Add system prompt | |
| if system_prompt: | |
| messages.append({"role": "system", "content": system_prompt}) | |
| # Add conversation history (excluding system messages) | |
| for h in history: | |
| if h.get("role") != "system": | |
| messages.append( | |
| { | |
| "role": h.get("role", "user"), | |
| "content": h.get("content", ""), | |
| } | |
| ) | |
| # Add current user message | |
| messages.append({"role": "user", "content": message}) | |
| # Make streaming API call with educational-appropriate settings | |
| # Lower temperature for more consistent, educational responses | |
| stream = self.client.chat.completions.create( | |
| model=self.model, | |
| messages=messages, | |
| stream=True, | |
| temperature=0.6, # Balanced: creative enough for engagement, consistent for learning | |
| max_tokens=500, # Limit response length for age-appropriate brevity | |
| ) | |
| response = "" | |
| for chunk in stream: | |
| if chunk.choices[0].delta.content is not None: | |
| content = chunk.choices[0].delta.content | |
| response += content | |
| yield response | |
| def extract_key_concepts(self, lesson_content: str) -> list[str]: | |
| """ | |
| Extract key concepts from the lesson content. | |
| Args: | |
| lesson_content (str): The content of the lesson. | |
| Returns: | |
| list[str]: A list of 2 to 5 key concepts from the lesson. | |
| """ | |
| system_prompt = ( | |
| "Your task is to extract 2 to 5 key educational concepts from the provided lesson content. " | |
| "These concepts should be appropriate for early childhood education (ages 3-12). " | |
| "Return only the concept names, one per line. " | |
| "Do not include any additional text, explanations, or numbering. " | |
| "Each concept should be a simple, clear phrase that a child could understand. " | |
| "Example output:\n" | |
| "Colors\n" | |
| "Numbers\n" | |
| "Shapes\n" | |
| "Animals\n" | |
| "Nature\n" | |
| ) | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": lesson_content}, | |
| ] | |
| response = self.client.chat.completions.create( | |
| model=self.model, | |
| messages=messages, | |
| temperature=0.3, | |
| ) | |
| content = response.choices[0].message.content | |
| print("Response from LLM:", content) | |
| # Split the response by new lines and strip whitespace | |
| concepts = [concept.strip() for concept in content.split("\n") if concept.strip()] | |
| # Limit to 10 concepts | |
| return concepts[:10] | |
| def generate_lesson_introduction( | |
| self, lesson_content: str, lesson_name: str, concepts: list[str], language: str = "English" | |
| ) -> str: | |
| """ | |
| Generate an educational introduction for the lesson including: | |
| - A brief summary of the activity | |
| - Key concepts | |
| - Example questions to guide the child | |
| Args: | |
| lesson_content (str): The content of the lesson. | |
| lesson_name (str): The name of the lesson. | |
| concepts (list[str]): List of key concepts extracted from the lesson. | |
| language (str): The language to generate the introduction in. Defaults to "English". | |
| Returns: | |
| str: A formatted introduction with summary, concepts, and example questions. | |
| """ | |
| concepts_text = ", ".join(concepts[:8]) # Show up to 8 concepts | |
| system_prompt = ( | |
| f"You are an educational expert creating a warm, natural introduction for an interactive learning adventure for children ages 3-12. " | |
| f"IMPORTANT: You must write the ENTIRE introduction in {language}. " | |
| f"Create a friendly, conversational introduction that:\n\n" | |
| f"1. Explains what adventure/activity the child is about to experience (in natural, story-like language)\n" | |
| f"2. Describes the child's role or task in this adventure (what they'll be doing)\n" | |
| f"3. Ends with an engaging, open-ended question to start the conversation\n\n" | |
| f"STYLE GUIDELINES:\n" | |
| f"- Write in a warm, conversational tone (like a friend talking to the child)\n" | |
| f"- Use simple, age-appropriate language in {language}\n" | |
| f"- Make it exciting and inviting, not formal or structured\n" | |
| f"- Don't use bullet points or structured lists - write in natural paragraphs\n" | |
| f"- The question at the end should be specific to the adventure and encourage the child to share their thoughts\n" | |
| f"- Keep it to 3-4 sentences total, then the question\n\n" | |
| f"Example structure (but make it unique to the lesson):\n" | |
| f"'Today we're going on [adventure description]! Your mission is to [child's task]. " | |
| f"Along the way, we'll discover [what they'll learn]. So, [engaging question]?'\n\n" | |
| f"Everything must be written in {language}." | |
| ) | |
| user_prompt = ( | |
| f"Lesson Name: {lesson_name}\n\n" | |
| f"Key Concepts: {concepts_text}\n\n" | |
| f"Lesson Content:\n{lesson_content[:2000]}\n\n" | |
| "Create an engaging introduction for this lesson." | |
| ) | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt}, | |
| ] | |
| response = self.client.chat.completions.create( | |
| model=self.model, | |
| messages=messages, | |
| temperature=0.7, # Slightly higher for creativity | |
| max_tokens=400, | |
| ) | |
| introduction = response.choices[0].message.content | |
| return introduction | |
| def generate_lesson(self, topic: str, age_range: str = "3-6") -> str: | |
| """ | |
| Generate a complete lesson content based on a topic using ChatGPT. | |
| Args: | |
| topic (str): The topic for the lesson (e.g., "dinosaurs", "space", "ocean animals"). | |
| age_range (str): The target age range. Defaults to "3-6". | |
| Returns: | |
| str: The generated lesson content suitable for early childhood education. | |
| """ | |
| system_prompt = ( | |
| f"You are an expert educational content creator specializing in early childhood education " | |
| f"(ages {age_range}). Create a comprehensive, engaging lesson about '{topic}'.\n\n" | |
| "The lesson should:\n" | |
| "1. Be written in simple, age-appropriate language\n" | |
| "2. Include key concepts and facts about the topic\n" | |
| "3. Be structured in a way that encourages exploration and curiosity\n" | |
| "4. Include examples and descriptions that children can relate to\n" | |
| "5. Be educational but also fun and engaging\n" | |
| "6. Be approximately 500-1000 words long\n" | |
| "7. Use clear, short sentences\n" | |
| "8. Include concrete examples from children's daily lives when possible\n\n" | |
| "Format the lesson as plain text without markdown. Make it ready to be used by an educational tutor." | |
| ) | |
| user_prompt = ( | |
| f"Create a lesson about '{topic}' for children ages {age_range}. " | |
| f"Make it engaging, educational, and age-appropriate. " | |
| f"Include key concepts, interesting facts, and examples that will help children learn about {topic}." | |
| ) | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt}, | |
| ] | |
| response = self.client.chat.completions.create( | |
| model=self.model, | |
| messages=messages, | |
| temperature=0.7, # Creative but consistent | |
| max_tokens=5000, # Allow for comprehensive lesson content | |
| ) | |
| lesson_content = response.choices[0].message.content | |
| return lesson_content | |