Spaces:
Sleeping
Sleeping
Update learning_platform.py
Browse files- learning_platform.py +84 -103
learning_platform.py
CHANGED
|
@@ -1,14 +1,20 @@
|
|
| 1 |
-
import
|
| 2 |
-
import json
|
| 3 |
from dataclasses import dataclass, field
|
| 4 |
from datetime import datetime
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
|
| 8 |
-
from langchain.chat_models import ChatOpenAI
|
| 9 |
-
from langchain.embeddings import OpenAIEmbeddings # Example for embeddings
|
| 10 |
-
from langgraph.graph import StateGraph, END
|
| 11 |
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
@dataclass
|
| 14 |
class Section:
|
|
@@ -18,7 +24,6 @@ class Section:
|
|
| 18 |
quiz_questions: List[Dict[str, Any]] = field(default_factory=list)
|
| 19 |
is_complete: bool = False
|
| 20 |
|
| 21 |
-
|
| 22 |
@dataclass
|
| 23 |
class LearningModule:
|
| 24 |
title: str
|
|
@@ -29,7 +34,6 @@ class LearningModule:
|
|
| 29 |
learning_objectives: List[str] = field(default_factory=list)
|
| 30 |
is_complete: bool = False
|
| 31 |
|
| 32 |
-
|
| 33 |
@dataclass
|
| 34 |
class LearningPath:
|
| 35 |
topic: str
|
|
@@ -39,7 +43,6 @@ class LearningPath:
|
|
| 39 |
difficulty_level: str
|
| 40 |
is_generating: bool = True
|
| 41 |
|
| 42 |
-
|
| 43 |
@dataclass
|
| 44 |
class CourseState:
|
| 45 |
topic: str
|
|
@@ -49,25 +52,31 @@ class CourseState:
|
|
| 49 |
sections: List[Dict] = field(default_factory=list)
|
| 50 |
error: str = ""
|
| 51 |
|
| 52 |
-
|
| 53 |
class Config:
|
| 54 |
def __init__(self):
|
| 55 |
self.llm_config = {
|
| 56 |
"temperature": 0.7,
|
| 57 |
-
"model": "gpt-
|
| 58 |
"max_tokens": 2000
|
| 59 |
}
|
| 60 |
-
|
| 61 |
self.graph_config = {
|
| 62 |
"recursion_limit": 25,
|
| 63 |
"max_retries": 3,
|
| 64 |
"timeout": 300
|
| 65 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
self.course_defaults = {
|
| 67 |
-
"max_modules": 5
|
|
|
|
|
|
|
| 68 |
}
|
| 69 |
|
| 70 |
-
|
| 71 |
class CoursePrompts:
|
| 72 |
@staticmethod
|
| 73 |
def planning_prompt() -> str:
|
|
@@ -77,102 +86,84 @@ Structure:
|
|
| 77 |
- Clear learning objectives
|
| 78 |
- Practical applications
|
| 79 |
Return JSON array of:
|
| 80 |
-
{
|
| 81 |
"title": "module title",
|
| 82 |
"objectives": ["objective1", "objective2"],
|
| 83 |
"prerequisites": ["prereq1", "prereq2"]
|
| 84 |
-
}
|
| 85 |
|
| 86 |
@staticmethod
|
| 87 |
def content_prompt() -> str:
|
| 88 |
return """Create engaging content for {module_title}:
|
| 89 |
-
- Start with
|
| 90 |
- Include code examples
|
| 91 |
- Real-world applications
|
| 92 |
- Best practices
|
| 93 |
Return JSON:
|
| 94 |
-
{
|
| 95 |
"content": "main content",
|
| 96 |
"key_points": ["point1", "point2"],
|
| 97 |
-
"details": {
|
| 98 |
"section1": "detailed content",
|
| 99 |
"section2": "detailed content"
|
| 100 |
-
}
|
| 101 |
-
}
|
| 102 |
|
| 103 |
@staticmethod
|
| 104 |
def assessment_prompt() -> str:
|
| 105 |
-
return """Create
|
| 106 |
Include:
|
| 107 |
- Practical scenarios
|
| 108 |
- Code challenges
|
| 109 |
- Concept validation
|
| 110 |
-
Return
|
| 111 |
-
{
|
| 112 |
"question": "text",
|
| 113 |
"options": ["A", "B", "C", "D"],
|
| 114 |
"correct_answer": "A",
|
| 115 |
"explanation": "detailed explanation"
|
| 116 |
-
}
|
| 117 |
-
|
| 118 |
|
| 119 |
class CourseBuilder:
|
| 120 |
-
def __init__(self, api_key=None, config: Config = None):
|
| 121 |
-
|
| 122 |
-
|
|
|
|
|
|
|
| 123 |
else:
|
| 124 |
-
self.api_key
|
| 125 |
-
if not self.api_key:
|
| 126 |
-
print("[DEBUG] API key is not set. Please ensure OPENAI_API_KEY is available in the environment.")
|
| 127 |
-
else:
|
| 128 |
-
print(f"[DEBUG] Using API key: {self.api_key[:4]}... (truncated for security)")
|
| 129 |
self.config = config or Config()
|
| 130 |
-
self.llm =
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
#
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
workflow.add_node("generate_content", self.generate_content)
|
| 146 |
-
workflow.add_node("create_assessment", self.create_assessment)
|
| 147 |
-
|
| 148 |
-
workflow.add_edge("plan_course", "create_module")
|
| 149 |
-
workflow.add_conditional_edge("create_module", "generate_content", self.has_modules) # Conditional edge
|
| 150 |
-
workflow.add_edge("generate_content", "create_assessment")
|
| 151 |
-
workflow.add_conditional_edge("create_assessment", "create_module", self.has_modules) # Loop back
|
| 152 |
-
workflow.add_edge("create_assessment", END) # Final end
|
| 153 |
-
|
| 154 |
-
self.graph = workflow.compile()
|
| 155 |
-
|
| 156 |
-
def has_modules(self, state: CourseState) -> Literal["generate_content", "create_module", "__end__"]:
|
| 157 |
-
if state.modules:
|
| 158 |
-
if state.current_module:
|
| 159 |
-
return "create_module" # Go to next module
|
| 160 |
-
else:
|
| 161 |
-
return "generate_content" # Start first module
|
| 162 |
-
else:
|
| 163 |
-
return END # No more modules
|
| 164 |
|
| 165 |
async def generate_with_retry(self, prompt: str, variables: Dict) -> Any:
|
|
|
|
| 166 |
print(f"[DEBUG] Generating text with prompt: {prompt.format(**variables)}")
|
| 167 |
for attempt in range(self.config.graph_config["max_retries"]):
|
| 168 |
try:
|
| 169 |
-
response = await
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
)
|
| 172 |
-
result = response
|
| 173 |
return json.loads(result)
|
| 174 |
-
except json.JSONDecodeError as e:
|
| 175 |
-
print(f"[DEBUG] Invalid JSON received, retrying. Error: {e}")
|
| 176 |
except Exception as e:
|
| 177 |
print(f"[DEBUG] Error occurred during attempt {attempt + 1}: {e}")
|
| 178 |
if attempt == self.config.graph_config["max_retries"] - 1:
|
|
@@ -222,7 +213,6 @@ class CourseBuilder:
|
|
| 222 |
state.error = str(e)
|
| 223 |
return state
|
| 224 |
|
| 225 |
-
|
| 226 |
class LearningPlatform:
|
| 227 |
def __init__(self, api_key: str = None, config: Config = None):
|
| 228 |
self.config = config or Config()
|
|
@@ -230,32 +220,17 @@ class LearningPlatform:
|
|
| 230 |
|
| 231 |
async def create_course(self, topic: str, difficulty: str) -> LearningPath:
|
| 232 |
try:
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
title=section["title"],
|
| 245 |
-
content=section["content"]["content"],
|
| 246 |
-
sections=[
|
| 247 |
-
Section(
|
| 248 |
-
title=kp,
|
| 249 |
-
content=section["content"].get("details", {}).get(kp, ""),
|
| 250 |
-
quiz_questions=section["assessment"]
|
| 251 |
-
) for kp in section["content"]["key_points"]
|
| 252 |
-
],
|
| 253 |
-
prerequisites=section.get("prerequisites", []),
|
| 254 |
-
learning_objectives=section.get("objectives", []),
|
| 255 |
-
is_complete=True
|
| 256 |
-
) for section in result.sections
|
| 257 |
-
]
|
| 258 |
-
|
| 259 |
return LearningPath(
|
| 260 |
topic=topic,
|
| 261 |
description=f"Course on {topic}",
|
|
@@ -265,5 +240,11 @@ class LearningPlatform:
|
|
| 265 |
is_generating=False
|
| 266 |
)
|
| 267 |
except Exception as e:
|
| 268 |
-
|
| 269 |
-
raise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List, Dict, Any
|
|
|
|
| 2 |
from dataclasses import dataclass, field
|
| 3 |
from datetime import datetime
|
| 4 |
+
import os
|
| 5 |
+
import json
|
| 6 |
+
import streamlit as st
|
| 7 |
+
import openai
|
| 8 |
+
import faiss
|
| 9 |
+
from langchain import OpenAI
|
| 10 |
+
from langgraph import Graph
|
| 11 |
+
|
| 12 |
|
| 13 |
+
from langchain.prompts import PromptTemplate
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
+
from langgraph.prebuilt import create_react_agent, ToolNode
|
| 16 |
+
from langgraph.graph import END
|
| 17 |
+
import asyncio
|
| 18 |
|
| 19 |
@dataclass
|
| 20 |
class Section:
|
|
|
|
| 24 |
quiz_questions: List[Dict[str, Any]] = field(default_factory=list)
|
| 25 |
is_complete: bool = False
|
| 26 |
|
|
|
|
| 27 |
@dataclass
|
| 28 |
class LearningModule:
|
| 29 |
title: str
|
|
|
|
| 34 |
learning_objectives: List[str] = field(default_factory=list)
|
| 35 |
is_complete: bool = False
|
| 36 |
|
|
|
|
| 37 |
@dataclass
|
| 38 |
class LearningPath:
|
| 39 |
topic: str
|
|
|
|
| 43 |
difficulty_level: str
|
| 44 |
is_generating: bool = True
|
| 45 |
|
|
|
|
| 46 |
@dataclass
|
| 47 |
class CourseState:
|
| 48 |
topic: str
|
|
|
|
| 52 |
sections: List[Dict] = field(default_factory=list)
|
| 53 |
error: str = ""
|
| 54 |
|
|
|
|
| 55 |
class Config:
|
| 56 |
def __init__(self):
|
| 57 |
self.llm_config = {
|
| 58 |
"temperature": 0.7,
|
| 59 |
+
"model": "gpt-4",
|
| 60 |
"max_tokens": 2000
|
| 61 |
}
|
| 62 |
+
|
| 63 |
self.graph_config = {
|
| 64 |
"recursion_limit": 25,
|
| 65 |
"max_retries": 3,
|
| 66 |
"timeout": 300
|
| 67 |
}
|
| 68 |
+
self.rag_config = {
|
| 69 |
+
"chunk_size": 500,
|
| 70 |
+
"chunk_overlap": 50,
|
| 71 |
+
"distance_metric": "cosine",
|
| 72 |
+
"k_similar": 3
|
| 73 |
+
}
|
| 74 |
self.course_defaults = {
|
| 75 |
+
"max_modules": 5,
|
| 76 |
+
"sections_per_module": 4,
|
| 77 |
+
"questions_per_quiz": 3
|
| 78 |
}
|
| 79 |
|
|
|
|
| 80 |
class CoursePrompts:
|
| 81 |
@staticmethod
|
| 82 |
def planning_prompt() -> str:
|
|
|
|
| 86 |
- Clear learning objectives
|
| 87 |
- Practical applications
|
| 88 |
Return JSON array of:
|
| 89 |
+
{
|
| 90 |
"title": "module title",
|
| 91 |
"objectives": ["objective1", "objective2"],
|
| 92 |
"prerequisites": ["prereq1", "prereq2"]
|
| 93 |
+
}"""
|
| 94 |
|
| 95 |
@staticmethod
|
| 96 |
def content_prompt() -> str:
|
| 97 |
return """Create engaging content for {module_title}:
|
| 98 |
+
- Start with hook
|
| 99 |
- Include code examples
|
| 100 |
- Real-world applications
|
| 101 |
- Best practices
|
| 102 |
Return JSON:
|
| 103 |
+
{
|
| 104 |
"content": "main content",
|
| 105 |
"key_points": ["point1", "point2"],
|
| 106 |
+
"details": {
|
| 107 |
"section1": "detailed content",
|
| 108 |
"section2": "detailed content"
|
| 109 |
+
}
|
| 110 |
+
}"""
|
| 111 |
|
| 112 |
@staticmethod
|
| 113 |
def assessment_prompt() -> str:
|
| 114 |
+
return """Create assessment for {module_title}.
|
| 115 |
Include:
|
| 116 |
- Practical scenarios
|
| 117 |
- Code challenges
|
| 118 |
- Concept validation
|
| 119 |
+
Return JSON array of:
|
| 120 |
+
{
|
| 121 |
"question": "text",
|
| 122 |
"options": ["A", "B", "C", "D"],
|
| 123 |
"correct_answer": "A",
|
| 124 |
"explanation": "detailed explanation"
|
| 125 |
+
}"""
|
|
|
|
| 126 |
|
| 127 |
class CourseBuilder:
|
| 128 |
+
def __init__(self, api_key: str = None, config: Config = None):
|
| 129 |
+
# Debugging API Key
|
| 130 |
+
self.api_key = api_key or os.getenv('OPENAI_API_KEY')
|
| 131 |
+
if not self.api_key:
|
| 132 |
+
print("[DEBUG] API key is not set. Please ensure OPENAI_API_KEY is available in the environment.")
|
| 133 |
else:
|
| 134 |
+
print(f"[DEBUG] Using API key: {self.api_key[:4]}... (truncated for security)")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
self.config = config or Config()
|
| 136 |
+
self.llm = openai.Completion
|
| 137 |
+
# Debugging OpenAI Client Initialization
|
| 138 |
+
print(f"[DEBUG] Initializing ChatOpenAI with api_key: {self.api_key[:4]}... (truncated for security)")
|
| 139 |
+
self.embeddings = OpenAI.Embeddings(api_key=self.api_key) # Example OpenAI Embeddings usage
|
| 140 |
+
# Debugging Embeddings Initialization
|
| 141 |
+
print(f"[DEBUG] Initializing OpenAIEmbeddings with api_key: {self.api_key[:4]}... (truncated for security)")
|
| 142 |
+
self.vector_store = faiss.IndexFlatL2(128) # Example FAISS initialization for vector store
|
| 143 |
+
self.setup_agent()
|
| 144 |
+
|
| 145 |
+
def setup_agent(self):
|
| 146 |
+
# Define the tools to be used by the agent
|
| 147 |
+
tools = [self.plan_course, self.create_module, self.generate_content, self.create_assessment]
|
| 148 |
+
|
| 149 |
+
# Create a react agent with the specified tools
|
| 150 |
+
self.graph = create_react_agent(self.llm, tools=tools)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
|
| 152 |
async def generate_with_retry(self, prompt: str, variables: Dict) -> Any:
|
| 153 |
+
# Debugging prompt generation
|
| 154 |
print(f"[DEBUG] Generating text with prompt: {prompt.format(**variables)}")
|
| 155 |
for attempt in range(self.config.graph_config["max_retries"]):
|
| 156 |
try:
|
| 157 |
+
response = await asyncio.to_thread(
|
| 158 |
+
openai.Completion.create,
|
| 159 |
+
model=self.config.llm_config['model'],
|
| 160 |
+
prompt=prompt.format(**variables),
|
| 161 |
+
max_tokens=self.config.llm_config['max_tokens'],
|
| 162 |
+
temperature=self.config.llm_config['temperature'],
|
| 163 |
+
api_key=self.api_key
|
| 164 |
)
|
| 165 |
+
result = response['choices'][0]['text']
|
| 166 |
return json.loads(result)
|
|
|
|
|
|
|
| 167 |
except Exception as e:
|
| 168 |
print(f"[DEBUG] Error occurred during attempt {attempt + 1}: {e}")
|
| 169 |
if attempt == self.config.graph_config["max_retries"] - 1:
|
|
|
|
| 213 |
state.error = str(e)
|
| 214 |
return state
|
| 215 |
|
|
|
|
| 216 |
class LearningPlatform:
|
| 217 |
def __init__(self, api_key: str = None, config: Config = None):
|
| 218 |
self.config = config or Config()
|
|
|
|
| 220 |
|
| 221 |
async def create_course(self, topic: str, difficulty: str) -> LearningPath:
|
| 222 |
try:
|
| 223 |
+
inputs = {"messages": ["user", f"Create a course on {topic} at {difficulty} level"]}
|
| 224 |
+
async for s in self.course_builder.graph.stream(inputs, stream_mode="values"):
|
| 225 |
+
message = s["messages"][-1]
|
| 226 |
+
if isinstance(message, tuple):
|
| 227 |
+
print(message)
|
| 228 |
+
else:
|
| 229 |
+
message.pretty_print()
|
| 230 |
+
|
| 231 |
+
# Use the response to create LearningPath instance
|
| 232 |
+
# Placeholder logic - modify this to fit your needs
|
| 233 |
+
modules = [LearningModule(title="Sample Module", content="Generated content...")]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
return LearningPath(
|
| 235 |
topic=topic,
|
| 236 |
description=f"Course on {topic}",
|
|
|
|
| 240 |
is_generating=False
|
| 241 |
)
|
| 242 |
except Exception as e:
|
| 243 |
+
st.error(f"Course creation error: {str(e)}")
|
| 244 |
+
raise,
|
| 245 |
+
difficulty_level=difficulty,
|
| 246 |
+
is_generating=False
|
| 247 |
+
)
|
| 248 |
+
except Exception as e:
|
| 249 |
+
st.error(f"Course creation error: {str(e)}")
|
| 250 |
+
raise
|