education-module-ai
Version:
AI Education Module using Agent Framework
588 lines (587 loc) • 24.1 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EducationalContentController = void 0;
const agent_framework_backend_1 = require("@defikitdotnet/agent-framework-backend");
const uuid_1 = require("uuid");
const EducationalContent_1 = require("../models/EducationalContent");
const plugin_manager_1 = require("../plugins/plugin-manager");
const ai_service_1 = require("../services/ai-service");
// In-memory storage for demo purposes
const contentStore = {};
// Create a resource implementation for EducationalContentResource
const educationalContentResource = {
name: "educational-contents",
findAll: async () => Object.values(contentStore),
findById: async (id) => contentStore[id] || null,
create: async (data) => {
const id = (0, uuid_1.v4)();
const newContent = new EducationalContent_1.EducationalContentResource();
Object.assign(newContent, data, {
id,
createdAt: new Date(),
});
contentStore[id] = newContent;
return newContent;
},
update: async (id, data) => {
if (!contentStore[id])
return null;
const updated = { ...contentStore[id], ...data, updatedAt: new Date() };
contentStore[id] = updated;
return updated;
},
delete: async (id) => {
if (!contentStore[id])
return false;
delete contentStore[id];
return true;
},
findBySubject: async (subject) => {
return Object.values(contentStore).filter((content) => content.subject === subject);
},
findByLevel: async (level) => {
return Object.values(contentStore).filter((content) => content.level === level);
},
search: async (query) => {
const lowerQuery = query.toLowerCase();
return Object.values(contentStore).filter((content) => content.title.toLowerCase().includes(lowerQuery) ||
content.content.toLowerCase().includes(lowerQuery) ||
content.keywords.some((keyword) => keyword.toLowerCase().includes(lowerQuery)));
},
};
/**
* Educational Content API controller
*/
let EducationalContentController = class EducationalContentController extends agent_framework_backend_1.ResourceController {
constructor() {
super(educationalContentResource);
this.educationModuleAdapter = null;
this.configService = EducationalContent_1.EducationConfigService.getInstance();
console.log("EducationalContentController initialized");
}
setEducationModuleAdapter(adapter) {
this.educationModuleAdapter = adapter;
console.log("EducationModuleAdapter set in controller");
}
/**
* Get all educational content items
*/
async findAll(req, res) {
try {
let items;
if (this.educationModuleAdapter) {
// Use database adapter if available
items = await this.educationModuleAdapter.getEducationalContents();
}
else {
// Fall back to in-memory storage
items = await this.resource.findAll();
}
res.status(200).json({ data: items });
}
catch (error) {
console.error("Error retrieving educational content list:", error);
res.status(500).json({ error: "Failed to retrieve educational content list" });
}
}
/**
* Create educational content using database adapter
*/
async create(req, res) {
try {
console.log("Creating educational content with payload:", JSON.stringify(req.body, null, 2));
// Validate subject and level from configuration
const subject = this.validateSubject(req.body.subject);
const level = this.validateEducationalLevel(req.body.level);
console.log("Validation results:", {
subject: subject || "Invalid",
level: level || "Invalid",
validSubjects: this.configService.subjects,
validLevels: this.configService.levels
});
if (!subject) {
res.status(400).json({
error: `Invalid subject. Valid subjects are: ${this.configService.subjects.join(', ')}`
});
return;
}
if (!level) {
res.status(400).json({
error: `Invalid educational level. Valid levels are: ${this.configService.levels.join(', ')}`
});
return;
}
let newContent;
if (this.educationModuleAdapter) {
console.log("Using database adapter to create content");
try {
// Create content using database adapter
const id = (0, uuid_1.v4)();
const now = new Date();
// Ensure we have proper JavaScript Date objects, not serialized dates
const content = {
id,
title: req.body.title,
content: req.body.content,
type: req.body.type,
subject,
level,
keywords: req.body.keywords || [],
createdAt: now,
updatedAt: now
};
console.log("Prepared content for database:", content);
const contentId = await this.educationModuleAdapter.createEducationalContent(content);
console.log("Content created successfully with ID:", contentId);
// Fetch the complete content after creation to return in response
newContent = await this.educationModuleAdapter.getEducationalContent(contentId);
if (!newContent) {
// If content can't be fetched, create a basic response
newContent = {
id: contentId,
title: content.title,
content: content.content,
type: content.type,
subject: content.subject,
level: content.level,
keywords: content.keywords,
createdAt: content.createdAt,
updatedAt: content.updatedAt
};
}
}
catch (adapterError) {
console.error("Error in database adapter:", adapterError);
throw adapterError;
}
}
else {
console.log("Using in-memory storage (adapter not available)");
// Fall back to in-memory storage
const content = {
...req.body,
subject,
level
};
newContent = await this.resource.create(content);
}
// Notify plugins
await plugin_manager_1.pluginManager.notifyContentCreated(newContent);
res.status(201).json({ data: newContent });
}
catch (error) {
console.error("Error creating educational content:", error);
res.status(500).json({ error: "Failed to create educational content", details: String(error) });
}
}
/**
* Update educational content using database adapter
*/
async update(req, res) {
try {
// Validate subject and level from configuration if provided in the update
let subject = req.body.subject;
let level = req.body.level;
if (subject) {
subject = this.validateSubject(subject);
if (!subject) {
res.status(400).json({
error: `Invalid subject. Valid subjects are: ${this.configService.subjects.join(', ')}`
});
return;
}
}
if (level) {
level = this.validateEducationalLevel(level);
if (!level) {
res.status(400).json({
error: `Invalid educational level. Valid levels are: ${this.configService.levels.join(', ')}`
});
return;
}
}
let updatedContent;
if (this.educationModuleAdapter) {
// Update content using database adapter
const content = {
id: req.params.id,
title: req.body.title,
content: req.body.content,
type: req.body.type,
subject: subject || req.body.subject,
level: level || req.body.level,
keywords: req.body.keywords,
updatedAt: new Date().toISOString()
};
updatedContent = await this.educationModuleAdapter.updateEducationalContent(content);
}
else {
// Fall back to in-memory storage
const updateData = { ...req.body };
if (subject)
updateData.subject = subject;
if (level)
updateData.level = level;
updatedContent = await this.resource.update(req.params.id, updateData);
}
// Notify plugins
await plugin_manager_1.pluginManager.notifyContentUpdated(updatedContent);
res.status(200).json({ data: updatedContent });
}
catch (error) {
console.error("Error updating educational content:", error);
res.status(500).json({ error: "Failed to update educational content" });
}
}
/**
* Delete educational content using database adapter
*/
async delete(req, res) {
try {
let deleted;
if (this.educationModuleAdapter) {
// Delete content using database adapter
deleted = await this.educationModuleAdapter.deleteEducationalContent(req.params.id);
}
else {
// Fall back to in-memory storage
deleted = await this.resource.delete(req.params.id);
}
// Notify plugins
await plugin_manager_1.pluginManager.notifyContentDeleted(deleted);
res.status(200).json({ data: deleted });
}
catch (error) {
console.error("Error deleting educational content:", error);
res.status(500).json({ error: "Failed to delete educational content" });
}
}
/**
* Get educational content by ID using database adapter
*/
async getById(req, res) {
try {
let content;
if (this.educationModuleAdapter) {
// Get content using database adapter
content = await this.educationModuleAdapter.getEducationalContent(req.params.id);
}
else {
// Fall back to in-memory storage
content = await this.resource.findById(req.params.id);
}
res.status(200).json({ data: content });
}
catch (error) {
console.error("Error retrieving educational content by ID:", error);
res.status(500).json({ error: "Failed to retrieve educational content by ID" });
}
}
/**
* Generate educational content
*/
async generateContent(req, res) {
const { subject, level, type, topic } = req.body;
if (!subject || !level || !type || !topic) {
res.status(400).json({ error: "Missing required fields" });
return;
}
try {
// Validate parameters
if (!this.isValidSubject(subject)) {
res.status(400).json({ error: "Invalid subject" });
return;
}
if (!this.isValidLevel(level)) {
res.status(400).json({ error: "Invalid educational level" });
return;
}
if (!this.isValidContentType(type)) {
res.status(400).json({ error: "Invalid content type" });
return;
}
// Generate content using AI service
const generatedContent = await ai_service_1.aiService.generateEducationalContent(subject, level, type, topic);
// Create and save the generated content
const newContent = await this.resource.create({
title: generatedContent.title,
content: generatedContent.content,
type: type,
subject: subject,
level: level,
keywords: generatedContent.keywords,
});
// Notify plugins
await plugin_manager_1.pluginManager.notifyContentCreated(newContent);
res.status(201).json({ data: newContent });
}
catch (error) {
console.error("Error generating educational content:", error);
res.status(500).json({ error: "Failed to generate educational content" });
}
}
/**
* Generate quiz
*/
async generateQuiz(req, res) {
console.log("Quiz endpoint called with data:", req.body);
const { subject, level, topic, questionCount } = req.body;
if (!subject || !level || !topic) {
res.status(400).json({ error: "Subject, level, and topic are required" });
return;
}
try {
const validSubject = this.validateSubject(subject);
const validLevel = this.validateEducationalLevel(level);
if (!validSubject) {
res.status(400).json({ error: `Invalid subject: ${subject}` });
return;
}
if (!validLevel) {
res.status(400).json({ error: `Invalid educational level: ${level}` });
return;
}
console.log(`Generating quiz for topic: "${topic}" in ${subject} at ${level} level with ${questionCount || 5} questions`);
// Generate quiz using AI service
const quiz = await ai_service_1.aiService.generateQuiz(validSubject, validLevel, topic, questionCount || 5);
// Create a content entry for this quiz
const quizContent = await this.resource.create({
title: quiz.title,
content: JSON.stringify(quiz.questions),
subject: validSubject,
level: validLevel,
type: "quiz",
keywords: [topic, "quiz", validSubject, validLevel],
});
// Notify plugins
await plugin_manager_1.pluginManager.notifyContentCreated(quizContent);
res.status(200).json({
data: {
...quiz,
id: quizContent.id
}
});
}
catch (error) {
console.error("Error generating quiz:", error);
res.status(500).json({ error: "Failed to generate quiz" });
}
}
/**
* Question answering endpoint
*/
async answerQuestion(req, res) {
const { subject, level, question } = req.body;
if (!subject || !level || !question) {
res.status(400).json({ error: "Subject, level, and question are required" });
return;
}
try {
const validSubject = this.validateSubject(subject);
const validLevel = this.validateEducationalLevel(level);
if (!validSubject) {
res.status(400).json({ error: `Invalid subject: ${subject}` });
return;
}
if (!validLevel) {
res.status(400).json({ error: `Invalid educational level: ${level}` });
return;
}
console.log(`Answering question about ${subject} at ${level} level: "${question}"`);
// Answer question using AI service
const answer = await ai_service_1.aiService.answerQuestion(validSubject, validLevel, question);
res.status(200).json({ data: answer });
}
catch (error) {
console.error("Error answering question:", error);
res.status(500).json({ error: "Failed to answer question" });
}
}
/**
* Search for educational content
*/
async searchContent(req, res) {
const { q } = req.query;
if (!q || typeof q !== "string") {
res.status(400).json({ error: "Missing search query" });
return;
}
try {
const results = await educationalContentResource.search(q);
res.status(200).json({ data: results });
}
catch (error) {
console.error("Error searching content:", error);
res.status(500).json({ error: "Failed to search content" });
}
}
/**
* Get content by subject
*/
async getBySubject(req, res) {
const { subject } = req.params;
if (!subject || !this.isValidSubject(subject)) {
res.status(400).json({ error: "Invalid subject" });
return;
}
try {
const results = await educationalContentResource.findBySubject(subject);
res.status(200).json({ data: results });
}
catch (error) {
console.error("Error retrieving content by subject:", error);
res.status(500).json({ error: "Failed to retrieve content" });
}
}
/**
* Get content by educational level
*/
async getByLevel(req, res) {
const { level } = req.params;
if (!level || !this.isValidLevel(level)) {
res.status(400).json({ error: "Invalid educational level" });
return;
}
try {
const results = await educationalContentResource.findByLevel(level);
res.status(200).json({ data: results });
}
catch (error) {
console.error("Error retrieving content by level:", error);
res.status(500).json({ error: "Failed to retrieve content" });
}
}
/**
* Get available configuration options
*/
async getConfig(req, res) {
try {
res.status(200).json({
data: {
levels: this.configService.levels,
subjects: this.configService.subjects
}
});
}
catch (error) {
console.error("Error retrieving configuration:", error);
res.status(500).json({ error: "Failed to retrieve configuration" });
}
}
/**
* Check if a subject is valid according to configuration
*/
isValidSubject(subject) {
return this.configService.isValidSubject(subject);
}
/**
* Check if an educational level is valid according to configuration
*/
isValidLevel(level) {
return this.configService.isValidLevel(level);
}
/**
* Check if content type is valid
*/
isValidContentType(type) {
return ["explanation", "tutorial", "practice_problem", "quiz", "reference_material"].includes(type);
}
/**
* Validate a subject value
*/
validateSubject(subject) {
return this.isValidSubject(subject) ? subject : null;
}
/**
* Validate an educational level value
*/
validateEducationalLevel(level) {
return this.isValidLevel(level) ? level : null;
}
};
exports.EducationalContentController = EducationalContentController;
__decorate([
(0, agent_framework_backend_1.Get)("/"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "findAll", null);
__decorate([
(0, agent_framework_backend_1.Post)("/"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "create", null);
__decorate([
(0, agent_framework_backend_1.Put)("/:id"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "update", null);
__decorate([
(0, agent_framework_backend_1.Delete)("/:id"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "delete", null);
__decorate([
(0, agent_framework_backend_1.Get)("/:id"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "getById", null);
__decorate([
(0, agent_framework_backend_1.Post)("/generate"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "generateContent", null);
__decorate([
(0, agent_framework_backend_1.Post)("/quiz"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "generateQuiz", null);
__decorate([
(0, agent_framework_backend_1.Post)("/answer-question"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "answerQuestion", null);
__decorate([
(0, agent_framework_backend_1.Get)("/search"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "searchContent", null);
__decorate([
(0, agent_framework_backend_1.Get)("/subject/:subject"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "getBySubject", null);
__decorate([
(0, agent_framework_backend_1.Get)("/level/:level"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "getByLevel", null);
__decorate([
(0, agent_framework_backend_1.Get)("/config"),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], EducationalContentController.prototype, "getConfig", null);
exports.EducationalContentController = EducationalContentController = __decorate([
(0, agent_framework_backend_1.Controller)("/api/educational-contents"),
__metadata("design:paramtypes", [])
], EducationalContentController);