@thanh01.pmt/curriculum-kit 1.2.0 → 1.3.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 +53 -4
- 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 +53 -5
- 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 +316 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.mjs +316 -46
- package/dist/index.mjs.map +1 -1
- package/dist/pipeline/index.cjs +4 -4
- package/dist/pipeline/index.cjs.map +1 -1
- package/dist/pipeline/index.mjs +4 -4
- package/dist/pipeline/index.mjs.map +1 -1
- package/dist/workflow/index.cjs +22 -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 +22 -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) {
|
|
@@ -5571,13 +5619,13 @@ Evaluate the candidate artifact across these 6 dimensions:
|
|
|
5571
5619
|
- 0 pts: Missing critical sections.
|
|
5572
5620
|
|
|
5573
5621
|
5. **Technical & Conceptual Accuracy (15 pts):**
|
|
5574
|
-
- 15 pts: Code snippets, logic flows, architectural descriptions, and Mermaid diagrams are conceptually sound
|
|
5622
|
+
- 15 pts: Code snippets, logic flows, architectural descriptions, and Mermaid diagrams are conceptually sound and valid. If \`canonicalExposition\` or \`[KNOWLEDGE_EXPOSITION]\` is provided, content strictly adheres to canonical concepts without contradicting definitions or examples.
|
|
5575
5623
|
- 8 pts: Minor syntax inaccuracies or unidiomatic code that does not break core concepts.
|
|
5576
|
-
- 0 pts: Severe technical hallucinations, broken logic, or
|
|
5624
|
+
- 0 pts: Severe technical hallucinations, broken logic, invalid diagrams, or direct contradiction of canonical exposition knowledge.
|
|
5577
5625
|
|
|
5578
5626
|
6. **Contract Consistency & Zero-Drift (10 pts):**
|
|
5579
|
-
- 10 pts: Satellite artifact strictly adheres to the scope, tech stack, and objectives of Lesson ${lessonId} without introducing unrelated topics.
|
|
5580
|
-
- 0-5 pts: Scope drift
|
|
5627
|
+
- 10 pts: Satellite artifact strictly adheres to the scope, tech stack, and objectives of Lesson ${lessonId} without introducing unrelated topics. If \`canonicalLessonExcerpt\` is present in candidate JSON, all satellite phases/activities strictly operationalize the master Lesson Flow and Activity Sequence without inventing divergent timelines.
|
|
5628
|
+
- 0-5 pts: Scope drift, topic divergence, or disjoint activity sequence detected compared to master lesson.
|
|
5581
5629
|
|
|
5582
5630
|
## VERDICT RULES
|
|
5583
5631
|
- **PASS:** Score >= 80 AND Language Adherence = true AND Standards Alignment >= 12 (when STANDARDS GROUND TRUTH is present).
|
|
@@ -11872,11 +11920,42 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
|
|
|
11872
11920
|
}
|
|
11873
11921
|
const pedagogy = detectProjectPedagogy(framework, styleGuide, options.pedagogy);
|
|
11874
11922
|
const pedagogyLabel = pedagogy.toUpperCase();
|
|
11875
|
-
|
|
11923
|
+
let glossaryContext = "";
|
|
11924
|
+
try {
|
|
11925
|
+
const glossaryRaw = await storage.readArtifact(projectId, "_sot/GLOSSARY.json");
|
|
11926
|
+
if (glossaryRaw) {
|
|
11927
|
+
let allTerms = [];
|
|
11928
|
+
try {
|
|
11929
|
+
allTerms = JSON.parse(glossaryRaw).terms ?? [];
|
|
11930
|
+
} catch {
|
|
11931
|
+
}
|
|
11932
|
+
let scopedTermsSet = null;
|
|
11933
|
+
const planRaw = await storage.readArtifact(projectId, "_sot/CURRICULUM_PLAN.json");
|
|
11934
|
+
if (planRaw) {
|
|
11935
|
+
try {
|
|
11936
|
+
const plan = JSON.parse(planRaw);
|
|
11937
|
+
const sessionScope = plan.glossary_scope?.find((g) => g.session_id === lessonCode);
|
|
11938
|
+
if (sessionScope?.terms?.length) {
|
|
11939
|
+
scopedTermsSet = new Set(sessionScope.terms.map((t) => t.toLowerCase()));
|
|
11940
|
+
}
|
|
11941
|
+
} catch {
|
|
11942
|
+
}
|
|
11943
|
+
}
|
|
11944
|
+
const relevantTerms = scopedTermsSet ? allTerms.filter((g) => scopedTermsSet.has(g.term.toLowerCase())) : allTerms.slice(0, 10);
|
|
11945
|
+
if (relevantTerms.length > 0) {
|
|
11946
|
+
const lines = relevantTerms.map(
|
|
11947
|
+
(t) => `- **${t.term}**: ${t.definition || ""}${t.example ? ` (e.g. \`${t.example}\`)` : ""}`
|
|
11948
|
+
);
|
|
11949
|
+
glossaryContext = buildSectionAwareExcerpt(lines.join("\n"), { budget: 800 }).excerpt;
|
|
11950
|
+
}
|
|
11951
|
+
}
|
|
11952
|
+
} catch {
|
|
11953
|
+
}
|
|
11954
|
+
let effectiveStyleGuide = styleGuide ? buildSectionAwareExcerpt(styleGuide, {
|
|
11876
11955
|
priorities: ["Voice & Tone", "Standard Terminology (Glossary)", "Lesson Content Standards"],
|
|
11877
11956
|
budget: 2e3
|
|
11878
11957
|
}).excerpt : "";
|
|
11879
|
-
|
|
11958
|
+
let effectiveRefPack = refPack ? buildSectionAwareExcerpt(refPack, {
|
|
11880
11959
|
priorities: [
|
|
11881
11960
|
"Technical Overview & Architecture Blueprint",
|
|
11882
11961
|
"Hardware Pinout & Wiring Configuration Matrix",
|
|
@@ -11884,7 +11963,7 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
|
|
|
11884
11963
|
],
|
|
11885
11964
|
budget: 2500
|
|
11886
11965
|
}).excerpt : "";
|
|
11887
|
-
const
|
|
11966
|
+
const baseContextPrefix = `PROJECT & ACADEMIC CONTEXT:
|
|
11888
11967
|
- Project ID: ${projectId}
|
|
11889
11968
|
- Pedagogical Model: ${pedagogyLabel}
|
|
11890
11969
|
- Lesson Code: ${lessonCode}
|
|
@@ -11894,13 +11973,51 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
|
|
|
11894
11973
|
- Target Bloom Level: ${bloomLevel}
|
|
11895
11974
|
|
|
11896
11975
|
[CURRICULUM FRAMEWORK EXCERPT]:
|
|
11897
|
-
${buildFrameworkExcerptForLesson(framework, lessonId)}
|
|
11976
|
+
${buildFrameworkExcerptForLesson(framework, lessonId)}`;
|
|
11977
|
+
const glossaryBlock = glossaryContext ? `
|
|
11978
|
+
|
|
11979
|
+
[GLOSSARY TERMS (use these exact definitions)]:
|
|
11980
|
+
${glossaryContext}` : "";
|
|
11981
|
+
const standardsBlock = standardsContext ? `
|
|
11982
|
+
|
|
11983
|
+
${standardsContext}` : "";
|
|
11984
|
+
const expositionBlock = expositionContext ? `
|
|
11985
|
+
|
|
11986
|
+
[KNOWLEDGE_EXPOSITION (CANONICAL KNOWLEDGE \u2014 teach from this, do not contradict)]:
|
|
11987
|
+
${expositionContext}` : "";
|
|
11988
|
+
const sessionSliceBlock = sessionSliceContext ? `
|
|
11989
|
+
|
|
11990
|
+
${sessionSliceContext}` : "";
|
|
11991
|
+
const assembleCommonContext = (ref, sg) => `${baseContextPrefix}
|
|
11898
11992
|
|
|
11899
11993
|
[CONTENT STYLE GUIDE EXCERPT]:
|
|
11900
|
-
${
|
|
11994
|
+
${sg}
|
|
11901
11995
|
|
|
11902
11996
|
[REFERENCE PACK GROUND TRUTH]:
|
|
11903
|
-
${
|
|
11997
|
+
${ref}${standardsBlock}${glossaryBlock}${expositionBlock}${sessionSliceBlock}`;
|
|
11998
|
+
let commonContext = assembleCommonContext(effectiveRefPack, effectiveStyleGuide);
|
|
11999
|
+
const MAX_COMPOSITE_PROMPT_CHARS = 3e4;
|
|
12000
|
+
if (commonContext.length > MAX_COMPOSITE_PROMPT_CHARS && effectiveRefPack.length > 1e3) {
|
|
12001
|
+
effectiveRefPack = buildSectionAwareExcerpt(refPack, {
|
|
12002
|
+
priorities: [
|
|
12003
|
+
"Technical Overview & Architecture Blueprint",
|
|
12004
|
+
"Hardware Pinout & Wiring Configuration Matrix"
|
|
12005
|
+
],
|
|
12006
|
+
budget: 1e3
|
|
12007
|
+
}).excerpt;
|
|
12008
|
+
commonContext = assembleCommonContext(effectiveRefPack, effectiveStyleGuide);
|
|
12009
|
+
}
|
|
12010
|
+
if (commonContext.length > MAX_COMPOSITE_PROMPT_CHARS && effectiveStyleGuide.length > 1e3) {
|
|
12011
|
+
effectiveStyleGuide = buildSectionAwareExcerpt(styleGuide, {
|
|
12012
|
+
priorities: ["Voice & Tone", "Standard Terminology (Glossary)"],
|
|
12013
|
+
budget: 1e3
|
|
12014
|
+
}).excerpt;
|
|
12015
|
+
commonContext = assembleCommonContext(effectiveRefPack, effectiveStyleGuide);
|
|
12016
|
+
}
|
|
12017
|
+
onProgress?.("@content", `[CONTEXT] Composite prompt ready (${commonContext.length} chars, adaptive budget <= ${MAX_COMPOSITE_PROMPT_CHARS})`, {
|
|
12018
|
+
type: "progress",
|
|
12019
|
+
promptChars: commonContext.length
|
|
12020
|
+
});
|
|
11904
12021
|
const producedArtifacts = [];
|
|
11905
12022
|
const lessonRelPath = `_content/${unitCode}/LESSON_${lessonCode}.md`;
|
|
11906
12023
|
const existingLessonContent = await storage.readArtifact(projectId, lessonRelPath);
|
|
@@ -12027,7 +12144,7 @@ ${yamlBlock.trim()}
|
|
|
12027
12144
|
lessonId: lessonCode,
|
|
12028
12145
|
title: lessonTitle,
|
|
12029
12146
|
frameworkExcerpt: framework ? buildFrameworkExcerptForLesson(framework, lessonId) : void 0,
|
|
12030
|
-
styleGuideExcerpt:
|
|
12147
|
+
styleGuideExcerpt: effectiveStyleGuide || void 0,
|
|
12031
12148
|
canonicalExposition: expositionContext ? expositionContext.slice(0, 3e3) : void 0
|
|
12032
12149
|
}),
|
|
12033
12150
|
standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0
|
|
@@ -12070,7 +12187,7 @@ ${yamlBlock.trim()}
|
|
|
12070
12187
|
onProgress?.("@repair", `\u{1F527} Repair round ${round}/${MAX_REPAIR_ROUNDS} for LESSON_${lessonCode} (judge score ${currentResult.score}/100)...`);
|
|
12071
12188
|
const repairedRaw = await runCurriculumAIInference(
|
|
12072
12189
|
[{ role: "user", content: `You previously authored \`LESSON_${lessonCode}.md\` and the academic Judge REJECTED it with the critique below.
|
|
12073
|
-
Rewrite the COMPLETE corrected document: fix every listed issue,
|
|
12190
|
+
Rewrite the COMPLETE corrected document in language "${targetLang}": fix every listed issue, preserve exact frontmatter and mandatory sections (Objectives, Activity Sequence, Lesson Flow). Output 100% clean Markdown only.
|
|
12074
12191
|
|
|
12075
12192
|
JUDGE CRITIQUE:
|
|
12076
12193
|
${repairFeedback}
|
|
@@ -12088,7 +12205,13 @@ ${currentContent}` }],
|
|
|
12088
12205
|
break;
|
|
12089
12206
|
}
|
|
12090
12207
|
const repairLint = lintAndSanitizeArtifact(repairedRaw, `LESSON_${lessonCode}.md`);
|
|
12091
|
-
|
|
12208
|
+
const candidateContent = repairLint.autoFixedContent || repairedRaw;
|
|
12209
|
+
const hasFrontmatter = /^---\s*\r?\n[\s\S]*?\r?\n---/m.test(candidateContent);
|
|
12210
|
+
if (!hasFrontmatter || candidateContent.length < Math.min(800, currentContent.length * 0.4)) {
|
|
12211
|
+
onProgress?.("@repair", `\u26A0\uFE0F Repair round ${round} dropped critical structure (length or frontmatter missing) \u2014 rejecting repaired draft, retaining previous`);
|
|
12212
|
+
break;
|
|
12213
|
+
}
|
|
12214
|
+
currentContent = candidateContent;
|
|
12092
12215
|
onProgress?.("@reviewer", `\u{1F916} Re-judging repaired LESSON_${lessonCode} (round ${round})...`);
|
|
12093
12216
|
const reJudge = await LLMJudgeEngine.evaluateArtifactWithLLM({
|
|
12094
12217
|
targetArtifactType: "LESSON",
|
|
@@ -12100,7 +12223,7 @@ ${currentContent}` }],
|
|
|
12100
12223
|
lessonId: lessonCode,
|
|
12101
12224
|
title: lessonTitle,
|
|
12102
12225
|
frameworkExcerpt: framework ? buildFrameworkExcerptForLesson(framework, lessonId) : void 0,
|
|
12103
|
-
styleGuideExcerpt:
|
|
12226
|
+
styleGuideExcerpt: effectiveStyleGuide || void 0,
|
|
12104
12227
|
canonicalExposition: expositionContext ? expositionContext.slice(0, 3e3) : void 0
|
|
12105
12228
|
}),
|
|
12106
12229
|
standardStatements: Object.keys(standardStatements).length > 0 ? standardStatements : void 0
|
|
@@ -12387,46 +12510,175 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
12387
12510
|
const slideRelPath = `_content/${unitCode}/SLIDE_${lessonCode}.md`;
|
|
12388
12511
|
if (artifactScope.includes("SLIDE") && (!await storage.readArtifact(projectId, slideRelPath) || force)) {
|
|
12389
12512
|
onProgress?.("@illustrator", `[4/4] Authoring Presentation Slide Deck: \`SLIDE_${lessonCode}.md\`...`);
|
|
12390
|
-
const
|
|
12391
|
-
|
|
12513
|
+
const isHtmlEngine = options.slidesEngine === "html";
|
|
12514
|
+
if (isHtmlEngine) {
|
|
12515
|
+
let presentationKitAi = null;
|
|
12516
|
+
try {
|
|
12517
|
+
presentationKitAi = await import('@thanh01.pmt/presentation-kit/ai');
|
|
12518
|
+
} catch {
|
|
12519
|
+
}
|
|
12520
|
+
const slidePrompt = buildHtmlDeckSlidePrompt({
|
|
12521
|
+
lessonCode,
|
|
12522
|
+
lessonTitle,
|
|
12523
|
+
pedagogyLabel,
|
|
12524
|
+
languageDirective,
|
|
12525
|
+
headingDirective: buildHeadingDirective("SLIDE", slcMarkdown, targetLang),
|
|
12526
|
+
glossaryBlock: glossaryContext ? `[GLOSSARY TERMS (use these exact definitions)]:
|
|
12527
|
+
${glossaryContext}` : void 0,
|
|
12528
|
+
customContract: presentationKitAi?.SLIDE_HTML_PROMPT_CONTRACT
|
|
12529
|
+
});
|
|
12530
|
+
const rawSlide = await runCurriculumAIInference(
|
|
12531
|
+
[{ role: "user", content: slidePrompt }],
|
|
12532
|
+
satelliteContext,
|
|
12533
|
+
{},
|
|
12534
|
+
(chunk, type) => {
|
|
12535
|
+
options.onProgress?.("@illustrator", chunk, { type: type || "content", artifactType: "SLIDE" });
|
|
12536
|
+
}
|
|
12537
|
+
);
|
|
12538
|
+
if (!rawSlide || rawSlide.startsWith("\u26A0\uFE0F") || rawSlide.length < 200) {
|
|
12539
|
+
throw createAiInferenceError({
|
|
12540
|
+
errorCode: "ERR_AI_RESPONSE_MALFORMED",
|
|
12541
|
+
agent: "@illustrator",
|
|
12542
|
+
artifactType: "SLIDE",
|
|
12543
|
+
lessonId: lessonCode,
|
|
12544
|
+
message: `Agent @illustrator failed to author valid HTML Slides for ${lessonCode}.`,
|
|
12545
|
+
rawError: rawSlide || "Empty AI response"
|
|
12546
|
+
});
|
|
12547
|
+
}
|
|
12548
|
+
let deckJson = null;
|
|
12549
|
+
const extractCleanJson = (str) => {
|
|
12550
|
+
const fenceMatch = str.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
12551
|
+
const candidate = fenceMatch ? fenceMatch[1] : str;
|
|
12552
|
+
return candidate.trim();
|
|
12553
|
+
};
|
|
12554
|
+
try {
|
|
12555
|
+
deckJson = JSON.parse(extractCleanJson(rawSlide));
|
|
12556
|
+
} catch {
|
|
12557
|
+
try {
|
|
12558
|
+
const { jsonrepair: jsonrepair2 } = await import('jsonrepair');
|
|
12559
|
+
deckJson = JSON.parse(jsonrepair2(extractCleanJson(rawSlide)));
|
|
12560
|
+
} catch {
|
|
12561
|
+
}
|
|
12562
|
+
}
|
|
12563
|
+
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 };
|
|
12564
|
+
if (!validation.valid && presentationKitAi?.validateHtmlSlideDeck) {
|
|
12565
|
+
const repairIssues = (validation.issues || []).map((i) => `- Slide ${i.slideIndex ?? "?"}: ${i.message}`).join("\n");
|
|
12566
|
+
onProgress?.("@illustrator", `\u26A0\uFE0F HTML slide deck failed schema validation \u2014 retrying once with targeted repair feedback...`);
|
|
12567
|
+
const repairPrompt = `Your previous slide deck failed strict schema validation:
|
|
12568
|
+
${repairIssues}
|
|
12569
|
+
|
|
12570
|
+
Fix ALL issues and output the complete corrected JSON object strictly matching the schema:`;
|
|
12571
|
+
const repairedRaw = await runCurriculumAIInference(
|
|
12572
|
+
[{ role: "user", content: repairPrompt }],
|
|
12573
|
+
satelliteContext,
|
|
12574
|
+
{},
|
|
12575
|
+
(chunk, type) => {
|
|
12576
|
+
options.onProgress?.("@illustrator", chunk, { type: type || "content", artifactType: "SLIDE" });
|
|
12577
|
+
}
|
|
12578
|
+
);
|
|
12579
|
+
try {
|
|
12580
|
+
deckJson = JSON.parse(extractCleanJson(repairedRaw));
|
|
12581
|
+
validation = presentationKitAi.validateHtmlSlideDeck(deckJson, true);
|
|
12582
|
+
} catch {
|
|
12583
|
+
try {
|
|
12584
|
+
const { jsonrepair: jsonrepair2 } = await import('jsonrepair');
|
|
12585
|
+
deckJson = JSON.parse(jsonrepair2(extractCleanJson(repairedRaw)));
|
|
12586
|
+
validation = presentationKitAi.validateHtmlSlideDeck(deckJson, true);
|
|
12587
|
+
} catch {
|
|
12588
|
+
}
|
|
12589
|
+
}
|
|
12590
|
+
}
|
|
12591
|
+
const markdownWrapper = `---
|
|
12592
|
+
id: "SLIDE_${lessonCode}"
|
|
12593
|
+
title: "${titleHeader(lessonTitle)}"
|
|
12594
|
+
type: "SLIDE"
|
|
12595
|
+
format: "html-deck"
|
|
12596
|
+
engine: "html"
|
|
12597
|
+
phase: "P2"
|
|
12598
|
+
deliverable: "P2-T09"
|
|
12599
|
+
version: "v1.0"
|
|
12600
|
+
date: "${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}"
|
|
12601
|
+
---
|
|
12602
|
+
|
|
12603
|
+
\`\`\`json
|
|
12604
|
+
${JSON.stringify(deckJson || {}, null, 2)}
|
|
12605
|
+
\`\`\`
|
|
12606
|
+
`;
|
|
12607
|
+
await storage.saveArtifact(projectId, slideRelPath, markdownWrapper);
|
|
12608
|
+
producedArtifacts.push(`SLIDE_${lessonCode}.md`);
|
|
12609
|
+
if (deckJson) {
|
|
12610
|
+
const deckJsonStr = JSON.stringify(deckJson, null, 2);
|
|
12611
|
+
await storage.saveArtifact(projectId, `_content/${unitCode}/SLIDE_${lessonCode}.deck.json`, deckJsonStr);
|
|
12612
|
+
producedArtifacts.push(`SLIDE_${lessonCode}.deck.json`);
|
|
12613
|
+
if (presentationKitAi?.compileHtmlDeck) {
|
|
12614
|
+
try {
|
|
12615
|
+
const compiled = presentationKitAi.compileHtmlDeck(deckJson);
|
|
12616
|
+
if (compiled?.html) {
|
|
12617
|
+
await storage.saveArtifact(projectId, `_content/${unitCode}/SLIDE_${lessonCode}.html`, compiled.html);
|
|
12618
|
+
producedArtifacts.push(`SLIDE_${lessonCode}.html`);
|
|
12619
|
+
}
|
|
12620
|
+
} catch (compErr) {
|
|
12621
|
+
console.warn(`[produceSingleLesson] Failed compiling HTML deck for ${lessonCode}:`, compErr);
|
|
12622
|
+
}
|
|
12623
|
+
}
|
|
12624
|
+
}
|
|
12625
|
+
const passed = Boolean(validation.valid && (validation.score ?? 100) >= 80);
|
|
12626
|
+
const score = validation.score ?? (passed ? 95 : 50);
|
|
12627
|
+
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
12628
|
+
state: passed ? "approved" : "rejected",
|
|
12629
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12630
|
+
contentHash: computeContentHash(markdownWrapper),
|
|
12631
|
+
review: {
|
|
12632
|
+
decision: passed ? "APPROVED" : "NEEDS_REVISION",
|
|
12633
|
+
reviewedBy: "@agent-as-judge",
|
|
12634
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12635
|
+
score,
|
|
12636
|
+
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("; ")}`
|
|
12637
|
+
}
|
|
12638
|
+
});
|
|
12639
|
+
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]`);
|
|
12640
|
+
} else {
|
|
12641
|
+
const canonicalSlideTemplate = loadCurriculumTemplate("SLIDE_template.md");
|
|
12642
|
+
const templateScaffold = canonicalSlideTemplate || `---
|
|
12392
12643
|
marp: true
|
|
12393
12644
|
theme: default
|
|
12394
12645
|
paginate: true
|
|
12395
12646
|
header: "${titleHeader(lessonTitle)}"
|
|
12396
12647
|
footer: "Curriculum OS \u2022 ${lessonCode} (${pedagogyLabel})"
|
|
12397
12648
|
---`;
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12649
|
+
const slidePrompt = buildMarpMarkdownSlidePrompt({
|
|
12650
|
+
lessonCode,
|
|
12651
|
+
templateScaffold,
|
|
12652
|
+
languageDirective,
|
|
12653
|
+
headingDirective: buildHeadingDirective("SLIDE", slcMarkdown, targetLang)
|
|
12654
|
+
});
|
|
12655
|
+
const rawSlide = await runCurriculumAIInference(
|
|
12656
|
+
[{ role: "user", content: slidePrompt }],
|
|
12657
|
+
satelliteContext,
|
|
12658
|
+
{},
|
|
12659
|
+
(chunk, type) => {
|
|
12660
|
+
options.onProgress?.("@illustrator", chunk, { type: type || "content", artifactType: "SLIDE" });
|
|
12661
|
+
}
|
|
12662
|
+
);
|
|
12663
|
+
if (!rawSlide || rawSlide.startsWith("\u26A0\uFE0F") || rawSlide.length < 200) {
|
|
12664
|
+
throw createAiInferenceError({
|
|
12665
|
+
errorCode: "ERR_AI_RESPONSE_MALFORMED",
|
|
12666
|
+
agent: "@illustrator",
|
|
12667
|
+
artifactType: "SLIDE",
|
|
12668
|
+
lessonId: lessonCode,
|
|
12669
|
+
message: `Agent @illustrator failed to author valid Slides for ${lessonCode}.`,
|
|
12670
|
+
rawError: rawSlide || "Empty AI response"
|
|
12671
|
+
});
|
|
12410
12672
|
}
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
artifactType: "SLIDE",
|
|
12417
|
-
lessonId: lessonCode,
|
|
12418
|
-
message: `Agent @illustrator failed to author valid Slides for ${lessonCode}.`,
|
|
12419
|
-
rawError: rawSlide || "Empty AI response"
|
|
12673
|
+
await storage.saveArtifact(projectId, slideRelPath, rawSlide);
|
|
12674
|
+
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
12675
|
+
state: "completed",
|
|
12676
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12677
|
+
contentHash: computeContentHash(rawSlide)
|
|
12420
12678
|
});
|
|
12679
|
+
producedArtifacts.push(`SLIDE_${lessonCode}.md`);
|
|
12680
|
+
await judgeSat("SLIDE", rawSlide);
|
|
12421
12681
|
}
|
|
12422
|
-
await storage.saveArtifact(projectId, slideRelPath, rawSlide);
|
|
12423
|
-
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
12424
|
-
state: "completed",
|
|
12425
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12426
|
-
contentHash: computeContentHash(rawSlide)
|
|
12427
|
-
});
|
|
12428
|
-
producedArtifacts.push(`SLIDE_${lessonCode}.md`);
|
|
12429
|
-
await judgeSat("SLIDE", rawSlide);
|
|
12430
12682
|
}
|
|
12431
12683
|
const guideRelPath = `_content/${unitCode}/GUIDE_${lessonCode}.md`;
|
|
12432
12684
|
if (artifactScope.includes("GUIDE") && (!await storage.readArtifact(projectId, guideRelPath) || force)) {
|
|
@@ -13369,6 +13621,24 @@ function getArtifactSpecializedInvariants(type, req) {
|
|
|
13369
13621
|
const studentCount = req.studentCount || "25";
|
|
13370
13622
|
switch (type) {
|
|
13371
13623
|
case "slide": {
|
|
13624
|
+
if (req.slidesEngine === "html") {
|
|
13625
|
+
return `
|
|
13626
|
+
### \u{1F3AC} INTERACTIVE HTML SLIDE ENGINE CONTRACT:
|
|
13627
|
+
You generate structured slide decks for the LearnWell HTML Slide Presentation Engine.
|
|
13628
|
+
Output a single valid JSON object matching: { "title": "...", "theme": "ocean", "slides": [{ "layoutId": "hero-cover", "slots": { ... }, "notes": "..." }] }
|
|
13629
|
+
|
|
13630
|
+
Layout Presets available:
|
|
13631
|
+
- hero-cover (title, subtitle, presenter, date, tag)
|
|
13632
|
+
- two-column-split (col1Title, col1Items[], col2Title, col2Items[])
|
|
13633
|
+
- code-explainer (filename, language, code, highlightLines, annotations[])
|
|
13634
|
+
- metrics-3-card (card1Title, card1Val, card1Desc, ...)
|
|
13635
|
+
- process-flow (steps: [{ label, desc, status }])
|
|
13636
|
+
- quiz-checkpoint (question, options[], answerIdx, explanation)
|
|
13637
|
+
- tiered-practice-3cards (foundation, standard, challenge cards)
|
|
13638
|
+
- takeaways-summary (takeaways: bullet points)
|
|
13639
|
+
|
|
13640
|
+
Mandatory Presenter Notes on every slide with Talk Track, Cold-Call, and Scaffolding Tip.`;
|
|
13641
|
+
}
|
|
13372
13642
|
const slideCount = cfg.slideCount || 10;
|
|
13373
13643
|
const presentationStyle = cfg.presentationStyle || "modern";
|
|
13374
13644
|
const presentationDuration = cfg.presentationDuration || 45;
|
|
@@ -17449,6 +17719,7 @@ exports.buildExpositionExcerpt = buildExpositionExcerpt;
|
|
|
17449
17719
|
exports.buildExtensionPrompt = buildExtensionPrompt;
|
|
17450
17720
|
exports.buildHandoutPrompt = buildHandoutPrompt;
|
|
17451
17721
|
exports.buildHeadingDirective = buildHeadingDirective;
|
|
17722
|
+
exports.buildHtmlDeckSlidePrompt = buildHtmlDeckSlidePrompt;
|
|
17452
17723
|
exports.buildImagePrompt = buildImagePrompt;
|
|
17453
17724
|
exports.buildJudgePrompt = buildJudgePrompt;
|
|
17454
17725
|
exports.buildLanguageDirective = buildLanguageDirective;
|