@thanh01.pmt/curriculum-kit 1.4.20 → 1.4.22
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/index.cjs +204 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -2
- package/dist/index.d.ts +39 -2
- package/dist/index.mjs +202 -56
- package/dist/index.mjs.map +1 -1
- package/dist/storage/index.cjs +21 -9
- package/dist/storage/index.cjs.map +1 -1
- package/dist/storage/index.d.cts +11 -1
- package/dist/storage/index.d.ts +11 -1
- package/dist/storage/index.mjs +21 -10
- package/dist/storage/index.mjs.map +1 -1
- package/dist/workflow/index.cjs +15 -0
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.mjs +15 -0
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1513,7 +1513,8 @@ function buildSlideBatchPrompt(params) {
|
|
|
1513
1513
|
skillPrompt,
|
|
1514
1514
|
language = "Vietnamese",
|
|
1515
1515
|
languageDirective = "",
|
|
1516
|
-
headingDirective = ""
|
|
1516
|
+
headingDirective = "",
|
|
1517
|
+
groundContext = ""
|
|
1517
1518
|
} = params;
|
|
1518
1519
|
const slidesSpec = clusterSlides.map((s) => `
|
|
1519
1520
|
- Slide ${s.slideIndex} [Phase: ${s.lessonPhase}] -> Layout: "${s.layoutId}"
|
|
@@ -1526,7 +1527,11 @@ function buildSlideBatchPrompt(params) {
|
|
|
1526
1527
|
const systemPrompt = `
|
|
1527
1528
|
${skillPrompt}
|
|
1528
1529
|
|
|
1529
|
-
|
|
1530
|
+
${groundContext ? `---
|
|
1531
|
+
### PROJECT GROUND CONTEXT (framework, style guide, glossary, canonical knowledge \u2014 author from these):
|
|
1532
|
+
${groundContext}
|
|
1533
|
+
|
|
1534
|
+
---` : ""}
|
|
1530
1535
|
### OPERATIONAL GROUND RULES FOR CHUNK GENERATION:
|
|
1531
1536
|
${languageDirective}
|
|
1532
1537
|
${headingDirective}
|
|
@@ -1604,6 +1609,7 @@ __export(slideProductionWorkflow_exports, {
|
|
|
1604
1609
|
HybridPipelineError: () => exports.HybridPipelineError,
|
|
1605
1610
|
SlideBlueprintArraySchema: () => exports.SlideBlueprintArraySchema,
|
|
1606
1611
|
SlideBlueprintItemSchema: () => exports.SlideBlueprintItemSchema,
|
|
1612
|
+
emitUsage: () => emitUsage,
|
|
1607
1613
|
executeSlideProductionWorkflow: () => executeSlideProductionWorkflow,
|
|
1608
1614
|
extractJsonArray: () => extractJsonArray,
|
|
1609
1615
|
validateHybridDeckSlides: () => validateHybridDeckSlides
|
|
@@ -1656,15 +1662,28 @@ function validateHybridDeckSlides(slides, blueprint) {
|
|
|
1656
1662
|
});
|
|
1657
1663
|
return v.slice(0, 10);
|
|
1658
1664
|
}
|
|
1665
|
+
function emitUsage(usage, onProgress) {
|
|
1666
|
+
if (!onProgress || !usage) return;
|
|
1667
|
+
const promptTokens = usage.promptTokens ?? usage.inputTokens ?? 0;
|
|
1668
|
+
const completionTokens = usage.completionTokens ?? usage.outputTokens ?? 0;
|
|
1669
|
+
if (promptTokens === 0 && completionTokens === 0) return;
|
|
1670
|
+
onProgress("@illustrator", JSON.stringify({
|
|
1671
|
+
promptTokens,
|
|
1672
|
+
completionTokens,
|
|
1673
|
+
totalTokens: usage.totalTokens ?? promptTokens + completionTokens,
|
|
1674
|
+
reasoningTokens: usage.reasoningTokens ?? 0
|
|
1675
|
+
}), { type: "usage" });
|
|
1676
|
+
}
|
|
1659
1677
|
async function inferTextJson(schema, systemPrompt, userPrompt, options, label) {
|
|
1660
1678
|
const model = getAIModel(options.modelOptions);
|
|
1661
1679
|
try {
|
|
1662
|
-
const { text, finishReason } = await ai.generateText({
|
|
1680
|
+
const { text, finishReason, usage } = await ai.generateText({
|
|
1663
1681
|
model,
|
|
1664
1682
|
system: systemPrompt || void 0,
|
|
1665
1683
|
prompt: userPrompt,
|
|
1666
1684
|
maxOutputTokens: options.maxOutputTokens ?? 65536
|
|
1667
1685
|
});
|
|
1686
|
+
emitUsage(usage, options.onProgress);
|
|
1668
1687
|
const extracted = extractJsonArray(text);
|
|
1669
1688
|
if (!("value" in extracted) || extracted.value === void 0) {
|
|
1670
1689
|
console.warn(
|
|
@@ -1685,12 +1704,13 @@ async function inferTextJson(schema, systemPrompt, userPrompt, options, label) {
|
|
|
1685
1704
|
async function inferStructured(schema, systemPrompt, userPrompt, options, label) {
|
|
1686
1705
|
try {
|
|
1687
1706
|
const model = getAIModel(options.modelOptions);
|
|
1688
|
-
const { object } = await ai.generateObject({
|
|
1707
|
+
const { object, usage } = await ai.generateObject({
|
|
1689
1708
|
model,
|
|
1690
1709
|
schema,
|
|
1691
1710
|
system: systemPrompt || void 0,
|
|
1692
1711
|
prompt: userPrompt
|
|
1693
1712
|
});
|
|
1713
|
+
emitUsage(usage, options.onProgress);
|
|
1694
1714
|
const validated = schema.safeParse(object);
|
|
1695
1715
|
if (validated.success) return validated.data;
|
|
1696
1716
|
console.warn(
|
|
@@ -1705,7 +1725,9 @@ async function inferStructured(schema, systemPrompt, userPrompt, options, label)
|
|
|
1705
1725
|
const messages = [
|
|
1706
1726
|
{ role: "user", content: [systemPrompt, userPrompt].filter(Boolean).join("\n\n") }
|
|
1707
1727
|
];
|
|
1708
|
-
const raw = await runCurriculumAIInference(messages, options.satelliteContext || "", options.runnerOptions)
|
|
1728
|
+
const raw = await runCurriculumAIInference(messages, options.satelliteContext || "", options.runnerOptions, (token, type) => {
|
|
1729
|
+
if (type === "usage") options.onProgress?.("@illustrator", token, { type: "usage" });
|
|
1730
|
+
});
|
|
1709
1731
|
const fenceMatch = raw.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
1710
1732
|
const candidate = (fenceMatch ? fenceMatch[1] : raw).trim();
|
|
1711
1733
|
let parsed;
|
|
@@ -1738,7 +1760,7 @@ function normalizeBlueprintItems(items) {
|
|
|
1738
1760
|
}));
|
|
1739
1761
|
}
|
|
1740
1762
|
function buildHybridDeckPrompts(params) {
|
|
1741
|
-
const { lessonFlow, blueprint, skillPrompt, language, languageDirective, headingDirective } = params;
|
|
1763
|
+
const { lessonFlow, blueprint, skillPrompt, language, languageDirective, headingDirective, groundContext } = params;
|
|
1742
1764
|
const blueprintText = JSON.stringify(
|
|
1743
1765
|
blueprint.map((s) => ({
|
|
1744
1766
|
slideIndex: s.slideIndex,
|
|
@@ -1757,6 +1779,7 @@ function buildHybridDeckPrompts(params) {
|
|
|
1757
1779
|
${p.content}`).join("\n\n") : lessonFlow.rawContent;
|
|
1758
1780
|
const systemPrompt = [
|
|
1759
1781
|
skillPrompt,
|
|
1782
|
+
groundContext || "",
|
|
1760
1783
|
languageDirective,
|
|
1761
1784
|
headingDirective,
|
|
1762
1785
|
`
|
|
@@ -1821,7 +1844,8 @@ async function runHybridPipeline(ctx) {
|
|
|
1821
1844
|
skillPrompt,
|
|
1822
1845
|
language,
|
|
1823
1846
|
languageDirective,
|
|
1824
|
-
headingDirective
|
|
1847
|
+
headingDirective,
|
|
1848
|
+
groundContext: options.groundContext
|
|
1825
1849
|
});
|
|
1826
1850
|
let slides = null;
|
|
1827
1851
|
let lastViolations = [];
|
|
@@ -1970,7 +1994,8 @@ ${p.content}`).join("\n\n") : lessonFlow.rawContent.slice(0, 3500);
|
|
|
1970
1994
|
skillPrompt,
|
|
1971
1995
|
language,
|
|
1972
1996
|
languageDirective,
|
|
1973
|
-
headingDirective
|
|
1997
|
+
headingDirective,
|
|
1998
|
+
groundContext: options.groundContext
|
|
1974
1999
|
});
|
|
1975
2000
|
try {
|
|
1976
2001
|
const batchSlides = await pRetry__default.default(
|
|
@@ -9312,6 +9337,21 @@ var STANDARD_SOT_FILES = [
|
|
|
9312
9337
|
"ART_DIRECTION.md",
|
|
9313
9338
|
"ALIGNMENT_MATRIX.md"
|
|
9314
9339
|
];
|
|
9340
|
+
function atomicWriteFileSync(targetPath, content) {
|
|
9341
|
+
const dir = path3__default.default.dirname(targetPath);
|
|
9342
|
+
if (!fs2__default.default.existsSync(dir)) fs2__default.default.mkdirSync(dir, { recursive: true });
|
|
9343
|
+
const tmpPath = path3__default.default.join(dir, `.${path3__default.default.basename(targetPath)}.${process.pid}.${Date.now()}.${crypto__default.default.randomBytes(4).toString("hex")}.tmp`);
|
|
9344
|
+
try {
|
|
9345
|
+
fs2__default.default.writeFileSync(tmpPath, content, "utf-8");
|
|
9346
|
+
fs2__default.default.renameSync(tmpPath, targetPath);
|
|
9347
|
+
} catch (err) {
|
|
9348
|
+
try {
|
|
9349
|
+
if (fs2__default.default.existsSync(tmpPath)) fs2__default.default.unlinkSync(tmpPath);
|
|
9350
|
+
} catch {
|
|
9351
|
+
}
|
|
9352
|
+
throw err;
|
|
9353
|
+
}
|
|
9354
|
+
}
|
|
9315
9355
|
var FileSystemCurriculumAdapter = class {
|
|
9316
9356
|
baseDir;
|
|
9317
9357
|
constructor(options = {}) {
|
|
@@ -9450,7 +9490,7 @@ var FileSystemCurriculumAdapter = class {
|
|
|
9450
9490
|
if (oldContent !== content) {
|
|
9451
9491
|
const historyDir = path3__default.default.join(projectDir, ".history", relPath);
|
|
9452
9492
|
fs2__default.default.mkdirSync(historyDir, { recursive: true });
|
|
9453
|
-
|
|
9493
|
+
atomicWriteFileSync(path3__default.default.join(historyDir, `${Date.now()}.md`), oldContent);
|
|
9454
9494
|
const versions = fs2__default.default.readdirSync(historyDir).filter((f) => f.endsWith(".md")).sort();
|
|
9455
9495
|
while (versions.length > 10) {
|
|
9456
9496
|
fs2__default.default.unlinkSync(path3__default.default.join(historyDir, versions.shift()));
|
|
@@ -9460,19 +9500,15 @@ var FileSystemCurriculumAdapter = class {
|
|
|
9460
9500
|
console.warn("[FileSystemCurriculumAdapter] version history snapshot failed:", histErr?.message || histErr);
|
|
9461
9501
|
}
|
|
9462
9502
|
}
|
|
9463
|
-
|
|
9503
|
+
atomicWriteFileSync(targetPath, content);
|
|
9464
9504
|
const filename = path3__default.default.basename(relPath);
|
|
9465
9505
|
const match = filename.match(/(LESSON|ACT|QUIZ|SLIDE|GUIDE|HANDOUT|WKS|EXT)_(U\d+_M\d+_L\d+)\.md/i);
|
|
9466
9506
|
if (match) {
|
|
9467
9507
|
const lessonId = match[2].toUpperCase();
|
|
9468
|
-
|
|
9469
|
-
if (!fs2__default.default.existsSync(lessonsDir)) {
|
|
9470
|
-
fs2__default.default.mkdirSync(lessonsDir, { recursive: true });
|
|
9471
|
-
}
|
|
9472
|
-
fs2__default.default.writeFileSync(path3__default.default.join(lessonsDir, filename), content, "utf-8");
|
|
9508
|
+
atomicWriteFileSync(path3__default.default.join(projectDir, "lessons", lessonId, filename), content);
|
|
9473
9509
|
const legacyContentDir = path3__default.default.join(projectDir, "_content", lessonId);
|
|
9474
9510
|
if (fs2__default.default.existsSync(legacyContentDir)) {
|
|
9475
|
-
|
|
9511
|
+
atomicWriteFileSync(path3__default.default.join(legacyContentDir, filename), content);
|
|
9476
9512
|
}
|
|
9477
9513
|
}
|
|
9478
9514
|
}
|
|
@@ -9563,7 +9599,7 @@ ${lessonTable}
|
|
|
9563
9599
|
}
|
|
9564
9600
|
const stateFile = path3__default.default.join(pipelineDir, "state.json");
|
|
9565
9601
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9566
|
-
|
|
9602
|
+
atomicWriteFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
9567
9603
|
}
|
|
9568
9604
|
async updateArtifactState(projectId, taskId, artifactType, update) {
|
|
9569
9605
|
const current = await this.getPipelineState(projectId);
|
|
@@ -11263,6 +11299,13 @@ var DEFAULT_LESSON_PRIORITIES = [
|
|
|
11263
11299
|
"Learning Objectives & Evidence",
|
|
11264
11300
|
"Activity Sequence"
|
|
11265
11301
|
];
|
|
11302
|
+
var SATELLITE_LESSON_PRIORITIES = [
|
|
11303
|
+
"Artifact Contract",
|
|
11304
|
+
"A. Lesson Design Plan",
|
|
11305
|
+
"B. Lesson Flow",
|
|
11306
|
+
"Learning Objectives & Evidence",
|
|
11307
|
+
"Activity Sequence"
|
|
11308
|
+
];
|
|
11266
11309
|
var DEFAULT_KX_PRIORITIES = [
|
|
11267
11310
|
"Key Terms",
|
|
11268
11311
|
"Concept Narratives",
|
|
@@ -11476,6 +11519,21 @@ function normalizeHeading(str) {
|
|
|
11476
11519
|
return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
11477
11520
|
}
|
|
11478
11521
|
function findCanonicalFuzzy(norm) {
|
|
11522
|
+
if (norm.includes("toolchain") || norm.includes("moi truong phat trien") || norm.includes("cong cu") || norm.includes("version") || norm.includes("phan mem") || norm.includes("development environment") || norm.includes("technical overview") || norm.includes("kien truc")) {
|
|
11523
|
+
return "Technical Overview & Architecture Blueprint";
|
|
11524
|
+
}
|
|
11525
|
+
if (norm.includes("pinout") || norm.includes("phan cung") || norm.includes("hardware") || norm.includes("wiring") || norm.includes("ket noi")) {
|
|
11526
|
+
return "Hardware Pinout & Wiring Configuration Matrix";
|
|
11527
|
+
}
|
|
11528
|
+
if (norm.includes("pedagog") || norm.includes("phuong phap") || norm.includes("day hoc") || norm.includes("su pham")) {
|
|
11529
|
+
return "Core Pedagogical Concept Anchor & Real-World Domain Bridge";
|
|
11530
|
+
}
|
|
11531
|
+
if (norm.includes("standard") || norm.includes("tieu chuan") || norm.includes("csta") || norm.includes("cs2023") || norm.includes("chuan academic")) {
|
|
11532
|
+
return "Standards Alignment";
|
|
11533
|
+
}
|
|
11534
|
+
if (norm.includes("roadmap") || norm.includes("lo trinh") || norm.includes("milestone") || norm.includes("giai doan")) {
|
|
11535
|
+
return void 0;
|
|
11536
|
+
}
|
|
11479
11537
|
if (norm.includes("symbol") || norm.includes("identifier ledger") || norm.includes("dinh danh")) {
|
|
11480
11538
|
return "Symbol & Identifier Ledger";
|
|
11481
11539
|
}
|
|
@@ -11612,7 +11670,7 @@ function buildSystemPrompt(targetLanguage = "vi", techStack, hardwarePlatform) {
|
|
|
11612
11670
|
langDirective
|
|
11613
11671
|
].join("\n");
|
|
11614
11672
|
}
|
|
11615
|
-
function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown) {
|
|
11673
|
+
function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown, refPack) {
|
|
11616
11674
|
const depthLines = session.depth_assignments.map((d) => "- " + d.node_id + " [" + d.depth.toUpperCase() + "]: " + DEPTH_RULES[d.depth]).join("\n");
|
|
11617
11675
|
const termLines = glossary.map((g) => "- " + g.term + (g.definition ? " \u2014 " + g.definition : "") + (g.example ? " (example: " + g.example + ")" : "")).join("\n");
|
|
11618
11676
|
const langCode = resolveTargetLanguageCode(targetLanguage);
|
|
@@ -11625,6 +11683,28 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
|
|
|
11625
11683
|
const hQuestions = headings["Self-Check Questions"] || (langCode === "vi" ? "C\xE2u h\u1ECFi T\u1EF1 ki\u1EC3m tra" : "Self-Check Questions");
|
|
11626
11684
|
const skeleton = `## ${hScope} / ## ${hTerms} / ## ${hNarratives} / ## ${hExamples} / ## ${hMistakes} / ## ${hQuestions}`;
|
|
11627
11685
|
const headingDirective = buildHeadingDirective("KNOWLEDGE_EXPOSITION", slcMarkdown, targetLanguage);
|
|
11686
|
+
let refPackBlock = "";
|
|
11687
|
+
if (refPack && refPack.trim()) {
|
|
11688
|
+
const refExcerpt = buildSectionAwareExcerpt(refPack, {
|
|
11689
|
+
priorities: [
|
|
11690
|
+
"Technical Overview & Architecture Blueprint",
|
|
11691
|
+
"Hardware Pinout & Wiring Configuration Matrix",
|
|
11692
|
+
"Standards Alignment"
|
|
11693
|
+
],
|
|
11694
|
+
budget: 2500
|
|
11695
|
+
}).excerpt;
|
|
11696
|
+
if (refExcerpt) {
|
|
11697
|
+
refPackBlock = [
|
|
11698
|
+
"",
|
|
11699
|
+
"[GROUND TRUTH \u2014 BINDING, from REFERENCE_PACK.md]:",
|
|
11700
|
+
"Tool versions, APIs, and platform facts below are CANONICAL. Do NOT",
|
|
11701
|
+
"contradict them; do NOT substitute versions from memory. If a fact you",
|
|
11702
|
+
"need is not stated here, stay generic rather than inventing specifics.",
|
|
11703
|
+
"",
|
|
11704
|
+
refExcerpt
|
|
11705
|
+
].join("\n");
|
|
11706
|
+
}
|
|
11707
|
+
}
|
|
11628
11708
|
return [
|
|
11629
11709
|
"Session: " + session.id + " \u2014 " + session.title,
|
|
11630
11710
|
"Objective: " + session.prose_objective,
|
|
@@ -11637,6 +11717,7 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
|
|
|
11637
11717
|
"",
|
|
11638
11718
|
"Glossary terms (definitions are canonical):",
|
|
11639
11719
|
termLines || "(none provided \u2014 write definitions and mark them for glossary sync)",
|
|
11720
|
+
refPackBlock,
|
|
11640
11721
|
"",
|
|
11641
11722
|
"Section skeleton to fill (replace ONLY the {{placeholders}}):",
|
|
11642
11723
|
skeleton,
|
|
@@ -11645,7 +11726,7 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
|
|
|
11645
11726
|
].filter(Boolean).join("\n");
|
|
11646
11727
|
}
|
|
11647
11728
|
async function ensureKnowledgeExposition(options) {
|
|
11648
|
-
const { projectId, lessonCode, plan, glossary, llmFn, storage, techStack, hardwarePlatform } = options;
|
|
11729
|
+
const { projectId, lessonCode, plan, glossary, llmFn, storage, techStack, hardwarePlatform, refPack } = options;
|
|
11649
11730
|
const session = plan.sessions.find((s) => s.id === lessonCode);
|
|
11650
11731
|
if (!session) throw new Error("Session " + lessonCode + " not found in plan " + plan.plan_id);
|
|
11651
11732
|
const existing = await storage.readArtifact(projectId, EXPOSITION_REL(lessonCode));
|
|
@@ -11659,9 +11740,11 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11659
11740
|
throw new ExpositionApprovalError("Approval hash " + plan.approval.plan_hash + " does not match current plan hash " + plan.plan_hash + " \u2014 re-approve after plan changes");
|
|
11660
11741
|
}
|
|
11661
11742
|
const targetLanguage = options.targetLanguage || plan.translation?.target_language || "vi";
|
|
11743
|
+
const originalSystemPrompt = buildSystemPrompt(targetLanguage, techStack, hardwarePlatform);
|
|
11744
|
+
const originalUserPrompt = buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown, refPack);
|
|
11662
11745
|
const content = (await llmFn(
|
|
11663
|
-
|
|
11664
|
-
|
|
11746
|
+
originalSystemPrompt,
|
|
11747
|
+
originalUserPrompt
|
|
11665
11748
|
)).trim();
|
|
11666
11749
|
if (content.length < 200) {
|
|
11667
11750
|
throw new Error("EXPOSITION too short for " + lessonCode + " (" + content.length + " chars) \u2014 refusing to save");
|
|
@@ -11694,8 +11777,16 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11694
11777
|
};
|
|
11695
11778
|
let verdict = await judgeOnce(finalContent);
|
|
11696
11779
|
if (verdict.verdict !== "APPROVED") {
|
|
11697
|
-
const repairPrompt =
|
|
11698
|
-
|
|
11780
|
+
const repairPrompt = [
|
|
11781
|
+
originalUserPrompt,
|
|
11782
|
+
"",
|
|
11783
|
+
"--- YOUR PREVIOUS DRAFT (REJECTED, fix ALL issues below) ---",
|
|
11784
|
+
finalContent,
|
|
11785
|
+
"",
|
|
11786
|
+
"--- JUDGE CRITIQUE (fix ALL issues, output the COMPLETE corrected document) ---",
|
|
11787
|
+
verdict.critique
|
|
11788
|
+
].join("\n");
|
|
11789
|
+
finalContent = (await llmFn(originalSystemPrompt, repairPrompt)).trim();
|
|
11699
11790
|
verdict = await judgeOnce(finalContent);
|
|
11700
11791
|
}
|
|
11701
11792
|
if (verdict.verdict !== "APPROVED") {
|
|
@@ -11736,12 +11827,27 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
|
|
|
11736
11827
|
try {
|
|
11737
11828
|
const lpRaw = await storage.readSotDocument(projectId, "LEARNER_PROFILE.md");
|
|
11738
11829
|
if (lpRaw) {
|
|
11739
|
-
const
|
|
11740
|
-
|
|
11830
|
+
const hwPatterns = [
|
|
11831
|
+
/(?:Student Equipment|Hardware|Platform|Thiết bị|Thiết bị học tập)\s*(?:\(|:|\*)*\s*([^\n\r]+?)(?:\s*\*\*|[.).]?\s*$)/i,
|
|
11832
|
+
/(?:máy|device|machine)\s+([^,.;\n]{4,60}(?:M\d|Intel|PC|computer|mini)[^,.;\n]{0,30})/i
|
|
11833
|
+
];
|
|
11834
|
+
for (const p of hwPatterns) {
|
|
11835
|
+
const m = lpRaw.match(p);
|
|
11836
|
+
if (m && !hw) {
|
|
11837
|
+
hw = m[1].trim();
|
|
11838
|
+
break;
|
|
11839
|
+
}
|
|
11840
|
+
}
|
|
11741
11841
|
}
|
|
11742
11842
|
} catch {
|
|
11743
11843
|
}
|
|
11744
11844
|
}
|
|
11845
|
+
let refPackContent;
|
|
11846
|
+
try {
|
|
11847
|
+
refPackContent = await storage.readSotDocument(projectId, "REFERENCE_PACK.md") || void 0;
|
|
11848
|
+
} catch {
|
|
11849
|
+
refPackContent = void 0;
|
|
11850
|
+
}
|
|
11745
11851
|
const { runCurriculumAIInference: runCurriculumAIInference2 } = await Promise.resolve().then(() => (init_streamRunner(), streamRunner_exports));
|
|
11746
11852
|
const targetLang = options.targetLanguage || plan.translation?.target_language || "vi";
|
|
11747
11853
|
const result = await ensureKnowledgeExposition({
|
|
@@ -11753,6 +11859,7 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
|
|
|
11753
11859
|
slcMarkdown: options.slcMarkdown,
|
|
11754
11860
|
techStack: tech,
|
|
11755
11861
|
hardwarePlatform: hw,
|
|
11862
|
+
refPack: refPackContent,
|
|
11756
11863
|
llmFn: async (systemPrompt, userPrompt) => {
|
|
11757
11864
|
const runnerOpts = options.customInference ? { customInference: options.customInference } : {};
|
|
11758
11865
|
const out = await runCurriculumAIInference2(
|
|
@@ -26051,11 +26158,16 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
|
|
|
26051
26158
|
}).excerpt : "";
|
|
26052
26159
|
let effectiveRefPack = refPack ? buildSectionAwareExcerpt(refPack, {
|
|
26053
26160
|
priorities: [
|
|
26161
|
+
// Toolchain/version matrix FIRST — the most-frequently-hallucinated
|
|
26162
|
+
// facts are tool versions ("Xcode 15" vs ground truth "Xcode 16").
|
|
26163
|
+
// The bilingual fuzzy matcher in contextBuilder resolves these keys
|
|
26164
|
+
// even when the generated RefPack headings are Vietnamese.
|
|
26054
26165
|
"Technical Overview & Architecture Blueprint",
|
|
26055
26166
|
"Hardware Pinout & Wiring Configuration Matrix",
|
|
26167
|
+
"Standards Alignment",
|
|
26056
26168
|
"Core Pedagogical Concept Anchor & Real-World Domain Bridge"
|
|
26057
26169
|
],
|
|
26058
|
-
budget:
|
|
26170
|
+
budget: 3500
|
|
26059
26171
|
}).excerpt : "";
|
|
26060
26172
|
const baseContextPrefix = `PROJECT & ACADEMIC CONTEXT:
|
|
26061
26173
|
- Project ID: ${projectId}
|
|
@@ -26094,6 +26206,21 @@ ${renderHorizonPromptBlock(horizon)}`;
|
|
|
26094
26206
|
} catch (hErr) {
|
|
26095
26207
|
console.warn(`[produceSingleLesson] Notice: Curriculum Horizon extraction:`, hErr?.message || hErr);
|
|
26096
26208
|
}
|
|
26209
|
+
const buildGroundTruthBlock = () => {
|
|
26210
|
+
const parts = [];
|
|
26211
|
+
if (expositionContext) {
|
|
26212
|
+
parts.push(`### KNOWLEDGE_EXPOSITION (CANONICAL KNOWLEDGE \u2014 teach from this, do not contradict)
|
|
26213
|
+
${expositionContext}`);
|
|
26214
|
+
}
|
|
26215
|
+
if (effectiveRefPack) {
|
|
26216
|
+
parts.push(`### REFERENCE PACK GROUND TRUTH
|
|
26217
|
+
${effectiveRefPack}`);
|
|
26218
|
+
}
|
|
26219
|
+
return parts.length > 0 ? `
|
|
26220
|
+
|
|
26221
|
+
[GROUND TRUTH]:
|
|
26222
|
+
${parts.join("\n\n")}` : "";
|
|
26223
|
+
};
|
|
26097
26224
|
const glossaryBlock = glossaryContext ? `
|
|
26098
26225
|
|
|
26099
26226
|
[GLOSSARY TERMS (use these exact definitions)]:
|
|
@@ -26101,21 +26228,14 @@ ${glossaryContext}` : "";
|
|
|
26101
26228
|
const standardsBlock = standardsContext ? `
|
|
26102
26229
|
|
|
26103
26230
|
${standardsContext}` : "";
|
|
26104
|
-
const expositionBlock = expositionContext ? `
|
|
26105
|
-
|
|
26106
|
-
[KNOWLEDGE_EXPOSITION (CANONICAL KNOWLEDGE \u2014 teach from this, do not contradict)]:
|
|
26107
|
-
${expositionContext}` : "";
|
|
26108
26231
|
const sessionSliceBlock = sessionSliceContext ? `
|
|
26109
26232
|
|
|
26110
26233
|
${sessionSliceContext}` : "";
|
|
26111
|
-
const assembleCommonContext = (
|
|
26234
|
+
const assembleCommonContext = (sg) => `${baseContextPrefix}
|
|
26112
26235
|
|
|
26113
26236
|
[CONTENT STYLE GUIDE EXCERPT]:
|
|
26114
|
-
${sg}
|
|
26115
|
-
|
|
26116
|
-
[REFERENCE PACK GROUND TRUTH]:
|
|
26117
|
-
${ref}${standardsBlock}${glossaryBlock}${expositionBlock}${sessionSliceBlock}${horizonBlock}`;
|
|
26118
|
-
let commonContext = assembleCommonContext(effectiveRefPack, effectiveStyleGuide);
|
|
26237
|
+
${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock()}${sessionSliceBlock}${horizonBlock}`;
|
|
26238
|
+
let commonContext = assembleCommonContext(effectiveStyleGuide);
|
|
26119
26239
|
const MAX_COMPOSITE_PROMPT_CHARS = 3e4;
|
|
26120
26240
|
const runnerOptions = options.customInference ? { customInference: options.customInference } : {};
|
|
26121
26241
|
if (commonContext.length > MAX_COMPOSITE_PROMPT_CHARS && effectiveRefPack.length > 1e3) {
|
|
@@ -26126,14 +26246,14 @@ ${ref}${standardsBlock}${glossaryBlock}${expositionBlock}${sessionSliceBlock}${h
|
|
|
26126
26246
|
],
|
|
26127
26247
|
budget: 1e3
|
|
26128
26248
|
}).excerpt;
|
|
26129
|
-
commonContext = assembleCommonContext(
|
|
26249
|
+
commonContext = assembleCommonContext(effectiveStyleGuide);
|
|
26130
26250
|
}
|
|
26131
26251
|
if (commonContext.length > MAX_COMPOSITE_PROMPT_CHARS && effectiveStyleGuide.length > 1e3) {
|
|
26132
26252
|
effectiveStyleGuide = buildSectionAwareExcerpt(styleGuide, {
|
|
26133
26253
|
priorities: ["Voice & Tone", "Standard Terminology (Glossary)"],
|
|
26134
26254
|
budget: 1e3
|
|
26135
26255
|
}).excerpt;
|
|
26136
|
-
commonContext = assembleCommonContext(
|
|
26256
|
+
commonContext = assembleCommonContext(effectiveStyleGuide);
|
|
26137
26257
|
}
|
|
26138
26258
|
onProgress?.("@content", `[CONTEXT] Composite prompt ready (${commonContext.length} chars, adaptive budget <= ${MAX_COMPOSITE_PROMPT_CHARS})`, {
|
|
26139
26259
|
type: "progress",
|
|
@@ -26484,7 +26604,7 @@ ${currentContent}` }],
|
|
|
26484
26604
|
});
|
|
26485
26605
|
}
|
|
26486
26606
|
const lessonExcerptResult = buildSectionAwareExcerpt(lessonContent, {
|
|
26487
|
-
priorities:
|
|
26607
|
+
priorities: SATELLITE_LESSON_PRIORITIES,
|
|
26488
26608
|
budget: 12e3,
|
|
26489
26609
|
sectionLanguageContract: slcMarkdown,
|
|
26490
26610
|
artifactType: "LESSON"
|
|
@@ -26674,6 +26794,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
26674
26794
|
} catch {
|
|
26675
26795
|
}
|
|
26676
26796
|
const { executeSlideProductionWorkflow: executeSlideProductionWorkflow2 } = await Promise.resolve().then(() => (init_slideProductionWorkflow(), slideProductionWorkflow_exports));
|
|
26797
|
+
const slideGroundContext = `${commonContext}${symbolLedgerBlock}`;
|
|
26677
26798
|
const workflowResult = await executeSlideProductionWorkflow2({
|
|
26678
26799
|
lessonMarkdown: lessonContent || "",
|
|
26679
26800
|
lessonCode,
|
|
@@ -26682,6 +26803,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
26682
26803
|
language: targetLang || "Vietnamese",
|
|
26683
26804
|
languageDirective,
|
|
26684
26805
|
headingDirective: buildHeadingDirective("SLIDE", slcMarkdown, targetLang),
|
|
26806
|
+
groundContext: slideGroundContext,
|
|
26685
26807
|
satelliteContext,
|
|
26686
26808
|
runnerOptions,
|
|
26687
26809
|
onProgress: (agent, msg, meta) => {
|
|
@@ -26690,7 +26812,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
26690
26812
|
});
|
|
26691
26813
|
let deckJson = workflowResult.deckJson;
|
|
26692
26814
|
const markdownWrapper = workflowResult.markdownWrapper;
|
|
26693
|
-
let validation = presentationKitAi?.validateHtmlSlideDeck ? presentationKitAi.validateHtmlSlideDeck(deckJson, true) : deckJson && Array.isArray(deckJson.slides) ? { valid: true, score: 95, issues: [], slideCount: deckJson.slides.length} : { valid: false, score: 0, issues: [{ message: "Malformed JSON slide deck" }]
|
|
26815
|
+
let validation = presentationKitAi?.validateHtmlSlideDeck ? presentationKitAi.validateHtmlSlideDeck(deckJson, true) : deckJson && Array.isArray(deckJson.slides) ? { valid: true, score: 95, issues: [], slideCount: deckJson.slides.length} : { valid: false, score: 0, issues: [{ message: "Malformed JSON slide deck" }]};
|
|
26694
26816
|
await storage.saveArtifact(projectId, slideRelPath, markdownWrapper);
|
|
26695
26817
|
producedArtifacts.push(`SLIDE_${lessonCode}.md`);
|
|
26696
26818
|
if (deckJson) {
|
|
@@ -26710,21 +26832,45 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
26710
26832
|
}
|
|
26711
26833
|
}
|
|
26712
26834
|
}
|
|
26713
|
-
const
|
|
26714
|
-
const
|
|
26715
|
-
|
|
26716
|
-
|
|
26717
|
-
|
|
26718
|
-
|
|
26719
|
-
|
|
26720
|
-
|
|
26721
|
-
|
|
26722
|
-
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
|
|
26727
|
-
|
|
26835
|
+
const structuralOk = Boolean(validation.valid && (validation.score ?? 100) >= 80);
|
|
26836
|
+
const structuralScore = validation.score ?? (structuralOk ? 95 : 50);
|
|
26837
|
+
const gateMode = gateModeFor(gates, "SLIDE");
|
|
26838
|
+
if (!structuralOk) {
|
|
26839
|
+
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
26840
|
+
state: "rejected",
|
|
26841
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26842
|
+
contentHash: computeContentHash(markdownWrapper),
|
|
26843
|
+
review: {
|
|
26844
|
+
decision: "NEEDS_REVISION",
|
|
26845
|
+
reviewedBy: "@heuristic-linter",
|
|
26846
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26847
|
+
score: structuralScore,
|
|
26848
|
+
critique: `HTML Slide Deck failed schema validation: ${(validation.issues || []).map((i) => i.message).join("; ")}`
|
|
26849
|
+
}
|
|
26850
|
+
});
|
|
26851
|
+
onProgress?.("@reviewer", `\u26A0\uFE0F SLIDE structural pre-check FAILED (${structuralScore}/100) [html-deck schema issues]`);
|
|
26852
|
+
} else {
|
|
26853
|
+
await storage.updateArtifactState(projectId, lessonCode, "SLIDE", {
|
|
26854
|
+
state: gateMode === "LLM_JUDGE" ? "pending" : "completed",
|
|
26855
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26856
|
+
contentHash: computeContentHash(markdownWrapper)
|
|
26857
|
+
});
|
|
26858
|
+
onProgress?.("@reviewer", gateMode === "LLM_JUDGE" ? `\u2705 SLIDE structural pre-check PASS (${structuralScore}/100) \u2014 handing content review to LLM-as-Judge gate` : `\u2705 SLIDE structural pre-check PASS (${structuralScore}/100) [gate=${gateMode}, no LLM judge]`);
|
|
26859
|
+
const deckTextForJudge = Array.isArray(deckJson?.slides) ? deckJson.slides.map((s, i) => {
|
|
26860
|
+
const slotLines = Object.entries(s.slots || {}).map(([k, v]) => {
|
|
26861
|
+
if (Array.isArray(v)) return `- ${k}:
|
|
26862
|
+
${v.map((item) => typeof item === "object" ? ` - ${JSON.stringify(item)}` : ` - ${item}`).join("\n")}`;
|
|
26863
|
+
if (v && typeof v === "object") return `- ${k}: ${JSON.stringify(v, null, 1)}`;
|
|
26864
|
+
return `- ${k}: ${v}`;
|
|
26865
|
+
}).join("\n");
|
|
26866
|
+
return `## Slide ${i + 1} [${s.layoutId}]
|
|
26867
|
+
${slotLines}
|
|
26868
|
+
|
|
26869
|
+
Presenter Notes:
|
|
26870
|
+
${s.notes || "(none)"}`;
|
|
26871
|
+
}).join("\n\n") : String(markdownWrapper);
|
|
26872
|
+
await judgeSat("SLIDE", deckTextForJudge);
|
|
26873
|
+
}
|
|
26728
26874
|
} else {
|
|
26729
26875
|
const canonicalSlideTemplate = loadCurriculumTemplate("SLIDE_template.md");
|
|
26730
26876
|
const templateScaffold = canonicalSlideTemplate || `---
|
|
@@ -31873,6 +32019,7 @@ exports.RoadmapInputSchema = RoadmapInputSchema;
|
|
|
31873
32019
|
exports.RotationStationSchema = RotationStationSchema;
|
|
31874
32020
|
exports.RubricCriteriaSchema = RubricCriteriaSchema;
|
|
31875
32021
|
exports.RubricSchema = RubricSchema;
|
|
32022
|
+
exports.SATELLITE_LESSON_PRIORITIES = SATELLITE_LESSON_PRIORITIES;
|
|
31876
32023
|
exports.SCIENCE_LAB_TEMPLATE = SCIENCE_LAB_TEMPLATE;
|
|
31877
32024
|
exports.SELF_LAB_TEMPLATE = SELF_LAB_TEMPLATE;
|
|
31878
32025
|
exports.SLIDE_CANONICAL_METHODOLOGY = SLIDE_CANONICAL_METHODOLOGY;
|
|
@@ -31922,6 +32069,7 @@ exports.analystTools = analystTools;
|
|
|
31922
32069
|
exports.analyzeProjectCreationIntent = analyzeProjectCreationIntent;
|
|
31923
32070
|
exports.assertAcyclic = assertAcyclic;
|
|
31924
32071
|
exports.assessorTools = assessorTools;
|
|
32072
|
+
exports.atomicWriteFileSync = atomicWriteFileSync;
|
|
31925
32073
|
exports.auditCurriculumQualityFlow = auditCurriculumQualityFlow;
|
|
31926
32074
|
exports.auditQualityReport = auditQualityReport;
|
|
31927
32075
|
exports.buildActivityPrompt = buildActivityPrompt;
|
|
@@ -31969,6 +32117,7 @@ exports.createStreamChunkExtractor = createStreamChunkExtractor;
|
|
|
31969
32117
|
exports.curateMediaLedger = curateMediaLedger;
|
|
31970
32118
|
exports.designerTools = designerTools;
|
|
31971
32119
|
exports.detectProjectPedagogy = detectProjectPedagogy;
|
|
32120
|
+
exports.emitUsage = emitUsage;
|
|
31972
32121
|
exports.ensureExpositionForLesson = ensureExpositionForLesson;
|
|
31973
32122
|
exports.ensureKnowledgeExposition = ensureKnowledgeExposition;
|
|
31974
32123
|
exports.evaluateStandardsCoverage = evaluateStandardsCoverage;
|