feat: implement AI-driven learning content generation service and interactive student dashboard
This commit is contained in:
@@ -6,7 +6,7 @@ You write training material for employees based on knowledge topics.
|
||||
Always write in clear, professional English.
|
||||
ALWAYS return valid JSON only — no markdown code blocks, no extra text.`;
|
||||
|
||||
const CONTENT_SCHEMA = `{
|
||||
const CONTENT_SCHEMA_ARTICLE = `{
|
||||
"article": {
|
||||
"title": "Article title",
|
||||
"intro": "Short intro of 1-2 sentences",
|
||||
@@ -14,11 +14,20 @@ const CONTENT_SCHEMA = `{
|
||||
{ "heading": "Section title", "body": "Section text of at least 3 sentences." }
|
||||
],
|
||||
"keyTakeaways": ["Takeaway 1", "Takeaway 2", "Takeaway 3"]
|
||||
},
|
||||
}
|
||||
}`;
|
||||
|
||||
const CONTENT_SCHEMA_SLIDES = `{
|
||||
"slides": [
|
||||
{ "title": "Slide title", "bullets": ["Point 1", "Point 2", "Point 3"], "speakerNote": "Speaker note for this slide." }
|
||||
],
|
||||
"podcastScript": "A natural spoken script of approx. 300 words summarizing the topic as a podcast episode.",
|
||||
]
|
||||
}`;
|
||||
|
||||
const CONTENT_SCHEMA_PODCAST = `{
|
||||
"podcastScript": "A natural spoken script of approx. 300 words summarizing the topic as a podcast episode."
|
||||
}`;
|
||||
|
||||
const CONTENT_SCHEMA_INFOGRAPHIC = `{
|
||||
"infographic": {
|
||||
"headline": "A short, punchy headline summarizing the topic (max 8 words)",
|
||||
"tagline": "A subtitle of max 15 words",
|
||||
@@ -33,6 +42,13 @@ const CONTENT_SCHEMA = `{
|
||||
}
|
||||
}`;
|
||||
|
||||
const CONTENT_SCHEMA_ALL = `{
|
||||
"article": ${CONTENT_SCHEMA_ARTICLE.replace(/^\{|\}$/g, '').trim()},
|
||||
"slides": ${CONTENT_SCHEMA_SLIDES.replace(/^\{|\}$/g, '').trim()},
|
||||
"podcastScript": "A natural spoken script of approx. 300 words summarizing the topic as a podcast episode.",
|
||||
"infographic": ${CONTENT_SCHEMA_INFOGRAPHIC.replace(/^\{|\}$/g, '').trim()}
|
||||
}`;
|
||||
|
||||
export async function getAssignedTopic(userId, weekNumber) {
|
||||
const topics = await db.getTopics();
|
||||
if (!topics || topics.length === 0) return null;
|
||||
@@ -62,38 +78,64 @@ export async function getAllGeneratedContent() {
|
||||
return results.filter(item => item.hasContent);
|
||||
}
|
||||
|
||||
export async function generateLearningContent(topic, force = false) {
|
||||
export async function generateLearningContent(topic, force = false, selectedType = 'article') {
|
||||
let cached = null;
|
||||
if (!force) {
|
||||
const cached = await db.getContent(topic.id);
|
||||
cached = await db.getContent(topic.id);
|
||||
if (cached) {
|
||||
console.log(`[Learn] Cache hit for topic: ${topic.id}`);
|
||||
return cached;
|
||||
if (selectedType === 'podcast' && cached.podcastScript) {
|
||||
console.log(`[Learn] Cache hit for topic: ${topic.id} (podcast)`);
|
||||
return cached;
|
||||
} else if (selectedType !== 'podcast' && cached[selectedType]) {
|
||||
console.log(`[Learn] Cache hit for topic: ${topic.id} (${selectedType})`);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const prompt = `Generate a complete learning module for the following topic:
|
||||
let schema = '';
|
||||
let instructions = '';
|
||||
if (selectedType === 'all') {
|
||||
schema = CONTENT_SCHEMA_ALL;
|
||||
instructions = 'Provide at least 3 article sections, 4 slides, 3 stats, and 3-5 steps in the infographic.';
|
||||
} else if (selectedType === 'article') {
|
||||
schema = CONTENT_SCHEMA_ARTICLE;
|
||||
instructions = 'Provide at least 3 article sections.';
|
||||
} else if (selectedType === 'slides') {
|
||||
schema = CONTENT_SCHEMA_SLIDES;
|
||||
instructions = 'Provide at least 4 slides.';
|
||||
} else if (selectedType === 'podcast') {
|
||||
schema = CONTENT_SCHEMA_PODCAST;
|
||||
instructions = 'Provide a natural spoken script.';
|
||||
} else if (selectedType === 'infographic') {
|
||||
schema = CONTENT_SCHEMA_INFOGRAPHIC;
|
||||
instructions = 'Provide at least 3 stats, and 3-5 steps in the infographic.';
|
||||
}
|
||||
|
||||
const prompt = `Generate a learning module piece for the following topic:
|
||||
|
||||
Label: ${topic.label}
|
||||
Type: ${topic.type}
|
||||
Description: ${topic.description}
|
||||
|
||||
Return ONLY a JSON object with the following structure:
|
||||
${CONTENT_SCHEMA}
|
||||
${schema}
|
||||
|
||||
Provide at least 3 article sections, 4 slides, 3 stats, and 3-5 steps in the infographic.`;
|
||||
${instructions}`;
|
||||
|
||||
const responseText = await anthropicApi.generateContent(CONTENT_GENERATION_SYSTEM, prompt);
|
||||
|
||||
let content;
|
||||
let newContent;
|
||||
try {
|
||||
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
|
||||
content = JSON.parse(jsonMatch ? jsonMatch[0] : responseText);
|
||||
newContent = JSON.parse(jsonMatch ? jsonMatch[0] : responseText);
|
||||
} catch (e) {
|
||||
throw new Error('AI could not generate valid learning content. Please try again.');
|
||||
}
|
||||
|
||||
await db.setContent(topic.id, content);
|
||||
return content;
|
||||
const mergedContent = { ...(cached || {}), ...newContent };
|
||||
await db.setContent(topic.id, mergedContent);
|
||||
return mergedContent;
|
||||
}
|
||||
|
||||
export async function refineLearningContent(topic, refinementInstruction) {
|
||||
|
||||
Reference in New Issue
Block a user