@thanh01.pmt/curriculum-kit 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/index.cjs +49 -0
- package/dist/ai/index.cjs.map +1 -1
- package/dist/ai/index.d.cts +11 -1
- package/dist/ai/index.d.ts +11 -1
- package/dist/ai/index.mjs +49 -1
- package/dist/ai/index.mjs.map +1 -1
- package/dist/{gateSettings-Buk_MSML.d.cts → gateSettings-L3FR2-MO.d.cts} +1 -0
- package/dist/{gateSettings-Buk_MSML.d.ts → gateSettings-L3FR2-MO.d.ts} +1 -0
- package/dist/index.cjs +295 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -4
- package/dist/index.d.ts +21 -4
- package/dist/index.mjs +295 -44
- package/dist/index.mjs.map +1 -1
- package/dist/pipeline/index.cjs.map +1 -1
- package/dist/pipeline/index.mjs.map +1 -1
- package/dist/workflow/index.cjs +39 -4
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.d.cts +2 -2
- package/dist/workflow/index.d.ts +2 -2
- package/dist/workflow/index.mjs +39 -4
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -13,6 +13,7 @@ interface SingleArtifactRequest {
|
|
|
13
13
|
studentCount?: string;
|
|
14
14
|
language?: string;
|
|
15
15
|
sectionLanguageContract?: Record<string, Record<string, string>> | string;
|
|
16
|
+
slidesEngine?: 'html' | 'marp';
|
|
16
17
|
failClosedContext?: boolean;
|
|
17
18
|
extraContext?: string;
|
|
18
19
|
config?: Record<string, any>;
|
|
@@ -13,6 +13,7 @@ interface SingleArtifactRequest {
|
|
|
13
13
|
studentCount?: string;
|
|
14
14
|
language?: string;
|
|
15
15
|
sectionLanguageContract?: Record<string, Record<string, string>> | string;
|
|
16
|
+
slidesEngine?: 'html' | 'marp';
|
|
16
17
|
failClosedContext?: boolean;
|
|
17
18
|
extraContext?: string;
|
|
18
19
|
config?: Record<string, any>;
|
package/dist/index.cjs
CHANGED
|
@@ -5432,6 +5432,54 @@ ${input.languageDirective}
|
|
|
5432
5432
|
|
|
5433
5433
|
${input.headingDirective}`.trim();
|
|
5434
5434
|
}
|
|
5435
|
+
function buildHtmlDeckSlidePrompt(input) {
|
|
5436
|
+
const contract = input.customContract || `## HTML SLIDE ENGINE \u2014 AGENT GENERATION CONTRACT
|
|
5437
|
+
You generate structured slide decks for the LearnWell HTML Slide Presentation Engine.
|
|
5438
|
+
Your output MUST be a single, valid JSON object matching the Slide Deck JSON schema below.
|
|
5439
|
+
|
|
5440
|
+
### \u{1F3A8} Available Layout Presets (Use layoutId):
|
|
5441
|
+
1. **hero-cover**: Title/Cover slide with title, subtitle, presenter, date, tag.
|
|
5442
|
+
2. **two-column-split**: Side-by-side comparison (col1Title, col1Items[], col2Title, col2Items[]).
|
|
5443
|
+
3. **code-explainer**: Code walkthrough with filename, language, code, highlightLines, annotations[].
|
|
5444
|
+
4. **metrics-3-card**: 3 key stat cards (card1Title, card1Val, card1Desc, card2..., card3...).
|
|
5445
|
+
5. **process-flow**: Step-by-step pipeline (steps: [{ label, desc, status }]).
|
|
5446
|
+
6. **quiz-checkpoint**: Quick multiple choice question (question, options[], answerIdx, explanation).
|
|
5447
|
+
7. **tiered-practice-3cards**: Differentiated tiers (foundation, standard, challenge cards).
|
|
5448
|
+
8. **takeaways-summary**: Summary review with bullet takeaways.
|
|
5449
|
+
|
|
5450
|
+
### \u{1F399}\uFE0F Mandatory 3-Part Presenter Notes on EVERY slide:
|
|
5451
|
+
Every slide MUST include notes with:
|
|
5452
|
+
- Talk Track: 2-3 sentences speaking script
|
|
5453
|
+
- Cold-Call: 1 check question
|
|
5454
|
+
- Scaffolding Tip: 1 analogy or hint
|
|
5455
|
+
|
|
5456
|
+
### Output JSON Format:
|
|
5457
|
+
\`\`\`json
|
|
5458
|
+
{
|
|
5459
|
+
"title": "${input.lessonTitle}",
|
|
5460
|
+
"theme": "ocean",
|
|
5461
|
+
"slides": [
|
|
5462
|
+
{
|
|
5463
|
+
"layoutId": "hero-cover",
|
|
5464
|
+
"slots": { "title": "${input.lessonTitle}", "subtitle": "..." },
|
|
5465
|
+
"notes": "Talk Track: ...\\nCold-Call: ...\\nScaffolding Tip: ..."
|
|
5466
|
+
}
|
|
5467
|
+
]
|
|
5468
|
+
}
|
|
5469
|
+
\`\`\``;
|
|
5470
|
+
return `You are @illustrator & @content (Interactive HTML Slide Deck Presentation Architects).
|
|
5471
|
+
Based on Master Lesson Plan \`LESSON_${input.lessonCode}.md\`, pedagogical model (${input.pedagogyLabel}), and the HTML SLIDE ENGINE CONTRACT below, author the complete, highly engaging HTML presentation deck in JSON format: \`SLIDE_${input.lessonCode}\`.
|
|
5472
|
+
|
|
5473
|
+
${contract}
|
|
5474
|
+
|
|
5475
|
+
${input.languageDirective}
|
|
5476
|
+
|
|
5477
|
+
${input.headingDirective}${input.glossaryBlock ? `
|
|
5478
|
+
|
|
5479
|
+
${input.glossaryBlock}` : ""}
|
|
5480
|
+
|
|
5481
|
+
Output ONLY the raw JSON object. Do not include extra conversational text or markdown code fences.`.trim();
|
|
5482
|
+
}
|
|
5435
5483
|
|
|
5436
5484
|
// src/ai/prompts/teacherGuidePrompt.ts
|
|
5437
5485
|
function buildTeacherGuidePrompt(input) {
|
|
@@ -9794,6 +9842,7 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
|
|
|
9794
9842
|
}
|
|
9795
9843
|
const slcMap = normalizeSlcContract(opts.sectionLanguageContract);
|
|
9796
9844
|
const sections = parseMarkdownSections(source, slcMap, opts.artifactType);
|
|
9845
|
+
const metaBlock = buildMetaBlock(source);
|
|
9797
9846
|
if (sections.length === 0) {
|
|
9798
9847
|
issues.push("parse:no-sections");
|
|
9799
9848
|
const excerpt2 = truncateByBudget(source, budget);
|
|
@@ -9827,12 +9876,13 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
|
|
|
9827
9876
|
const block = `## ${heading}
|
|
9828
9877
|
|
|
9829
9878
|
${trimmedBody}`;
|
|
9830
|
-
if (used + block.length > budget &&
|
|
9879
|
+
if (used + block.length > budget && used > 0) return false;
|
|
9831
9880
|
blocks.push(block);
|
|
9832
9881
|
used += block.length;
|
|
9833
9882
|
includedSections.push(key);
|
|
9834
9883
|
return true;
|
|
9835
9884
|
};
|
|
9885
|
+
blocks.push(metaBlock);
|
|
9836
9886
|
let sectionAware = false;
|
|
9837
9887
|
for (const key of priorities) {
|
|
9838
9888
|
const sec = byCanonical.get(key);
|
|
@@ -9849,6 +9899,9 @@ ${trimmedBody}`;
|
|
|
9849
9899
|
if (used >= budget) break;
|
|
9850
9900
|
if (!tryAdd(sec.cleanTitle, sec.body, sec.cleanTitle)) break;
|
|
9851
9901
|
}
|
|
9902
|
+
if (includedSections.length === 0 && unmatched.length > 0) {
|
|
9903
|
+
issues.push("parse:no-canonical-resolution");
|
|
9904
|
+
}
|
|
9852
9905
|
if (priorities.length > 0 && !priorities.some((k) => includedSections.includes(k))) {
|
|
9853
9906
|
issues.push("parse:no-priority-resolution");
|
|
9854
9907
|
}
|
|
@@ -9857,14 +9910,14 @@ ${trimmedBody}`;
|
|
|
9857
9910
|
excerpt = truncateByBudget(excerpt, budget);
|
|
9858
9911
|
}
|
|
9859
9912
|
const effectiveMinChars = Math.min(minContentChars, source.length);
|
|
9860
|
-
if (excerpt.length < effectiveMinChars) {
|
|
9913
|
+
if (excerpt.length - metaBlock.length < effectiveMinChars) {
|
|
9861
9914
|
issues.push("content:insufficient");
|
|
9862
9915
|
}
|
|
9863
|
-
const verified = !issues.includes("parse:no-priority-resolution") && excerpt.length >= effectiveMinChars;
|
|
9916
|
+
const verified = !issues.includes("parse:no-priority-resolution") && !issues.includes("parse:no-canonical-resolution") && excerpt.length - metaBlock.length >= effectiveMinChars;
|
|
9864
9917
|
return {
|
|
9865
9918
|
excerpt,
|
|
9866
9919
|
verified,
|
|
9867
|
-
sectionAware: sectionAware ||
|
|
9920
|
+
sectionAware: sectionAware || unmatched.length === 0,
|
|
9868
9921
|
includedSections,
|
|
9869
9922
|
issues,
|
|
9870
9923
|
tokenEstimate: Math.round(excerpt.length / 4)
|
|
@@ -9980,6 +10033,18 @@ function truncateByBudget(source, budget) {
|
|
|
9980
10033
|
if (fenceCount % 2 === 1) cut += "\n```";
|
|
9981
10034
|
return cut;
|
|
9982
10035
|
}
|
|
10036
|
+
function buildMetaBlock(source) {
|
|
10037
|
+
const fm = source.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
10038
|
+
const pick = (key) => {
|
|
10039
|
+
if (!fm) return "";
|
|
10040
|
+
const m = fm[1].match(new RegExp(`^${key}:\\s*"?([^"\\n]+)"?`, "m"));
|
|
10041
|
+
return (m?.[1] || "").trim();
|
|
10042
|
+
};
|
|
10043
|
+
const id = pick("id");
|
|
10044
|
+
const title = pick("title");
|
|
10045
|
+
const type = pick("type");
|
|
10046
|
+
return `<!-- SOURCE: ${type || "ARTIFACT"} | ${id || "unknown"}${title ? ` \u2014 ${title}` : ""} (section-aware excerpt; canonical knowledge, do not contradict) -->`;
|
|
10047
|
+
}
|
|
9983
10048
|
|
|
9984
10049
|
// src/services/knowledgeExpositionService.ts
|
|
9985
10050
|
var EXPOSITION_REL = (lessonCode) => "_sot/expositions/" + lessonCode + ".md";
|
|
@@ -10128,7 +10193,7 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
|
|
|
10128
10193
|
storage,
|
|
10129
10194
|
gates: options.gates
|
|
10130
10195
|
});
|
|
10131
|
-
return { relPath: result.relPath, context: buildExpositionContext(result.content), reused: result.reused };
|
|
10196
|
+
return { relPath: result.relPath, context: buildExpositionContext(result.content), reused: result.reused, rawContent: result.content };
|
|
10132
10197
|
}
|
|
10133
10198
|
init_errors();
|
|
10134
10199
|
var asArr = (v) => Array.isArray(v) ? v : [];
|
|
@@ -11839,6 +11904,8 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
|
|
|
11839
11904
|
targetLang = targetLang || "vi";
|
|
11840
11905
|
const languageDirective = buildLanguageDirective(targetLang);
|
|
11841
11906
|
let expositionContext = "";
|
|
11907
|
+
let expositionInjection;
|
|
11908
|
+
let lessonInjection;
|
|
11842
11909
|
let sessionSliceContext = "";
|
|
11843
11910
|
try {
|
|
11844
11911
|
const exposition = await ensureExpositionForLesson(storage, projectId, lessonCode, {
|
|
@@ -11846,7 +11913,18 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
|
|
|
11846
11913
|
force: options.force
|
|
11847
11914
|
});
|
|
11848
11915
|
if (exposition) {
|
|
11849
|
-
|
|
11916
|
+
const expoExcerpt = buildExpositionExcerpt(exposition.rawContent || exposition.context, {
|
|
11917
|
+
budget: 6e3,
|
|
11918
|
+
sectionLanguageContract: slcMarkdown,
|
|
11919
|
+
artifactType: "KNOWLEDGE_EXPOSITION"
|
|
11920
|
+
});
|
|
11921
|
+
expositionContext = expoExcerpt.excerpt || exposition.context;
|
|
11922
|
+
expositionInjection = {
|
|
11923
|
+
source: "KNOWLEDGE_EXPOSITION",
|
|
11924
|
+
verified: expoExcerpt.verified,
|
|
11925
|
+
sectionAware: expoExcerpt.sectionAware,
|
|
11926
|
+
issues: expoExcerpt.issues
|
|
11927
|
+
};
|
|
11850
11928
|
onProgress?.("@content", `[EXPOSITION] ${exposition.reused ? "Reused" : "Generated"} \`\${exposition.relPath}\` (plan-driven knowledge source)...`);
|
|
11851
11929
|
} else {
|
|
11852
11930
|
onProgress?.("@content", "[EXPOSITION] No CURRICULUM_PLAN.json \u2014 running legacy knowledge flow (no plan-driven exposition).");
|
|
@@ -12086,6 +12164,7 @@ ${yamlBlock.trim()}
|
|
|
12086
12164
|
};
|
|
12087
12165
|
var injectYamlReviewMetadata = injectYamlReviewMetadata2;
|
|
12088
12166
|
const judgeObjectives = parseLessonObjectives(lessonContent, concept, bloomLevel);
|
|
12167
|
+
const judgeExpositionExcerpt = expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0;
|
|
12089
12168
|
const judgeResult = await LLMJudgeEngine.evaluateArtifactWithLLM({
|
|
12090
12169
|
targetArtifactType: "LESSON",
|
|
12091
12170
|
lessonId: lessonCode,
|
|
@@ -12097,7 +12176,7 @@ ${yamlBlock.trim()}
|
|
|
12097
12176
|
title: lessonTitle,
|
|
12098
12177
|
frameworkExcerpt: framework ? buildFrameworkExcerptForLesson(framework, lessonId) : void 0,
|
|
12099
12178
|
styleGuideExcerpt: effectiveStyleGuide || void 0,
|
|
12100
|
-
canonicalExposition:
|
|
12179
|
+
canonicalExposition: judgeExpositionExcerpt
|
|
12101
12180
|
}),
|
|
12102
12181
|
standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0
|
|
12103
12182
|
});
|
|
@@ -12152,6 +12231,16 @@ ${currentContent}` }],
|
|
|
12152
12231
|
options.onProgress?.("@repair", chunk, { type: type || "content", artifactType: "LESSON" });
|
|
12153
12232
|
}
|
|
12154
12233
|
);
|
|
12234
|
+
const repairedExcerptCheck = buildSectionAwareExcerpt(repairedRaw, {
|
|
12235
|
+
priorities: DEFAULT_LESSON_PRIORITIES,
|
|
12236
|
+
budget: 12e3,
|
|
12237
|
+
sectionLanguageContract: slcMarkdown,
|
|
12238
|
+
artifactType: "LESSON"
|
|
12239
|
+
});
|
|
12240
|
+
if (repairedExcerptCheck.issues.includes("parse:no-priority-resolution")) {
|
|
12241
|
+
onProgress?.("@repair", `\u26A0\uFE0F Repair round ${round} lost mandatory section structure (no priority section resolved) \u2014 rejecting repaired draft, retaining previous`);
|
|
12242
|
+
break;
|
|
12243
|
+
}
|
|
12155
12244
|
if (!repairedRaw || repairedRaw.startsWith("\u26A0\uFE0F") || repairedRaw.length < 200) {
|
|
12156
12245
|
onProgress?.("@repair", `\u26A0\uFE0F Repair round ${round} produced invalid output \u2014 stopping repair loop`);
|
|
12157
12246
|
break;
|
|
@@ -12176,7 +12265,7 @@ ${currentContent}` }],
|
|
|
12176
12265
|
title: lessonTitle,
|
|
12177
12266
|
frameworkExcerpt: framework ? buildFrameworkExcerptForLesson(framework, lessonId) : void 0,
|
|
12178
12267
|
styleGuideExcerpt: effectiveStyleGuide || void 0,
|
|
12179
|
-
canonicalExposition: expositionContext ? expositionContext
|
|
12268
|
+
canonicalExposition: expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0
|
|
12180
12269
|
}),
|
|
12181
12270
|
standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0
|
|
12182
12271
|
});
|
|
@@ -12234,7 +12323,9 @@ ${currentContent}` }],
|
|
|
12234
12323
|
pedagogy,
|
|
12235
12324
|
producedArtifacts,
|
|
12236
12325
|
pausedForReview: true,
|
|
12237
|
-
gateStatus: "judge_needs_revision"
|
|
12326
|
+
gateStatus: "judge_needs_revision",
|
|
12327
|
+
contextInjection: expositionInjection || lessonInjection,
|
|
12328
|
+
promptChars: commonContext.length
|
|
12238
12329
|
};
|
|
12239
12330
|
}
|
|
12240
12331
|
}
|
|
@@ -12257,7 +12348,9 @@ ${currentContent}` }],
|
|
|
12257
12348
|
pedagogy,
|
|
12258
12349
|
producedArtifacts,
|
|
12259
12350
|
pausedForReview: true,
|
|
12260
|
-
gateStatus: "judge_error"
|
|
12351
|
+
gateStatus: "judge_error",
|
|
12352
|
+
contextInjection: expositionInjection,
|
|
12353
|
+
promptChars: commonContext.length
|
|
12261
12354
|
};
|
|
12262
12355
|
}
|
|
12263
12356
|
}
|
|
@@ -12287,7 +12380,9 @@ ${currentContent}` }],
|
|
|
12287
12380
|
pedagogy,
|
|
12288
12381
|
producedArtifacts,
|
|
12289
12382
|
pausedForReview: true,
|
|
12290
|
-
gateStatus: "awaiting_approval"
|
|
12383
|
+
gateStatus: "awaiting_approval",
|
|
12384
|
+
contextInjection: expositionInjection,
|
|
12385
|
+
promptChars: commonContext.length
|
|
12291
12386
|
};
|
|
12292
12387
|
}
|
|
12293
12388
|
if (isLessonApproved && currentLessonInfo?.state !== "approved") {
|
|
@@ -12304,6 +12399,13 @@ ${currentContent}` }],
|
|
|
12304
12399
|
artifactType: "LESSON"
|
|
12305
12400
|
});
|
|
12306
12401
|
const lessonExcerpt = lessonExcerptResult.excerpt;
|
|
12402
|
+
const lessonInjectionMeta = {
|
|
12403
|
+
source: "LESSON",
|
|
12404
|
+
verified: lessonExcerptResult.verified,
|
|
12405
|
+
sectionAware: lessonExcerptResult.sectionAware,
|
|
12406
|
+
issues: lessonExcerptResult.issues
|
|
12407
|
+
};
|
|
12408
|
+
lessonInjection = lessonInjectionMeta;
|
|
12307
12409
|
const satelliteContext = `${commonContext}
|
|
12308
12410
|
|
|
12309
12411
|
[CANONICAL LESSON PLAN (${pedagogyLabel})]:
|
|
@@ -12462,46 +12564,175 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
12462
12564
|
const slideRelPath = `_content/${unitCode}/SLIDE_${lessonCode}.md`;
|
|
12463
12565
|
if (artifactScope.includes("SLIDE") && (!await storage.readArtifact(projectId, slideRelPath) || force)) {
|
|
12464
12566
|
onProgress?.("@illustrator", `[4/4] Authoring Presentation Slide Deck: \`SLIDE_${lessonCode}.md\`...`);
|
|
12465
|
-
const
|
|
12466
|
-
|
|
12567
|
+
const isHtmlEngine = options.slidesEngine === "html";
|
|
12568
|
+
if (isHtmlEngine) {
|
|
12569
|
+
let presentationKitAi = null;
|
|
12570
|
+
try {
|
|
12571
|
+
presentationKitAi = await import('@thanh01.pmt/presentation-kit/ai');
|
|
12572
|
+
} catch {
|
|
12573
|
+
}
|
|
12574
|
+
const slidePrompt = buildHtmlDeckSlidePrompt({
|
|
12575
|
+
lessonCode,
|
|
12576
|
+
lessonTitle,
|
|
12577
|
+
pedagogyLabel,
|
|
12578
|
+
languageDirective,
|
|
12579
|
+
headingDirective: buildHeadingDirective("SLIDE", slcMarkdown, targetLang),
|
|
12580
|
+
glossaryBlock: glossaryContext ? `[GLOSSARY TERMS (use these exact definitions)]:
|
|
12581
|
+
${glossaryContext}` : void 0,
|
|
12582
|
+
customContract: presentationKitAi?.SLIDE_HTML_PROMPT_CONTRACT
|
|
12583
|
+
});
|
|
12584
|
+
const rawSlide = await runCurriculumAIInference(
|
|
12585
|
+
[{ role: "user", content: slidePrompt }],
|
|
12586
|
+
satelliteContext,
|
|
12587
|
+
{},
|
|
12588
|
+
(chunk, type) => {
|
|
12589
|
+
options.onProgress?.("@illustrator", chunk, { type: type || "content", artifactType: "SLIDE" });
|
|
12590
|
+
}
|
|
12591
|
+
);
|
|
12592
|
+
if (!rawSlide || rawSlide.startsWith("\u26A0\uFE0F") || rawSlide.length < 200) {
|
|
12593
|
+
throw createAiInferenceError({
|
|
12594
|
+
errorCode: "ERR_AI_RESPONSE_MALFORMED",
|
|
12595
|
+
agent: "@illustrator",
|
|
12596
|
+
artifactType: "SLIDE",
|
|
12597
|
+
lessonId: lessonCode,
|
|
12598
|
+
message: `Agent @illustrator failed to author valid HTML Slides for ${lessonCode}.`,
|
|
12599
|
+
rawError: rawSlide || "Empty AI response"
|
|
12600
|
+
});
|
|
12601
|
+
}
|
|
12602
|
+
let deckJson = null;
|
|
12603
|
+
const extractCleanJson = (str) => {
|
|
12604
|
+
const fenceMatch = str.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
12605
|
+
const candidate = fenceMatch ? fenceMatch[1] : str;
|
|
12606
|
+
return candidate.trim();
|
|
12607
|
+
};
|
|
12608
|
+
try {
|
|
12609
|
+
deckJson = JSON.parse(extractCleanJson(rawSlide));
|
|
12610
|
+
} catch {
|
|
12611
|
+
try {
|
|
12612
|
+
const { jsonrepair: jsonrepair2 } = await import('jsonrepair');
|
|
12613
|
+
deckJson = JSON.parse(jsonrepair2(extractCleanJson(rawSlide)));
|
|
12614
|
+
} catch {
|
|
12615
|
+
}
|
|
12616
|
+
}
|
|
12617
|
+
let validation = presentationKitAi?.validateHtmlSlideDeck ? presentationKitAi.validateHtmlSlideDeck(deckJson, true) : deckJson && Array.isArray(deckJson.slides) ? { valid: true, score: 95, issues: [], slideCount: deckJson.slides.length, notesCoveragePercent: 100 } : { valid: false, score: 0, issues: [{ message: "Malformed JSON slide deck" }], slideCount: 0, notesCoveragePercent: 0 };
|
|
12618
|
+
if (!validation.valid && presentationKitAi?.validateHtmlSlideDeck) {
|
|
12619
|
+
const repairIssues = (validation.issues || []).map((i) => `- Slide ${i.slideIndex ?? "?"}: ${i.message}`).join("\n");
|
|
12620
|
+
onProgress?.("@illustrator", `\u26A0\uFE0F HTML slide deck failed schema validation \u2014 retrying once with targeted repair feedback...`);
|
|
12621
|
+
const repairPrompt = `Your previous slide deck failed strict schema validation:
|
|
12622
|
+
${repairIssues}
|
|
12623
|
+
|
|
12624
|
+
Fix ALL issues and output the complete corrected JSON object strictly matching the schema:`;
|
|
12625
|
+
const repairedRaw = await runCurriculumAIInference(
|
|
12626
|
+
[{ role: "user", content: repairPrompt }],
|
|
12627
|
+
satelliteContext,
|
|
12628
|
+
{},
|
|
12629
|
+
(chunk, type) => {
|
|
12630
|
+
options.onProgress?.("@illustrator", chunk, { type: type || "content", artifactType: "SLIDE" });
|
|
12631
|
+
}
|
|
12632
|
+
);
|
|
12633
|
+
try {
|
|
12634
|
+
deckJson = JSON.parse(extractCleanJson(repairedRaw));
|
|
12635
|
+
validation = presentationKitAi.validateHtmlSlideDeck(deckJson, true);
|
|
12636
|
+
} catch {
|
|
12637
|
+
try {
|
|
12638
|
+
const { jsonrepair: jsonrepair2 } = await import('jsonrepair');
|
|
12639
|
+
deckJson = JSON.parse(jsonrepair2(extractCleanJson(repairedRaw)));
|
|
12640
|
+
validation = presentationKitAi.validateHtmlSlideDeck(deckJson, true);
|
|
12641
|
+
} catch {
|
|
12642
|
+
}
|
|
12643
|
+
}
|
|
12644
|
+
}
|
|
12645
|
+
const markdownWrapper = `---
|
|
12646
|
+
id: "SLIDE_${lessonCode}"
|
|
12647
|
+
title: "${titleHeader(lessonTitle)}"
|
|
12648
|
+
type: "SLIDE"
|
|
12649
|
+
format: "html-deck"
|
|
12650
|
+
engine: "html"
|
|
12651
|
+
phase: "P2"
|
|
12652
|
+
deliverable: "P2-T09"
|
|
12653
|
+
version: "v1.0"
|
|
12654
|
+
date: "${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}"
|
|
12655
|
+
---
|
|
12656
|
+
|
|
12657
|
+
\`\`\`json
|
|
12658
|
+
${JSON.stringify(deckJson || {}, null, 2)}
|
|
12659
|
+
\`\`\`
|
|
12660
|
+
`;
|
|
12661
|
+
await storage.saveArtifact(projectId, slideRelPath, markdownWrapper);
|
|
12662
|
+
producedArtifacts.push(`SLIDE_${lessonCode}.md`);
|
|
12663
|
+
if (deckJson) {
|
|
12664
|
+
const deckJsonStr = JSON.stringify(deckJson, null, 2);
|
|
12665
|
+
await storage.saveArtifact(projectId, `_content/${unitCode}/SLIDE_${lessonCode}.deck.json`, deckJsonStr);
|
|
12666
|
+
producedArtifacts.push(`SLIDE_${lessonCode}.deck.json`);
|
|
12667
|
+
if (presentationKitAi?.compileHtmlDeck) {
|
|
12668
|
+
try {
|
|
12669
|
+
const compiled = presentationKitAi.compileHtmlDeck(deckJson);
|
|
12670
|
+
if (compiled?.html) {
|
|
12671
|
+
await storage.saveArtifact(projectId, `_content/${unitCode}/SLIDE_${lessonCode}.html`, compiled.html);
|
|
12672
|
+
producedArtifacts.push(`SLIDE_${lessonCode}.html`);
|
|
12673
|
+
}
|
|
12674
|
+
} catch (compErr) {
|
|
12675
|
+
console.warn(`[produceSingleLesson] Failed compiling HTML deck for ${lessonCode}:`, compErr);
|
|
12676
|
+
}
|
|
12677
|
+
}
|
|
12678
|
+
}
|
|
12679
|
+
const passed = Boolean(validation.valid && (validation.score ?? 100) >= 80);
|
|
12680
|
+
const score = validation.score ?? (passed ? 95 : 50);
|
|
12681
|
+
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
12682
|
+
state: passed ? "approved" : "rejected",
|
|
12683
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12684
|
+
contentHash: computeContentHash(markdownWrapper),
|
|
12685
|
+
review: {
|
|
12686
|
+
decision: passed ? "APPROVED" : "NEEDS_REVISION",
|
|
12687
|
+
reviewedBy: "@agent-as-judge",
|
|
12688
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12689
|
+
score,
|
|
12690
|
+
critique: passed ? `HTML Slide Deck passed strict schema validation (${score}/100, ${validation.slideCount ?? deckJson?.slides?.length ?? 0} slides). Auto-approved.` : `HTML Slide Deck failed schema validation: ${(validation.issues || []).map((i) => i.message).join("; ")}`
|
|
12691
|
+
}
|
|
12692
|
+
});
|
|
12693
|
+
onProgress?.("@reviewer", passed ? `\u2705 SLIDE judge PASS (${score}/100) [html-deck schema validated]` : `\u26A0\uFE0F SLIDE judge NEEDS_REVISION (${score}/100) [html-deck schema issues]`);
|
|
12694
|
+
} else {
|
|
12695
|
+
const canonicalSlideTemplate = loadCurriculumTemplate("SLIDE_template.md");
|
|
12696
|
+
const templateScaffold = canonicalSlideTemplate || `---
|
|
12467
12697
|
marp: true
|
|
12468
12698
|
theme: default
|
|
12469
12699
|
paginate: true
|
|
12470
12700
|
header: "${titleHeader(lessonTitle)}"
|
|
12471
12701
|
footer: "Curriculum OS \u2022 ${lessonCode} (${pedagogyLabel})"
|
|
12472
12702
|
---`;
|
|
12473
|
-
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12703
|
+
const slidePrompt = buildMarpMarkdownSlidePrompt({
|
|
12704
|
+
lessonCode,
|
|
12705
|
+
templateScaffold,
|
|
12706
|
+
languageDirective,
|
|
12707
|
+
headingDirective: buildHeadingDirective("SLIDE", slcMarkdown, targetLang)
|
|
12708
|
+
});
|
|
12709
|
+
const rawSlide = await runCurriculumAIInference(
|
|
12710
|
+
[{ role: "user", content: slidePrompt }],
|
|
12711
|
+
satelliteContext,
|
|
12712
|
+
{},
|
|
12713
|
+
(chunk, type) => {
|
|
12714
|
+
options.onProgress?.("@illustrator", chunk, { type: type || "content", artifactType: "SLIDE" });
|
|
12715
|
+
}
|
|
12716
|
+
);
|
|
12717
|
+
if (!rawSlide || rawSlide.startsWith("\u26A0\uFE0F") || rawSlide.length < 200) {
|
|
12718
|
+
throw createAiInferenceError({
|
|
12719
|
+
errorCode: "ERR_AI_RESPONSE_MALFORMED",
|
|
12720
|
+
agent: "@illustrator",
|
|
12721
|
+
artifactType: "SLIDE",
|
|
12722
|
+
lessonId: lessonCode,
|
|
12723
|
+
message: `Agent @illustrator failed to author valid Slides for ${lessonCode}.`,
|
|
12724
|
+
rawError: rawSlide || "Empty AI response"
|
|
12725
|
+
});
|
|
12485
12726
|
}
|
|
12486
|
-
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
|
|
12491
|
-
artifactType: "SLIDE",
|
|
12492
|
-
lessonId: lessonCode,
|
|
12493
|
-
message: `Agent @illustrator failed to author valid Slides for ${lessonCode}.`,
|
|
12494
|
-
rawError: rawSlide || "Empty AI response"
|
|
12727
|
+
await storage.saveArtifact(projectId, slideRelPath, rawSlide);
|
|
12728
|
+
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
12729
|
+
state: "completed",
|
|
12730
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12731
|
+
contentHash: computeContentHash(rawSlide)
|
|
12495
12732
|
});
|
|
12733
|
+
producedArtifacts.push(`SLIDE_${lessonCode}.md`);
|
|
12734
|
+
await judgeSat("SLIDE", rawSlide);
|
|
12496
12735
|
}
|
|
12497
|
-
await storage.saveArtifact(projectId, slideRelPath, rawSlide);
|
|
12498
|
-
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
12499
|
-
state: "completed",
|
|
12500
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12501
|
-
contentHash: computeContentHash(rawSlide)
|
|
12502
|
-
});
|
|
12503
|
-
producedArtifacts.push(`SLIDE_${lessonCode}.md`);
|
|
12504
|
-
await judgeSat("SLIDE", rawSlide);
|
|
12505
12736
|
}
|
|
12506
12737
|
const guideRelPath = `_content/${unitCode}/GUIDE_${lessonCode}.md`;
|
|
12507
12738
|
if (artifactScope.includes("GUIDE") && (!await storage.readArtifact(projectId, guideRelPath) || force)) {
|
|
@@ -12814,7 +13045,9 @@ ${buildHeadingDirective("EXT", slcMarkdown, targetLang)}`;
|
|
|
12814
13045
|
lessonId: lessonCode,
|
|
12815
13046
|
pedagogy,
|
|
12816
13047
|
producedArtifacts,
|
|
12817
|
-
pausedForReview: false
|
|
13048
|
+
pausedForReview: false,
|
|
13049
|
+
contextInjection: lessonInjection && lessonInjection.verified ? expositionInjection || lessonInjection : lessonInjection || expositionInjection,
|
|
13050
|
+
promptChars: commonContext.length
|
|
12818
13051
|
};
|
|
12819
13052
|
}
|
|
12820
13053
|
function parseLessonObjectives(lessonContent, concept, bloomLevel) {
|
|
@@ -13444,6 +13677,24 @@ function getArtifactSpecializedInvariants(type, req) {
|
|
|
13444
13677
|
const studentCount = req.studentCount || "25";
|
|
13445
13678
|
switch (type) {
|
|
13446
13679
|
case "slide": {
|
|
13680
|
+
if (req.slidesEngine === "html") {
|
|
13681
|
+
return `
|
|
13682
|
+
### \u{1F3AC} INTERACTIVE HTML SLIDE ENGINE CONTRACT:
|
|
13683
|
+
You generate structured slide decks for the LearnWell HTML Slide Presentation Engine.
|
|
13684
|
+
Output a single valid JSON object matching: { "title": "...", "theme": "ocean", "slides": [{ "layoutId": "hero-cover", "slots": { ... }, "notes": "..." }] }
|
|
13685
|
+
|
|
13686
|
+
Layout Presets available:
|
|
13687
|
+
- hero-cover (title, subtitle, presenter, date, tag)
|
|
13688
|
+
- two-column-split (col1Title, col1Items[], col2Title, col2Items[])
|
|
13689
|
+
- code-explainer (filename, language, code, highlightLines, annotations[])
|
|
13690
|
+
- metrics-3-card (card1Title, card1Val, card1Desc, ...)
|
|
13691
|
+
- process-flow (steps: [{ label, desc, status }])
|
|
13692
|
+
- quiz-checkpoint (question, options[], answerIdx, explanation)
|
|
13693
|
+
- tiered-practice-3cards (foundation, standard, challenge cards)
|
|
13694
|
+
- takeaways-summary (takeaways: bullet points)
|
|
13695
|
+
|
|
13696
|
+
Mandatory Presenter Notes on every slide with Talk Track, Cold-Call, and Scaffolding Tip.`;
|
|
13697
|
+
}
|
|
13447
13698
|
const slideCount = cfg.slideCount || 10;
|
|
13448
13699
|
const presentationStyle = cfg.presentationStyle || "modern";
|
|
13449
13700
|
const presentationDuration = cfg.presentationDuration || 45;
|
|
@@ -17524,6 +17775,7 @@ exports.buildExpositionExcerpt = buildExpositionExcerpt;
|
|
|
17524
17775
|
exports.buildExtensionPrompt = buildExtensionPrompt;
|
|
17525
17776
|
exports.buildHandoutPrompt = buildHandoutPrompt;
|
|
17526
17777
|
exports.buildHeadingDirective = buildHeadingDirective;
|
|
17778
|
+
exports.buildHtmlDeckSlidePrompt = buildHtmlDeckSlidePrompt;
|
|
17527
17779
|
exports.buildImagePrompt = buildImagePrompt;
|
|
17528
17780
|
exports.buildJudgePrompt = buildJudgePrompt;
|
|
17529
17781
|
exports.buildLanguageDirective = buildLanguageDirective;
|