Spaces:
Sleeping
Sleeping
| """ | |
| API Layer - FastAPI Application | |
| This module implements the FastAPI application for the MCP EdTech project, | |
| providing RESTful endpoints for interacting with the MCP system. | |
| """ | |
| import os | |
| from typing import Dict, List, Optional, Any, Union | |
| from datetime import datetime, timedelta | |
| from fastapi import FastAPI, Depends, HTTPException, status, BackgroundTasks | |
| from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse, FileResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel | |
| from ..mcp_core.context import Context, ContextManager | |
| from ..mcp_core.model_adapters import ModelRegistry, MockModelAdapter | |
| from ..mcp_core.processor import InteractionProcessor | |
| from ..mcp_core.protocol import ( | |
| MCPRequest, MCPResponse, InteractionType, ContentFormat, | |
| EducationalLevel, LearningObjective, StudentProfile | |
| ) | |
| from ..edtech_extensions.student_profile import StudentProfileManager | |
| from ..edtech_extensions.progress_tracking import ProgressTracker, AssessmentType | |
| from ..edtech_extensions.content_adaptation import ContentAdapter | |
| # Initialize FastAPI app | |
| app = FastAPI( | |
| title="MCP EdTech API", | |
| description="Model Context Protocol implementation for EdTech applications", | |
| version="1.0.0" | |
| ) | |
| # Add CORS middleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # In production, specify actual origins | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Initialize core components | |
| context_manager = ContextManager() | |
| model_registry = ModelRegistry() | |
| # Register mock model adapter for demonstration | |
| mock_adapter = MockModelAdapter(model_id="mock-edtech-model") | |
| model_registry.register_adapter("mock", mock_adapter) | |
| # Initialize processor | |
| interaction_processor = InteractionProcessor(context_manager, model_registry) | |
| # Initialize EdTech extensions | |
| os.makedirs("./storage", exist_ok=True) | |
| student_profile_manager = StudentProfileManager() | |
| progress_tracker = ProgressTracker() | |
| content_adapter = ContentAdapter() | |
| # API Models | |
| class ContextCreate(BaseModel): | |
| """Request model for creating a new context.""" | |
| metadata: Dict[str, Any] = {} | |
| class ContextUpdate(BaseModel): | |
| """Request model for updating a context.""" | |
| metadata: Dict[str, Any] = {} | |
| state: Dict[str, Any] = {} | |
| class InteractionRequest(BaseModel): | |
| """Request model for processing an interaction.""" | |
| context_id: Optional[str] = None | |
| interaction_type: InteractionType | |
| content: Dict[str, Any] | |
| format: ContentFormat = ContentFormat.TEXT | |
| metadata: Dict[str, Any] = {} | |
| model_name: str = "mock" # Default to mock model | |
| class StudentProfileCreate(BaseModel): | |
| """Request model for creating a student profile.""" | |
| name: str | |
| educational_level: EducationalLevel | |
| learning_style: Optional[str] = None | |
| interests: List[str] = [] | |
| strengths: List[str] = [] | |
| areas_for_improvement: List[str] = [] | |
| class StudentProfileUpdate(BaseModel): | |
| """Request model for updating a student profile.""" | |
| name: Optional[str] = None | |
| educational_level: Optional[EducationalLevel] = None | |
| learning_style: Optional[str] = None | |
| interests: Optional[List[str]] = None | |
| strengths: Optional[List[str]] = None | |
| areas_for_improvement: Optional[List[str]] = None | |
| class AssessmentCreate(BaseModel): | |
| """Request model for creating an assessment.""" | |
| student_id: str | |
| assessment_type: AssessmentType | |
| title: str | |
| score: float | |
| max_score: float | |
| objectives: List[str] | |
| feedback: str = "" | |
| details: Dict[str, Any] = {} | |
| class ContentAdaptRequest(BaseModel): | |
| """Request model for adapting content.""" | |
| content: Dict[str, Any] | |
| student_id: str | |
| learning_objectives: List[str] | |
| content_format: ContentFormat = ContentFormat.TEXT | |
| # API Routes | |
| async def root(): | |
| """Root endpoint returning API information.""" | |
| return { | |
| "name": "MCP EdTech API", | |
| "version": "1.0.0", | |
| "description": "Model Context Protocol implementation for EdTech applications" | |
| } | |
| # Context endpoints | |
| async def create_context(request: ContextCreate): | |
| """Create a new context.""" | |
| context = context_manager.create_context(metadata=request.metadata) | |
| return context.to_dict() | |
| async def get_context(context_id: str): | |
| """Get context information.""" | |
| context = context_manager.get_context(context_id) | |
| if not context: | |
| raise HTTPException(status_code=404, detail="Context not found") | |
| return context.to_dict() | |
| async def update_context(context_id: str, request: ContextUpdate): | |
| """Update context.""" | |
| context = context_manager.get_context(context_id) | |
| if not context: | |
| raise HTTPException(status_code=404, detail="Context not found") | |
| if request.metadata: | |
| context.metadata.update(request.metadata) | |
| if request.state: | |
| context.update_state(request.state) | |
| return context.to_dict() | |
| async def delete_context(context_id: str): | |
| """Delete context.""" | |
| success = context_manager.delete_context(context_id) | |
| if not success: | |
| raise HTTPException(status_code=404, detail="Context not found") | |
| return {"status": "deleted", "context_id": context_id} | |
| # Interaction endpoint | |
| async def process_interaction(request: InteractionRequest): | |
| """Process an interaction.""" | |
| try: | |
| mcp_request = MCPRequest( | |
| context_id=request.context_id, | |
| interaction_type=request.interaction_type, | |
| content=request.content, | |
| format=request.format, | |
| metadata=request.metadata | |
| ) | |
| response = await interaction_processor.process_interaction( | |
| mcp_request, | |
| request.model_name | |
| ) | |
| return response.dict() | |
| except ValueError as e: | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Error processing interaction: {str(e)}") | |
| # Student profile endpoints | |
| async def create_student_profile(request: StudentProfileCreate): | |
| """Create student profile.""" | |
| profile = await student_profile_manager.create_profile( | |
| name=request.name, | |
| educational_level=request.educational_level, | |
| learning_style=request.learning_style, | |
| interests=request.interests, | |
| strengths=request.strengths, | |
| areas_for_improvement=request.areas_for_improvement | |
| ) | |
| return profile.dict() | |
| async def get_student_profile(student_id: str): | |
| """Get student information.""" | |
| profile = await student_profile_manager.get_profile(student_id) | |
| if not profile: | |
| raise HTTPException(status_code=404, detail="Student profile not found") | |
| return profile.dict() | |
| async def update_student_profile(student_id: str, request: StudentProfileUpdate): | |
| """Update student information.""" | |
| updates = {k: v for k, v in request.dict().items() if v is not None} | |
| profile = await student_profile_manager.update_profile(student_id, updates) | |
| if not profile: | |
| raise HTTPException(status_code=404, detail="Student profile not found") | |
| return profile.dict() | |
| # Progress tracking endpoints | |
| async def get_student_progress(student_id: str): | |
| """Get learning progress.""" | |
| # Check if student exists | |
| profile = await student_profile_manager.get_profile(student_id) | |
| if not profile: | |
| raise HTTPException(status_code=404, detail="Student profile not found") | |
| summary = await progress_tracker.get_progress_summary(student_id) | |
| return summary | |
| async def create_assessment(request: AssessmentCreate): | |
| """Create assessment.""" | |
| # Check if student exists | |
| profile = await student_profile_manager.get_profile(request.student_id) | |
| if not profile: | |
| raise HTTPException(status_code=404, detail="Student profile not found") | |
| result = await progress_tracker.record_assessment( | |
| student_id=request.student_id, | |
| assessment_type=request.assessment_type, | |
| title=request.title, | |
| score=request.score, | |
| max_score=request.max_score, | |
| objectives=request.objectives, | |
| feedback=request.feedback, | |
| details=request.details | |
| ) | |
| # Update student profile with completed objectives if score is good | |
| if request.score / request.max_score >= 0.7: # 70% or better | |
| for objective_id in request.objectives: | |
| await student_profile_manager.add_completed_objective( | |
| request.student_id, | |
| objective_id | |
| ) | |
| return result.dict() | |
| async def list_student_assessments(student_id: str): | |
| """List assessments for a student.""" | |
| # Check if student exists | |
| profile = await student_profile_manager.get_profile(student_id) | |
| if not profile: | |
| raise HTTPException(status_code=404, detail="Student profile not found") | |
| results = await progress_tracker.list_assessments(student_id) | |
| return [result.dict() for result in results] | |
| # Content adaptation endpoint | |
| async def adapt_content(request: ContentAdaptRequest): | |
| """Adapt content for a student.""" | |
| # Get student profile | |
| profile = await student_profile_manager.get_profile(request.student_id) | |
| if not profile: | |
| raise HTTPException(status_code=404, detail="Student profile not found") | |
| # Create learning objectives (in a real app, these would come from a database) | |
| learning_objectives = [ | |
| LearningObjective( | |
| id=obj_id, | |
| description=f"Learning objective {obj_id}", | |
| taxonomy_level="understand", | |
| subject_area="general", | |
| prerequisites=[] | |
| ) | |
| for obj_id in request.learning_objectives | |
| ] | |
| # Adapt content | |
| adapted_content = await content_adapter.adapt_content( | |
| content=request.content, | |
| student_profile=profile, | |
| learning_objectives=learning_objectives, | |
| content_format=request.content_format | |
| ) | |
| return adapted_content | |
| # Learning path endpoint | |
| async def get_learning_path(student_id: str): | |
| """Get personalized learning path.""" | |
| # Check if student exists | |
| profile = await student_profile_manager.get_profile(student_id) | |
| if not profile: | |
| raise HTTPException(status_code=404, detail="Student profile not found") | |
| # In a real implementation, this would generate a personalized learning path | |
| # based on the student's profile, progress, and available learning materials. | |
| # For this demo, we'll return a mock learning path. | |
| # Get student's completed objectives | |
| completed_objectives = profile.completed_objectives | |
| current_objectives = profile.current_objectives | |
| # Mock learning path | |
| return { | |
| "student_id": student_id, | |
| "student_name": profile.name, | |
| "educational_level": profile.educational_level, | |
| "completed_objectives": completed_objectives, | |
| "current_objectives": current_objectives, | |
| "recommended_objectives": [ | |
| { | |
| "id": f"obj-{i}", | |
| "title": f"Recommended Objective {i}", | |
| "description": f"This is a recommended learning objective for {profile.name}", | |
| "estimated_time": f"{i*2} hours", | |
| "difficulty": "intermediate" | |
| } | |
| for i in range(1, 4) | |
| ], | |
| "recommended_resources": [ | |
| { | |
| "id": f"res-{i}", | |
| "title": f"Resource {i}", | |
| "type": "video" if i % 2 == 0 else "article", | |
| "url": f"https://example.com/resource-{i}", | |
| "description": f"This is a recommended resource for {profile.name}" | |
| } | |
| for i in range(1, 6) | |
| ], | |
| "generated_at": datetime.utcnow().isoformat() | |
| } | |
| # Model information endpoints | |
| async def list_models(): | |
| """List available models.""" | |
| return model_registry.list_adapters() | |
| async def get_model_capabilities(model_name: str): | |
| """Get model capabilities.""" | |
| capabilities = model_registry.get_adapter_capabilities(model_name) | |
| if not capabilities: | |
| raise HTTPException(status_code=404, detail="Model not found") | |
| return capabilities | |
| # Health check endpoint | |
| async def health_check(): | |
| """Health check endpoint.""" | |
| return { | |
| "status": "healthy", | |
| "timestamp": datetime.utcnow().isoformat() | |
| } | |
| # Demo UI route | |
| async def demo_ui(): | |
| """Serve the demo UI.""" | |
| return FileResponse("static/index.html") | |
| # Serve static files for demo UI | |
| app.mount("/static", StaticFiles(directory="static"), name="static") | |
| # Main function to run the app | |
| def start(): | |
| """Start the FastAPI application using uvicorn.""" | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=8000) | |
| if __name__ == "__main__": | |
| start() | |