@thanh01.pmt/curriculum-kit 1.4.24 → 1.4.26
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 +72 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.mjs +72 -25
- package/dist/index.mjs.map +1 -1
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1076,6 +1076,10 @@ interface SlideProductionWorkflowOptions {
|
|
|
1076
1076
|
* already injected into user prompts via parseLessonFlow (no duplication).
|
|
1077
1077
|
*/
|
|
1078
1078
|
groundContext?: string;
|
|
1079
|
+
/** Canonical PRODUCT_SPEC block (prompt audit S-1): injected into the
|
|
1080
|
+
* blueprint prompt so deck-wide content decisions see binding platform/
|
|
1081
|
+
* version facts even though the blueprint call carries no groundContext. */
|
|
1082
|
+
productSpecBlock?: string;
|
|
1079
1083
|
satelliteContext?: any;
|
|
1080
1084
|
/** Vercel AI SDK model resolution options (provider/model/apiKey). */
|
|
1081
1085
|
modelOptions?: ModelResolutionOptions;
|
package/dist/index.d.ts
CHANGED
|
@@ -1076,6 +1076,10 @@ interface SlideProductionWorkflowOptions {
|
|
|
1076
1076
|
* already injected into user prompts via parseLessonFlow (no duplication).
|
|
1077
1077
|
*/
|
|
1078
1078
|
groundContext?: string;
|
|
1079
|
+
/** Canonical PRODUCT_SPEC block (prompt audit S-1): injected into the
|
|
1080
|
+
* blueprint prompt so deck-wide content decisions see binding platform/
|
|
1081
|
+
* version facts even though the blueprint call carries no groundContext. */
|
|
1082
|
+
productSpecBlock?: string;
|
|
1079
1083
|
satelliteContext?: any;
|
|
1080
1084
|
/** Vercel AI SDK model resolution options (provider/model/apiKey). */
|
|
1081
1085
|
modelOptions?: ModelResolutionOptions;
|
package/dist/index.mjs
CHANGED
|
@@ -1324,6 +1324,10 @@ function parseLessonFlow(lessonMarkdown) {
|
|
|
1324
1324
|
}
|
|
1325
1325
|
const durMatch = lessonMarkdown.match(/Estimated Duration:\s*([^\n]+)/i);
|
|
1326
1326
|
if (durMatch) result.estimatedDuration = durMatch[1].trim();
|
|
1327
|
+
if (!result.lessonTitle) {
|
|
1328
|
+
const h1 = lessonMarkdown.match(/^#\s+(?:LESSON[^:]*:\s*)?(.+)$/m);
|
|
1329
|
+
if (h1 && h1[1]) result.lessonTitle = h1[1].replace(/\*+/g, "").trim();
|
|
1330
|
+
}
|
|
1327
1331
|
const contractMatch = lessonMarkdown.match(/SLIDE must visualize:?\s*([^\n]+)/i);
|
|
1328
1332
|
if (contractMatch) result.slideContract = contractMatch[1].trim();
|
|
1329
1333
|
const actSeqSectionMatch = lessonMarkdown.match(/###\s*(?:\d+\.\s*)?Activity Sequence[\s\S]*?(?=(?:###|\n##\s+|$))/i);
|
|
@@ -1405,7 +1409,7 @@ var init_lessonFlowParser = __esm({
|
|
|
1405
1409
|
|
|
1406
1410
|
// src/ai/prompts/slideBlueprintPrompt.ts
|
|
1407
1411
|
function buildSlideBlueprintPrompt(params) {
|
|
1408
|
-
const { lessonFlow, targetSlideCount, stylePresetName = "Blue Professional" } = params;
|
|
1412
|
+
const { lessonFlow, targetSlideCount, stylePresetName = "Blue Professional", productSpecBlock } = params;
|
|
1409
1413
|
const activitiesSummary = lessonFlow.activities.map(
|
|
1410
1414
|
(a) => `Seq ${a.seq} [${a.phase}]: ${a.purpose} (Teacher: ${a.teacherMove} | Student: ${a.studentAction} | Time: ${a.time})`
|
|
1411
1415
|
).join("\n");
|
|
@@ -1427,7 +1431,9 @@ ${countGuidance}
|
|
|
1427
1431
|
- Target Duration: ${lessonFlow.estimatedDuration}
|
|
1428
1432
|
- Active Visual Style: "${stylePresetName}"
|
|
1429
1433
|
${lessonFlow.slideContract ? `- Slide Artifact Contract: "${lessonFlow.slideContract}"` : ""}
|
|
1430
|
-
|
|
1434
|
+
${productSpecBlock ? `
|
|
1435
|
+
${productSpecBlock}
|
|
1436
|
+
` : ""}
|
|
1431
1437
|
### \u{1F5FA}\uFE0F CANONICAL ACTIVITY SEQUENCE:
|
|
1432
1438
|
${activitiesSummary || "Standard phased sequence"}
|
|
1433
1439
|
|
|
@@ -1501,7 +1507,7 @@ function buildSlideBatchPrompt(params) {
|
|
|
1501
1507
|
skillPrompt,
|
|
1502
1508
|
language = "Vietnamese",
|
|
1503
1509
|
languageDirective = "",
|
|
1504
|
-
|
|
1510
|
+
// deprecated — see interface doc
|
|
1505
1511
|
groundContext = ""
|
|
1506
1512
|
} = params;
|
|
1507
1513
|
const slidesSpec = clusterSlides.map((s) => `
|
|
@@ -1522,7 +1528,6 @@ ${groundContext}
|
|
|
1522
1528
|
---` : ""}
|
|
1523
1529
|
### OPERATIONAL GROUND RULES FOR CHUNK GENERATION:
|
|
1524
1530
|
${languageDirective}
|
|
1525
|
-
${headingDirective}
|
|
1526
1531
|
1. **FOCUS ON CLUSTER ${clusterId}: "${clusterTitle}"**:
|
|
1527
1532
|
Generate EXACTLY the ${clusterSlides.length} slides requested in the user prompt.
|
|
1528
1533
|
2. **ZERO ABBREVIATION / NO "// TODO"**:
|
|
@@ -1713,7 +1718,7 @@ async function inferStructured(schema, systemPrompt, userPrompt, options, label)
|
|
|
1713
1718
|
const messages = [
|
|
1714
1719
|
{ role: "user", content: [systemPrompt, userPrompt].filter(Boolean).join("\n\n") }
|
|
1715
1720
|
];
|
|
1716
|
-
const raw = await runCurriculumAIInference(messages,
|
|
1721
|
+
const raw = await runCurriculumAIInference(messages, "", options.runnerOptions, (token, type) => {
|
|
1717
1722
|
if (type === "usage") options.onProgress?.("@illustrator", token, { type: "usage" });
|
|
1718
1723
|
});
|
|
1719
1724
|
const fenceMatch = raw.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
@@ -1899,7 +1904,8 @@ async function executeSlideProductionWorkflow(options) {
|
|
|
1899
1904
|
const blueprintPrompt = buildSlideBlueprintPrompt({
|
|
1900
1905
|
lessonFlow,
|
|
1901
1906
|
targetSlideCount,
|
|
1902
|
-
stylePresetName: stylePreset?.name || "Blue Professional"
|
|
1907
|
+
stylePresetName: stylePreset?.name || "Blue Professional",
|
|
1908
|
+
productSpecBlock: options.productSpecBlock
|
|
1903
1909
|
});
|
|
1904
1910
|
let blueprintItems;
|
|
1905
1911
|
let allGeneratedSlides;
|
|
@@ -1982,7 +1988,6 @@ ${p.content}`).join("\n\n") : lessonFlow.rawContent.slice(0, 3500);
|
|
|
1982
1988
|
skillPrompt,
|
|
1983
1989
|
language,
|
|
1984
1990
|
languageDirective,
|
|
1985
|
-
headingDirective,
|
|
1986
1991
|
groundContext: options.groundContext
|
|
1987
1992
|
});
|
|
1988
1993
|
try {
|
|
@@ -11707,19 +11712,30 @@ function extractToolchainFacts(refPack) {
|
|
|
11707
11712
|
const factLines = [];
|
|
11708
11713
|
for (const rawLine of excerpt.split("\n")) {
|
|
11709
11714
|
const line = rawLine.trim();
|
|
11710
|
-
if (!line || line.startsWith("#")) continue;
|
|
11711
|
-
if (
|
|
11712
|
-
|
|
11713
|
-
}
|
|
11715
|
+
if (!line || line.startsWith("#") || line.startsWith("<!--")) continue;
|
|
11716
|
+
if (!/\d{1,2}(?:\.\d{1,2})?/.test(line) && !/Xcode|Swift|macOS|iOS|SDK|Node|Python|JDK|\.NET/i.test(line)) continue;
|
|
11717
|
+
factLines.push(line.replace(/^[*\-•]\s*/, "- "));
|
|
11714
11718
|
if (factLines.length >= 8) break;
|
|
11715
11719
|
}
|
|
11716
11720
|
return factLines;
|
|
11717
11721
|
}
|
|
11722
|
+
function derivePrimaryTechnology(facts, briefing) {
|
|
11723
|
+
const explicit = (briefing?.coreTechnology || briefing?.techStack || "").trim();
|
|
11724
|
+
if (explicit && explicit.length <= 80) return explicit;
|
|
11725
|
+
const found = [];
|
|
11726
|
+
const CANON = ["Swift", "SwiftUI", "macOS", "iOS", "Xcode", "SwiftData", "Node.js", "TypeScript", "Python", "React"];
|
|
11727
|
+
for (const name of CANON) {
|
|
11728
|
+
if (facts.some((f) => f.includes(name)) && !found.includes(name)) found.push(name);
|
|
11729
|
+
}
|
|
11730
|
+
if (found.length > 0) return found.slice(0, 4).join(" + ");
|
|
11731
|
+
const topic = (briefing?.topic || "").trim();
|
|
11732
|
+
return topic && topic.length <= 60 ? topic : "";
|
|
11733
|
+
}
|
|
11718
11734
|
function buildProductSpecBlock(input) {
|
|
11719
11735
|
const b = input.briefing || {};
|
|
11720
|
-
const platform = (input.overrides?.primaryLanguage || b.coreTechnology || b.techStack || b.topic || "").trim();
|
|
11721
|
-
const hardware = (b.hardwarePlatform || b.studentEquipment || b.classDynamic || "").trim();
|
|
11722
11736
|
const toolchainFacts = input.refPack ? extractToolchainFacts(input.refPack) : [];
|
|
11737
|
+
const platform = (input.overrides?.primaryLanguage || derivePrimaryTechnology(toolchainFacts, b) || "").trim();
|
|
11738
|
+
const hardware = (b.hardwarePlatform || b.studentEquipment || b.classDynamic || "").trim();
|
|
11723
11739
|
const lines = [];
|
|
11724
11740
|
lines.push("[PRODUCT SPEC \u2014 CANONICAL, BINDING FOR EVERY ARTIFACT OF THIS PROJECT]:");
|
|
11725
11741
|
lines.push("All decisions below are PROJECT-WIDE. Every artifact (LESSON, satellites, KX)");
|
|
@@ -11735,7 +11751,7 @@ function buildProductSpecBlock(input) {
|
|
|
11735
11751
|
if (input.overrides?.deliverableTemplate) {
|
|
11736
11752
|
lines.push(`- Deliverable template: ${input.overrides.deliverableTemplate}`);
|
|
11737
11753
|
}
|
|
11738
|
-
if (toolchainFacts.length > 0) {
|
|
11754
|
+
if (toolchainFacts.length > 0 && !input.skipToolchainList) {
|
|
11739
11755
|
lines.push("- Toolchain & versions (ground truth):");
|
|
11740
11756
|
for (const f of toolchainFacts) lines.push(` ${f}`);
|
|
11741
11757
|
}
|
|
@@ -11863,7 +11879,9 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11863
11879
|
const productSpecBlock = buildProductSpecBlock({
|
|
11864
11880
|
refPack,
|
|
11865
11881
|
briefing: techStack || hardwarePlatform ? { techStack, hardwarePlatform } : void 0,
|
|
11866
|
-
overrides: options.productSpec
|
|
11882
|
+
overrides: options.productSpec,
|
|
11883
|
+
skipToolchainList: true
|
|
11884
|
+
// same RefPack excerpt already in [GROUND TRUTH]
|
|
11867
11885
|
});
|
|
11868
11886
|
const originalUserPrompt = buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown, refPack) + (productSpecBlock ? "\n\n" + productSpecBlock : "");
|
|
11869
11887
|
const content = (await llmFn(
|
|
@@ -11963,17 +11981,29 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
|
|
|
11963
11981
|
const lpRaw = await storage.readSotDocument(projectId, "LEARNER_PROFILE.md");
|
|
11964
11982
|
if (lpRaw) {
|
|
11965
11983
|
const hwPatterns = [
|
|
11984
|
+
// "Cơ sở phần cứng (Hardware Ecosystem): Học sinh thực hành trên máy..."
|
|
11985
|
+
// — the audit: the old pattern captured the parenthetical label
|
|
11986
|
+
// "(Hardware Ecosystem):" itself. Capture AFTER the closing paren + colon.
|
|
11987
|
+
/\([^)]*Ecosystem[^)]*\)\s*:\s*([^\n\r]+)/i,
|
|
11966
11988
|
/(?:Student Equipment|Hardware|Platform|Thiết bị|Thiết bị học tập)\s*(?:\(|:|\*)*\s*([^\n\r]+?)(?:\s*\*\*|[.).]?\s*$)/i,
|
|
11967
11989
|
/(?:máy|device|machine)\s+([^,.;\n]{4,60}(?:M\d|Intel|PC|computer|mini)[^,.;\n]{0,30})/i
|
|
11968
11990
|
];
|
|
11969
11991
|
for (const p of hwPatterns) {
|
|
11970
11992
|
const m = lpRaw.match(p);
|
|
11971
|
-
if (m && !hw) {
|
|
11972
|
-
|
|
11993
|
+
if (m && m[1] && !hw) {
|
|
11994
|
+
const firstSentence = m[1].split(/(?<=[.!?])\s/)[0] || m[1];
|
|
11995
|
+
hw = firstSentence.replace(/\*+/g, "").replace(/\s+/g, " ").trim().slice(0, 140);
|
|
11973
11996
|
break;
|
|
11974
11997
|
}
|
|
11975
11998
|
}
|
|
11976
11999
|
}
|
|
12000
|
+
if (!hw) {
|
|
12001
|
+
const rpRaw = await storage.readSotDocument(projectId, "REFERENCE_PACK.md");
|
|
12002
|
+
if (rpRaw) {
|
|
12003
|
+
const rp = rpRaw.match(/(?:School Lab|Thiết bị lớp học)[^\n]*\*\*([^*]+(?:M\d|Mac)[^*]*)\*\*/i);
|
|
12004
|
+
if (rp && rp[1]) hw = rp[1].trim();
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
11977
12007
|
} catch {
|
|
11978
12008
|
}
|
|
11979
12009
|
}
|
|
@@ -12710,11 +12740,11 @@ function buildSessionSliceContext(plan, lessonCode) {
|
|
|
12710
12740
|
lines.push("- Time split: knowledge " + s.knowledge_minutes + "m / practice " + s.practice_minutes + "m / overhead " + s.overhead_minutes + "m");
|
|
12711
12741
|
lines.push("- Depth assignments: " + s.depth_assignments.map((d) => d.node_id + "=" + d.depth.toUpperCase() + "(" + d.source + ")").join("; "));
|
|
12712
12742
|
if (s.prerequisite_decisions.length > 0) {
|
|
12713
|
-
lines.push("- Prerequisite decisions [
|
|
12743
|
+
lines.push("- Prerequisite decisions [" + s.prerequisite_decisions.length + "]:");
|
|
12714
12744
|
for (const d of s.prerequisite_decisions) lines.push(" * " + d.node_id + ": " + d.decision + " \u2014 " + d.reason);
|
|
12715
12745
|
}
|
|
12716
12746
|
if (s.scaffold_decisions.length > 0) {
|
|
12717
|
-
lines.push("- Scaffold decisions [
|
|
12747
|
+
lines.push("- Scaffold decisions [" + s.scaffold_decisions.length + "]:");
|
|
12718
12748
|
for (const d of s.scaffold_decisions) lines.push(" * " + d.item + ": " + d.decision + (d.minutes_saved > 0 ? " (saves " + d.minutes_saved + "m)" : "") + " \u2014 " + d.reason);
|
|
12719
12749
|
}
|
|
12720
12750
|
lines.push("- Exit evidence: " + s.exit_evidence.join("; "));
|
|
@@ -25949,8 +25979,11 @@ function buildFrameworkExcerptForLesson(framework, lessonId) {
|
|
|
25949
25979
|
});
|
|
25950
25980
|
const rowIdx = scopeRows[0] ?? -1;
|
|
25951
25981
|
const currentRow = rowIdx >= 0 ? lines[rowIdx] : "";
|
|
25952
|
-
const
|
|
25953
|
-
const
|
|
25982
|
+
const isSeparatorRow = (l) => !!l && /^\s*\|?[\s:|-]+\|?\s*$/.test(l) && l.includes("-");
|
|
25983
|
+
const prevRaw = rowIdx > 0 ? lines[rowIdx - 1] : "";
|
|
25984
|
+
const prevRow = prevRaw.trim().startsWith("|") && !isSeparatorRow(prevRaw) ? prevRaw : "";
|
|
25985
|
+
const nextRaw = lines[rowIdx + 1] || "";
|
|
25986
|
+
const nextRow = nextRaw.trim().startsWith("|") && !isSeparatorRow(nextRaw) ? nextRaw : "";
|
|
25954
25987
|
const hierStart = lines.findIndex((l) => /Structural Hierarchy/i.test(l));
|
|
25955
25988
|
let hierarchyBlock = "";
|
|
25956
25989
|
if (hierStart >= 0) {
|
|
@@ -26356,6 +26389,7 @@ ${renderHorizonPromptBlock(horizon)}`;
|
|
|
26356
26389
|
} catch (hErr) {
|
|
26357
26390
|
console.warn(`[produceSingleLesson] Notice: Curriculum Horizon extraction:`, hErr?.message || hErr);
|
|
26358
26391
|
}
|
|
26392
|
+
let productSpecBlock = "";
|
|
26359
26393
|
const buildGroundTruthBlock = () => {
|
|
26360
26394
|
const parts = [];
|
|
26361
26395
|
if (expositionContext) {
|
|
@@ -26366,9 +26400,9 @@ ${expositionContext}`);
|
|
|
26366
26400
|
parts.push(`### REFERENCE PACK GROUND TRUTH
|
|
26367
26401
|
${effectiveRefPack}`);
|
|
26368
26402
|
}
|
|
26369
|
-
|
|
26370
|
-
if (
|
|
26371
|
-
parts.push(
|
|
26403
|
+
productSpecBlock = buildProductSpecBlock({ refPack, briefing: options.briefing, skipToolchainList: true });
|
|
26404
|
+
if (productSpecBlock) {
|
|
26405
|
+
parts.push(productSpecBlock);
|
|
26372
26406
|
}
|
|
26373
26407
|
return parts.length > 0 ? `
|
|
26374
26408
|
|
|
@@ -26385,10 +26419,17 @@ ${standardsContext}` : "";
|
|
|
26385
26419
|
const sessionSliceBlock = sessionSliceContext ? `
|
|
26386
26420
|
|
|
26387
26421
|
${sessionSliceContext}` : "";
|
|
26422
|
+
const platformGuardrail = buildDomainLexiconGuardrail(
|
|
26423
|
+
options.briefing?.techStack || options.briefing?.coreTechnology,
|
|
26424
|
+
options.briefing?.hardwarePlatform
|
|
26425
|
+
);
|
|
26426
|
+
const guardrailBlock = `
|
|
26427
|
+
|
|
26428
|
+
${platformGuardrail}`;
|
|
26388
26429
|
const assembleCommonContext = (sg) => `${baseContextPrefix}
|
|
26389
26430
|
|
|
26390
26431
|
[CONTENT STYLE GUIDE EXCERPT]:
|
|
26391
|
-
${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock()}${sessionSliceBlock}${horizonBlock}`;
|
|
26432
|
+
${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock()}${sessionSliceBlock}${horizonBlock}${guardrailBlock}`;
|
|
26392
26433
|
let commonContext = assembleCommonContext(effectiveStyleGuide);
|
|
26393
26434
|
const MAX_COMPOSITE_PROMPT_CHARS = 3e4;
|
|
26394
26435
|
const runnerOptions = options.customInference ? { customInference: options.customInference } : {};
|
|
@@ -26914,6 +26955,11 @@ ${buildHeadingDirective("ACT", slcMarkdown, targetLang)}`;
|
|
|
26914
26955
|
const quizPrompt = `You are @assessor (Lead Psychometrician & Competency Assessment Specialist).
|
|
26915
26956
|
Based on Master Lesson Plan \`LESSON_${lessonCode}.md\`, author the scenario-based diagnostic question bank: \`QUIZ_${lessonCode}.md\`.
|
|
26916
26957
|
|
|
26958
|
+
SCOPE & PACING (prompt audit S-6):
|
|
26959
|
+
- This is a FORMATIVE CHECKPOINT for ONE 90-minute session, NOT an exam bank.
|
|
26960
|
+
- Size the bank to fit the session's Wrap-up (~10 minutes of answering): the MCQ bank, code-tracing question, and rubric together must be completable by a student in that time.
|
|
26961
|
+
- Question ONLY concepts inside this session's scope (see SESSION SLICE + ALLOWED DESIGN SPACE in the context).
|
|
26962
|
+
|
|
26917
26963
|
Mandatory Format Requirements:
|
|
26918
26964
|
1. EXACT YAML Frontmatter \u2014 copy verbatim, then fill in the blanks:
|
|
26919
26965
|
---
|
|
@@ -26998,6 +27044,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
26998
27044
|
languageDirective,
|
|
26999
27045
|
headingDirective: buildHeadingDirective("SLIDE", slcMarkdown, targetLang),
|
|
27000
27046
|
groundContext: slideGroundContext,
|
|
27047
|
+
productSpecBlock: productSpecBlock || void 0,
|
|
27001
27048
|
satelliteContext,
|
|
27002
27049
|
runnerOptions,
|
|
27003
27050
|
onProgress: (agent, msg, meta) => {
|