@thanh01.pmt/curriculum-kit 1.0.6 → 1.0.7
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-BIhxrUEa.d.cts → index-BEicVaQ9.d.cts} +60 -5
- package/dist/{index-BvWC_xXe.d.ts → index-BfDHBO7f.d.ts} +60 -5
- package/dist/index.cjs +293 -234
- 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 +291 -215
- package/dist/index.mjs.map +1 -1
- package/dist/media/index.cjs +30 -0
- package/dist/media/index.cjs.map +1 -1
- package/dist/media/index.d.cts +8 -0
- package/dist/media/index.d.ts +8 -0
- package/dist/media/index.mjs +30 -0
- package/dist/media/index.mjs.map +1 -1
- package/dist/workflow/index.cjs +460 -47
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.d.cts +2 -1
- package/dist/workflow/index.d.ts +2 -1
- package/dist/workflow/index.mjs +457 -27
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -14,35 +14,16 @@ var supabaseJs = require('@supabase/supabase-js');
|
|
|
14
14
|
var url = require('url');
|
|
15
15
|
var jsonrepair = require('jsonrepair');
|
|
16
16
|
var rest = require('@octokit/rest');
|
|
17
|
-
var
|
|
17
|
+
var workflow = require('workflow');
|
|
18
18
|
|
|
19
19
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
20
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
21
|
|
|
22
|
-
function _interopNamespace(e) {
|
|
23
|
-
if (e && e.__esModule) return e;
|
|
24
|
-
var n = Object.create(null);
|
|
25
|
-
if (e) {
|
|
26
|
-
Object.keys(e).forEach(function (k) {
|
|
27
|
-
if (k !== 'default') {
|
|
28
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
29
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
30
|
-
enumerable: true,
|
|
31
|
-
get: function () { return e[k]; }
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
n.default = e;
|
|
37
|
-
return Object.freeze(n);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
22
|
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
41
23
|
var path3__default = /*#__PURE__*/_interopDefault(path3);
|
|
42
24
|
var pLimit__default = /*#__PURE__*/_interopDefault(pLimit);
|
|
43
25
|
var fsPromises__default = /*#__PURE__*/_interopDefault(fsPromises);
|
|
44
26
|
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
45
|
-
var workflowPkg__namespace = /*#__PURE__*/_interopNamespace(workflowPkg);
|
|
46
27
|
|
|
47
28
|
var __defProp = Object.defineProperty;
|
|
48
29
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -9576,14 +9557,14 @@ init_errors();
|
|
|
9576
9557
|
|
|
9577
9558
|
// src/workflow/gateSettings.ts
|
|
9578
9559
|
var DEFAULT_GATE_SETTINGS = Object.freeze({
|
|
9579
|
-
LEARNER_PROFILE: "
|
|
9580
|
-
PROJECT_BRIEF: "
|
|
9581
|
-
REFERENCE_PACK: "
|
|
9582
|
-
CURRICULUM_FRAMEWORK: "
|
|
9583
|
-
KNOWLEDGE_EXPOSITION: "
|
|
9584
|
-
STYLE_GUIDE: "
|
|
9585
|
-
ART_DIRECTION: "
|
|
9586
|
-
LESSON: "
|
|
9560
|
+
LEARNER_PROFILE: "OFF",
|
|
9561
|
+
PROJECT_BRIEF: "OFF",
|
|
9562
|
+
REFERENCE_PACK: "OFF",
|
|
9563
|
+
CURRICULUM_FRAMEWORK: "OFF",
|
|
9564
|
+
KNOWLEDGE_EXPOSITION: "OFF",
|
|
9565
|
+
STYLE_GUIDE: "OFF",
|
|
9566
|
+
ART_DIRECTION: "OFF",
|
|
9567
|
+
LESSON: "OFF",
|
|
9587
9568
|
ACT: "OFF",
|
|
9588
9569
|
QUIZ: "OFF",
|
|
9589
9570
|
SLIDE: "OFF",
|
|
@@ -9591,7 +9572,7 @@ var DEFAULT_GATE_SETTINGS = Object.freeze({
|
|
|
9591
9572
|
WKS: "OFF",
|
|
9592
9573
|
HANDOUT: "OFF",
|
|
9593
9574
|
EXT: "OFF",
|
|
9594
|
-
CODE: "
|
|
9575
|
+
CODE: "OFF",
|
|
9595
9576
|
MEDIA_SCRIPT: "OFF",
|
|
9596
9577
|
EXIT_TICKET: "OFF"
|
|
9597
9578
|
});
|
|
@@ -11480,9 +11461,10 @@ Fix ALL issues above and output the complete corrected document.` }],
|
|
|
11480
11461
|
const taskState = pipelineState.tasks[lessonCode] || {};
|
|
11481
11462
|
const currentLessonInfo = taskState.LESSON;
|
|
11482
11463
|
const lessonGate = gateModeFor(gates, "LESSON");
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
|
|
11464
|
+
const humanApproved = currentLessonInfo?.state === "approved" || currentLessonInfo?.review?.decision === "APPROVED" || approvedArtifacts.includes("LESSON") || approvedArtifacts.includes(lessonCode);
|
|
11465
|
+
const needsJudge = lessonGate === "LLM_JUDGE" && !humanApproved;
|
|
11466
|
+
let isLessonApproved = lessonGate === "OFF" || humanApproved;
|
|
11467
|
+
if (needsJudge && lessonContent) {
|
|
11486
11468
|
onProgress?.("@reviewer", `\u{1F916} Executing LLM-as-Judge academic evaluation on LESSON_${lessonCode}.md...`);
|
|
11487
11469
|
try {
|
|
11488
11470
|
let injectYamlReviewMetadata2 = function(content, meta) {
|
|
@@ -12521,6 +12503,8 @@ async function generateSingleArtifact(req) {
|
|
|
12521
12503
|
extraContext,
|
|
12522
12504
|
config,
|
|
12523
12505
|
contextSot = {},
|
|
12506
|
+
mediaPolicy,
|
|
12507
|
+
researchContext,
|
|
12524
12508
|
options = {},
|
|
12525
12509
|
onChunk
|
|
12526
12510
|
} = req;
|
|
@@ -12558,7 +12542,10 @@ ${langRule}
|
|
|
12558
12542
|
3. PEDAGOGICAL GROUNDING: Strictly align with the ${pedagogy.toUpperCase()} model, Bloom's Revised Taxonomy, and IEEE/ACM CS2023 guidelines.
|
|
12559
12543
|
4. SCAFFOLDING LEVEL: "${scaffoldingLevel.toUpperCase()}".${scaffoldingRule}
|
|
12560
12544
|
5. TARGET AUDIENCE: ${gradeLevel ? `Grade ${gradeLevel}, ` : ""}${targetAge} on ${hwStr}.${deviceRule}${classDynamicsRule}${extraContextRule}
|
|
12561
|
-
6. OUTPUT INTEGRITY: Output clean Markdown starting directly with YAML frontmatter. Do not wrap entire response in triple backticks.${artifactSpecializedPrompt}
|
|
12545
|
+
6. OUTPUT INTEGRITY: Output clean Markdown starting directly with YAML frontmatter. Do not wrap entire response in triple backticks.${artifactSpecializedPrompt}${mediaPolicy ? `
|
|
12546
|
+
|
|
12547
|
+
IMAGE PLANNING (media ledger) \u2014 QUOTA GEN: t\u1ED1i \u0111a ${mediaPolicy.maxGenImagesPerArtifact} \u1EA3nh AI cho artifact n\xE0y (\u1EA3nh search kho kh\xF4ng gi\u1EDBi h\u1EA1n).
|
|
12548
|
+
Khi n\u1ED9i dung c\u1EA7n minh h\u1ECDa (diagram quy tr\xECnh, s\u01A1 \u0111\u1ED3 kh\xE1i ni\u1EC7m, step-by-step visual), ch\xE8n placeholder \u0111\xFAng format [IMAGE: slug] (slug ch\u1EEF th\u01B0\u1EDDng-g\u1EA1ch ngang, unique trong b\xE0i, vd img-for-loop-diagram) T\u1EA0I \u0110\xDANG CH\u1ED6 c\u1EA7n \u1EA3nh, tr\xEAn d\xF2ng ri\xEAng. KH\xD4NG t\u1EF1 sinh \u1EA3nh, KH\xD4NG d\xF9ng markdown image \u2014 placeholder s\u1EBD \u0111\u01B0\u1EE3c thay b\u1EB1ng \u1EA3nh th\u1EADt sau khi Media Curator duy\u1EC7t. Ch\u1EC9 \u0111\u1EB7t cho \u1EA3nh TH\u1EF0C S\u1EF0 c\u1EA7n thi\u1EBFt.` : ""}`;
|
|
12562
12549
|
const userPrompt = `### CONTEXT GROUND TRUTH:
|
|
12563
12550
|
- Topic / Concept Domain: "${topic}"
|
|
12564
12551
|
- Lesson ID: "${lessonId || "ON_DEMAND"}"
|
|
@@ -12573,6 +12560,9 @@ ${selectedDevices ? `- Device Mode: "${selectedDevices}"
|
|
|
12573
12560
|
` : ""}${extraContext ? `- User Specific Guidance & Requirements: "${extraContext}"
|
|
12574
12561
|
` : ""}${config ? `- Full Task Config: ${JSON.stringify(config)}
|
|
12575
12562
|
` : ""}
|
|
12563
|
+
${researchContext ? `### RESEARCH GROUND TRUTH (fact-checked context \u2014 \u01B0u ti\xEAn d\xF9ng, KH\xD4NG b\u1ECBa s\u1ED1 li\u1EC7u):
|
|
12564
|
+
${researchContext.slice(0, 4e3)}
|
|
12565
|
+
` : ""}
|
|
12576
12566
|
${contextSot.framework ? `### SOT CURRICULUM FRAMEWORK:
|
|
12577
12567
|
${contextSot.framework.slice(0, 1500)}
|
|
12578
12568
|
` : ""}
|
|
@@ -12613,13 +12603,171 @@ Generate the complete, thorough, publication-grade "${artifactType.toUpperCase()
|
|
|
12613
12603
|
}
|
|
12614
12604
|
);
|
|
12615
12605
|
const { thought, content } = extractThoughtAndContent(rawResult);
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
12621
|
-
|
|
12606
|
+
const finalContent = content || accumulatedContent;
|
|
12607
|
+
const finalThought = thought || accumulatedThought;
|
|
12608
|
+
const gates = resolveGateSettings(req.gateSettings);
|
|
12609
|
+
const kitGateType = {
|
|
12610
|
+
lesson: "LESSON",
|
|
12611
|
+
slide: "SLIDE",
|
|
12612
|
+
act: "ACT",
|
|
12613
|
+
quiz: "QUIZ",
|
|
12614
|
+
guide: "GUIDE",
|
|
12615
|
+
handout: "HANDOUT",
|
|
12616
|
+
worksheet: "WKS",
|
|
12617
|
+
ext: "EXT",
|
|
12618
|
+
codelab: "CODE",
|
|
12619
|
+
exit_ticket: "EXIT_TICKET"
|
|
12620
|
+
}[artifactType];
|
|
12621
|
+
const gateMode = kitGateType ? gateModeFor(gates, kitGateType) : "OFF";
|
|
12622
|
+
if (gateMode === "OFF") {
|
|
12623
|
+
return { artifactType, filename: meta.filename, content: finalContent, persona: meta.persona, thought: finalThought };
|
|
12624
|
+
}
|
|
12625
|
+
const injectReviewMeta = (doc, metaMap) => {
|
|
12626
|
+
const fm = doc.match(/^---\s*\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
12627
|
+
if (!fm) {
|
|
12628
|
+
return `---
|
|
12629
|
+
${Object.entries(metaMap).map(([k, v]) => `${k}: "${v}"`).join("\n")}
|
|
12630
|
+
---
|
|
12631
|
+
|
|
12632
|
+
${doc}`;
|
|
12633
|
+
}
|
|
12634
|
+
let yaml = fm[1] ?? "";
|
|
12635
|
+
for (const [k, v] of Object.entries(metaMap)) {
|
|
12636
|
+
const re = new RegExp(`^${k}:.*$`, "m");
|
|
12637
|
+
if (re.test(yaml)) yaml = yaml.replace(re, `${k}: "${v}"`);
|
|
12638
|
+
else yaml += `
|
|
12639
|
+
${k}: "${v}"`;
|
|
12640
|
+
}
|
|
12641
|
+
return doc.replace(fm[0], `---
|
|
12642
|
+
${yaml.trim()}
|
|
12643
|
+
---
|
|
12644
|
+
`);
|
|
12622
12645
|
};
|
|
12646
|
+
if (gateMode === "HITL") {
|
|
12647
|
+
return {
|
|
12648
|
+
artifactType,
|
|
12649
|
+
filename: meta.filename,
|
|
12650
|
+
content: injectReviewMeta(finalContent, { review_status: "PENDING_REVIEW", review_decision: "PENDING" }),
|
|
12651
|
+
persona: meta.persona,
|
|
12652
|
+
thought: finalThought,
|
|
12653
|
+
pausedForReview: true,
|
|
12654
|
+
gateStatus: "awaiting_approval"
|
|
12655
|
+
};
|
|
12656
|
+
}
|
|
12657
|
+
const judgeObjectives = [{ code: "A1", description: `Artifact fulfills its pedagogical contract for topic "${topic}" with complete required sections`, bloomLevel: "Analyze" }];
|
|
12658
|
+
try {
|
|
12659
|
+
const verdict = await LLMJudgeEngine.evaluateArtifactWithLLM({
|
|
12660
|
+
targetArtifactType: artifactType.toUpperCase(),
|
|
12661
|
+
lessonId: lessonId || "ON_DEMAND",
|
|
12662
|
+
expectedLanguage: language.toLowerCase().includes("viet") ? "vietnamese" : "english",
|
|
12663
|
+
targetObjectives: judgeObjectives,
|
|
12664
|
+
generatedContentJson: JSON.stringify({ content: finalContent, artifactType, lessonId: lessonId || "ON_DEMAND", title: displayTitle })
|
|
12665
|
+
});
|
|
12666
|
+
const passed = Boolean(verdict.passed) && verdict.score >= 85;
|
|
12667
|
+
if (passed) {
|
|
12668
|
+
return {
|
|
12669
|
+
artifactType,
|
|
12670
|
+
filename: meta.filename,
|
|
12671
|
+
content: injectReviewMeta(finalContent, {
|
|
12672
|
+
review_status: "APPROVED",
|
|
12673
|
+
reviewed_by: "@reviewer (LLM-as-Judge)",
|
|
12674
|
+
review_decision: "APPROVED"
|
|
12675
|
+
}),
|
|
12676
|
+
persona: meta.persona,
|
|
12677
|
+
thought: finalThought,
|
|
12678
|
+
judgeScore: verdict.score,
|
|
12679
|
+
judgeCritique: verdict.critique
|
|
12680
|
+
};
|
|
12681
|
+
}
|
|
12682
|
+
const repairFeedback = [verdict.autoRepairInstructions, ...verdict.weaknesses || []].filter(Boolean).join("\n");
|
|
12683
|
+
const repairedRaw = await streamCurriculumAIInference(
|
|
12684
|
+
[
|
|
12685
|
+
{ role: "user", content: `You previously authored \`${meta.filename}\` and the academic Judge REJECTED it with the critique below.
|
|
12686
|
+
Rewrite the COMPLETE corrected document: fix every listed issue, keep the mandatory structure, frontmatter and all approved sections. Output 100% clean Markdown only.
|
|
12687
|
+
|
|
12688
|
+
JUDGE CRITIQUE:
|
|
12689
|
+
${repairFeedback}
|
|
12690
|
+
|
|
12691
|
+
CURRENT DOCUMENT:
|
|
12692
|
+
${finalContent}` }
|
|
12693
|
+
],
|
|
12694
|
+
userPrompt,
|
|
12695
|
+
{ ...options, systemPersona: systemPrompt },
|
|
12696
|
+
(chunk, type) => {
|
|
12697
|
+
if (onChunk) onChunk(chunk, type === "thought" ? "thought" : "content");
|
|
12698
|
+
}
|
|
12699
|
+
);
|
|
12700
|
+
const repairedExtract = extractThoughtAndContent(repairedRaw);
|
|
12701
|
+
const repaired = repairedExtract.content || repairedRaw;
|
|
12702
|
+
if (repaired && repaired.trim().length > 100) {
|
|
12703
|
+
const reVerdict = await LLMJudgeEngine.evaluateArtifactWithLLM({
|
|
12704
|
+
targetArtifactType: artifactType.toUpperCase(),
|
|
12705
|
+
lessonId: lessonId || "ON_DEMAND",
|
|
12706
|
+
expectedLanguage: language.toLowerCase().includes("viet") ? "vietnamese" : "english",
|
|
12707
|
+
targetObjectives: judgeObjectives,
|
|
12708
|
+
generatedContentJson: JSON.stringify({ content: repaired, artifactType, lessonId: lessonId || "ON_DEMAND", title: displayTitle })
|
|
12709
|
+
});
|
|
12710
|
+
if (reVerdict.passed && reVerdict.score >= 85) {
|
|
12711
|
+
return {
|
|
12712
|
+
artifactType,
|
|
12713
|
+
filename: meta.filename,
|
|
12714
|
+
content: injectReviewMeta(repaired, {
|
|
12715
|
+
review_status: "APPROVED",
|
|
12716
|
+
reviewed_by: "@reviewer (LLM-as-Judge)",
|
|
12717
|
+
review_decision: "APPROVED"
|
|
12718
|
+
}),
|
|
12719
|
+
persona: meta.persona,
|
|
12720
|
+
thought: repairedExtract.thought || "",
|
|
12721
|
+
judgeScore: reVerdict.score,
|
|
12722
|
+
judgeCritique: reVerdict.critique
|
|
12723
|
+
};
|
|
12724
|
+
}
|
|
12725
|
+
return {
|
|
12726
|
+
artifactType,
|
|
12727
|
+
filename: meta.filename,
|
|
12728
|
+
content: injectReviewMeta(repaired, {
|
|
12729
|
+
review_status: "NEEDS_REVISION",
|
|
12730
|
+
reviewed_by: "@reviewer (LLM-as-Judge)",
|
|
12731
|
+
review_decision: "NEEDS_REVISION"
|
|
12732
|
+
}),
|
|
12733
|
+
persona: meta.persona,
|
|
12734
|
+
thought: repairedExtract.thought || "",
|
|
12735
|
+
pausedForReview: true,
|
|
12736
|
+
gateStatus: "judge_needs_revision",
|
|
12737
|
+
judgeScore: reVerdict.score,
|
|
12738
|
+
judgeCritique: reVerdict.critique
|
|
12739
|
+
};
|
|
12740
|
+
}
|
|
12741
|
+
return {
|
|
12742
|
+
artifactType,
|
|
12743
|
+
filename: meta.filename,
|
|
12744
|
+
content: injectReviewMeta(finalContent, {
|
|
12745
|
+
review_status: "NEEDS_REVISION",
|
|
12746
|
+
reviewed_by: "@reviewer (LLM-as-Judge)",
|
|
12747
|
+
review_decision: "NEEDS_REVISION"
|
|
12748
|
+
}),
|
|
12749
|
+
persona: meta.persona,
|
|
12750
|
+
thought: finalThought,
|
|
12751
|
+
pausedForReview: true,
|
|
12752
|
+
gateStatus: "judge_needs_revision",
|
|
12753
|
+
judgeScore: verdict.score,
|
|
12754
|
+
judgeCritique: verdict.critique
|
|
12755
|
+
};
|
|
12756
|
+
} catch (judgeErr) {
|
|
12757
|
+
return {
|
|
12758
|
+
artifactType,
|
|
12759
|
+
filename: meta.filename,
|
|
12760
|
+
content: injectReviewMeta(finalContent, {
|
|
12761
|
+
review_status: "NEEDS_REVISION",
|
|
12762
|
+
review_decision: "NEEDS_REVISION",
|
|
12763
|
+
critique: `Judge unavailable (fail-closed): ${String(judgeErr?.message || judgeErr).slice(0, 300)}`
|
|
12764
|
+
}),
|
|
12765
|
+
persona: meta.persona,
|
|
12766
|
+
thought: finalThought,
|
|
12767
|
+
pausedForReview: true,
|
|
12768
|
+
gateStatus: "judge_error"
|
|
12769
|
+
};
|
|
12770
|
+
}
|
|
12623
12771
|
} catch (error) {
|
|
12624
12772
|
throw createAiInferenceError({
|
|
12625
12773
|
errorCode: "ERR_AI_ALL_PROVIDERS_FAILED",
|
|
@@ -13375,43 +13523,15 @@ function safeParseJson(rawText) {
|
|
|
13375
13523
|
return null;
|
|
13376
13524
|
}
|
|
13377
13525
|
}
|
|
13378
|
-
async function readSseStream(reader, decoder, onData) {
|
|
13379
|
-
let buffer = "";
|
|
13380
|
-
while (true) {
|
|
13381
|
-
const { done, value } = await reader.read();
|
|
13382
|
-
if (done) break;
|
|
13383
|
-
buffer += decoder.decode(value, { stream: true });
|
|
13384
|
-
const lines = buffer.split("\n");
|
|
13385
|
-
buffer = lines.pop() || "";
|
|
13386
|
-
for (const line of lines) {
|
|
13387
|
-
const trimmed = line.trim();
|
|
13388
|
-
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
13389
|
-
if (trimmed === "data: [DONE]") return;
|
|
13390
|
-
if (trimmed.startsWith("data: ")) {
|
|
13391
|
-
const dataStr = trimmed.slice(6).trim();
|
|
13392
|
-
if (dataStr) onData(dataStr);
|
|
13393
|
-
}
|
|
13394
|
-
}
|
|
13395
|
-
}
|
|
13396
|
-
}
|
|
13397
13526
|
async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onChunk) {
|
|
13398
|
-
apiKeys.alibaba || apiKeys.dashscope || process.env.ALIBABA_API_KEY || process.env.DASHSCOPE_API_KEY;
|
|
13527
|
+
const alibabaKey = apiKeys.alibaba || apiKeys.dashscope || process.env.ALIBABA_API_KEY || process.env.DASHSCOPE_API_KEY;
|
|
13399
13528
|
const nvidiaKey = apiKeys.nvidia || process.env.NVIDIA_API_KEY;
|
|
13400
|
-
apiKeys.deepseek || process.env.DEEPSEEK_API_KEY;
|
|
13529
|
+
const deepseekKey = apiKeys.deepseek || process.env.DEEPSEEK_API_KEY;
|
|
13401
13530
|
const openrouterKey = apiKeys.openrouter || process.env.OPENROUTER_API_KEY;
|
|
13402
|
-
apiKeys.gemini || process.env.GEMINI_API_KEY;
|
|
13531
|
+
const geminiKey = apiKeys.gemini || process.env.GEMINI_API_KEY;
|
|
13403
13532
|
const openrouterPreset = process.env.OPENROUTER_FREE_PRESET || "@preset/coding-free";
|
|
13404
|
-
const
|
|
13405
|
-
{
|
|
13406
|
-
role: "system",
|
|
13407
|
-
content: `${systemPrompt}
|
|
13408
|
-
|
|
13409
|
-
THINKING DIRECTIVE: Keep reasoning concise, structured, and focused strictly on the core pedagogical trade-offs in 4-6 concise bullet points (under 120 words). Then output the JSON immediately.`
|
|
13410
|
-
},
|
|
13411
|
-
{ role: "user", content: userPrompt }
|
|
13412
|
-
];
|
|
13533
|
+
const candidates = [];
|
|
13413
13534
|
if (openrouterKey && isProviderEnabled("openrouter")) {
|
|
13414
|
-
const t0 = Date.now();
|
|
13415
13535
|
const orModels = [
|
|
13416
13536
|
openrouterPreset,
|
|
13417
13537
|
"openrouter/free",
|
|
@@ -13421,174 +13541,82 @@ THINKING DIRECTIVE: Keep reasoning concise, structured, and focused strictly on
|
|
|
13421
13541
|
"minimax/minimax-m3:free",
|
|
13422
13542
|
"cohere/north-mini-code:free"
|
|
13423
13543
|
].filter((m) => isModelAllowed(m));
|
|
13424
|
-
const
|
|
13425
|
-
|
|
13426
|
-
try {
|
|
13427
|
-
const controller = new AbortController();
|
|
13428
|
-
const timeout = setTimeout(() => controller.abort(), 25e3);
|
|
13429
|
-
const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
13430
|
-
method: "POST",
|
|
13431
|
-
headers: {
|
|
13432
|
-
"Content-Type": "application/json",
|
|
13433
|
-
"Authorization": `Bearer ${openrouterKey}`
|
|
13434
|
-
},
|
|
13435
|
-
body: JSON.stringify({
|
|
13436
|
-
model,
|
|
13437
|
-
messages,
|
|
13438
|
-
stream: true,
|
|
13439
|
-
temperature: 0.3
|
|
13440
|
-
}),
|
|
13441
|
-
signal: controller.signal
|
|
13442
|
-
});
|
|
13443
|
-
clearTimeout(timeout);
|
|
13444
|
-
if (res.ok && res.body) {
|
|
13445
|
-
const reader = res.body.getReader();
|
|
13446
|
-
const decoder = new TextDecoder();
|
|
13447
|
-
let fullContent = "";
|
|
13448
|
-
await readSseStream(reader, decoder, (dataStr) => {
|
|
13449
|
-
try {
|
|
13450
|
-
const parsed = JSON.parse(dataStr);
|
|
13451
|
-
const delta = parsed.choices?.[0]?.delta;
|
|
13452
|
-
if (delta?.content) {
|
|
13453
|
-
fullContent += delta.content;
|
|
13454
|
-
onChunk?.(delta.content, "content");
|
|
13455
|
-
}
|
|
13456
|
-
} catch {
|
|
13457
|
-
}
|
|
13458
|
-
});
|
|
13459
|
-
if (fullContent.trim()) {
|
|
13460
|
-
console.log(`[curriculum-kit:LayerPrefill] \u2705 Tier 1 OpenRouter (${model}) streamed in ${Date.now() - t0}ms`);
|
|
13461
|
-
return fullContent.trim();
|
|
13462
|
-
}
|
|
13463
|
-
}
|
|
13464
|
-
} catch (e) {
|
|
13465
|
-
console.warn(`[curriculum-kit:LayerPrefill] Tier 1 OpenRouter stream failed for ${model} in ${Date.now() - t0}ms:`, e?.message || e);
|
|
13466
|
-
}
|
|
13544
|
+
for (const m of Array.from(new Set(orModels))) {
|
|
13545
|
+
candidates.push({ provider: "openrouter", model: m, apiKey: openrouterKey });
|
|
13467
13546
|
}
|
|
13468
13547
|
}
|
|
13469
13548
|
if (nvidiaKey && isProviderEnabled("nvidia")) {
|
|
13470
|
-
const t0 = Date.now();
|
|
13471
13549
|
const nemotronModels = [
|
|
13472
13550
|
"nvidia/nemotron-3-ultra-550b-a55b",
|
|
13473
13551
|
"nvidia/nemotron-3.5-lightning-30b-a3b",
|
|
13474
13552
|
"nvidia/nemotron-3-super-120b-a12b",
|
|
13475
13553
|
"nvidia/llama-3.1-nemotron-70b-instruct"
|
|
13476
13554
|
].filter((m) => isModelAllowed(m));
|
|
13477
|
-
for (const
|
|
13478
|
-
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
|
|
13495
|
-
|
|
13496
|
-
|
|
13497
|
-
const reader = res.body.getReader();
|
|
13498
|
-
const decoder = new TextDecoder();
|
|
13499
|
-
let fullContent = "";
|
|
13500
|
-
await readSseStream(reader, decoder, (dataStr) => {
|
|
13501
|
-
try {
|
|
13502
|
-
const parsed = JSON.parse(dataStr);
|
|
13503
|
-
const delta = parsed.choices?.[0]?.delta;
|
|
13504
|
-
if (delta?.content) {
|
|
13505
|
-
fullContent += delta.content;
|
|
13506
|
-
onChunk?.(delta.content, "content");
|
|
13507
|
-
}
|
|
13508
|
-
} catch {
|
|
13509
|
-
}
|
|
13510
|
-
});
|
|
13511
|
-
if (fullContent.trim()) {
|
|
13512
|
-
console.log(`[curriculum-kit:LayerPrefill] \u2705 Tier 2 NVIDIA NIM (${model}) streamed in ${Date.now() - t0}ms`);
|
|
13513
|
-
return fullContent.trim();
|
|
13514
|
-
}
|
|
13515
|
-
}
|
|
13516
|
-
} catch (e) {
|
|
13517
|
-
console.warn(`[curriculum-kit:LayerPrefill] Tier 2 NVIDIA NIM stream failed for ${model} in ${Date.now() - t0}ms:`, e?.message || e);
|
|
13518
|
-
}
|
|
13555
|
+
for (const m of nemotronModels) {
|
|
13556
|
+
candidates.push({ provider: "nvidia", model: m, apiKey: nvidiaKey });
|
|
13557
|
+
}
|
|
13558
|
+
}
|
|
13559
|
+
if (alibabaKey && isProviderEnabled("alibaba")) {
|
|
13560
|
+
const aliModels = ["qwen-plus", "qwen-turbo", "qwen3.7-plus"].filter((m) => isModelAllowed(m));
|
|
13561
|
+
for (const m of aliModels) {
|
|
13562
|
+
candidates.push({ provider: "alibaba", model: m, apiKey: alibabaKey });
|
|
13563
|
+
}
|
|
13564
|
+
}
|
|
13565
|
+
if (deepseekKey && isProviderEnabled("deepseek")) {
|
|
13566
|
+
const dsModels = ["deepseek-chat", "deepseek-coder"].filter((m) => isModelAllowed(m));
|
|
13567
|
+
for (const m of dsModels) {
|
|
13568
|
+
candidates.push({ provider: "deepseek", model: m, apiKey: deepseekKey });
|
|
13569
|
+
}
|
|
13570
|
+
}
|
|
13571
|
+
if (geminiKey && (isProviderEnabled("gemini") || isProviderEnabled("google"))) {
|
|
13572
|
+
const gModels = ["gemini-1.5-flash", "gemini-2.0-flash", "gemini-1.5-pro"].filter((m) => isModelAllowed(m));
|
|
13573
|
+
for (const m of gModels) {
|
|
13574
|
+
candidates.push({ provider: "google", model: m, apiKey: geminiKey });
|
|
13519
13575
|
}
|
|
13520
13576
|
}
|
|
13521
13577
|
const fallbackChain = getDesignatedFallbackChain();
|
|
13522
13578
|
for (const target of fallbackChain) {
|
|
13523
|
-
|
|
13524
|
-
|
|
13525
|
-
|
|
13526
|
-
|
|
13527
|
-
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
|
|
13531
|
-
|
|
13532
|
-
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13547
|
-
|
|
13548
|
-
|
|
13549
|
-
|
|
13550
|
-
|
|
13551
|
-
|
|
13552
|
-
|
|
13553
|
-
|
|
13554
|
-
|
|
13579
|
+
if (!candidates.some((c) => c.provider === target.provider && c.model === target.model)) {
|
|
13580
|
+
candidates.push({
|
|
13581
|
+
provider: target.provider,
|
|
13582
|
+
model: target.model,
|
|
13583
|
+
apiKey: apiKeys[target.provider]
|
|
13584
|
+
});
|
|
13585
|
+
}
|
|
13586
|
+
}
|
|
13587
|
+
const systemInstructions = `${systemPrompt}
|
|
13588
|
+
|
|
13589
|
+
THINKING DIRECTIVE: Keep reasoning concise, structured, and focused strictly on the core pedagogical trade-offs in 4-6 concise bullet points (under 120 words). Then output the JSON immediately.`;
|
|
13590
|
+
for (const candidate of candidates) {
|
|
13591
|
+
const t0 = Date.now();
|
|
13592
|
+
try {
|
|
13593
|
+
const modelInstance = getAIModel({
|
|
13594
|
+
provider: candidate.provider,
|
|
13595
|
+
modelName: candidate.model,
|
|
13596
|
+
apiKey: candidate.apiKey
|
|
13597
|
+
});
|
|
13598
|
+
const streamResult = ai.streamText({
|
|
13599
|
+
model: modelInstance,
|
|
13600
|
+
system: systemInstructions,
|
|
13601
|
+
prompt: userPrompt,
|
|
13602
|
+
temperature: 0.2,
|
|
13603
|
+
abortSignal: AbortSignal.timeout(25e3)
|
|
13604
|
+
});
|
|
13605
|
+
let fullContent = "";
|
|
13606
|
+
for await (const part of streamResult.fullStream) {
|
|
13607
|
+
if (part.type === "reasoning-delta") {
|
|
13608
|
+
onChunk?.(part.text, "thought");
|
|
13609
|
+
} else if (part.type === "text-delta") {
|
|
13610
|
+
fullContent += part.text;
|
|
13611
|
+
onChunk?.(part.text, "content");
|
|
13555
13612
|
}
|
|
13556
|
-
} catch {
|
|
13557
13613
|
}
|
|
13558
|
-
|
|
13559
|
-
|
|
13560
|
-
|
|
13561
|
-
method: "POST",
|
|
13562
|
-
headers: {
|
|
13563
|
-
"Content-Type": "application/json",
|
|
13564
|
-
"Authorization": `Bearer ${openrouterKey}`
|
|
13565
|
-
},
|
|
13566
|
-
body: JSON.stringify({
|
|
13567
|
-
model,
|
|
13568
|
-
messages,
|
|
13569
|
-
stream: true,
|
|
13570
|
-
temperature: 0.2
|
|
13571
|
-
})
|
|
13572
|
-
});
|
|
13573
|
-
if (res.ok && res.body) {
|
|
13574
|
-
const reader = res.body.getReader();
|
|
13575
|
-
const decoder = new TextDecoder();
|
|
13576
|
-
let fullContent = "";
|
|
13577
|
-
await readSseStream(reader, decoder, (dataStr) => {
|
|
13578
|
-
try {
|
|
13579
|
-
const parsed = JSON.parse(dataStr);
|
|
13580
|
-
const delta = parsed.choices?.[0]?.delta;
|
|
13581
|
-
if (delta?.content) {
|
|
13582
|
-
fullContent += delta.content;
|
|
13583
|
-
onChunk?.(delta.content, "content");
|
|
13584
|
-
}
|
|
13585
|
-
} catch {
|
|
13586
|
-
}
|
|
13587
|
-
});
|
|
13588
|
-
if (fullContent.trim()) return fullContent.trim();
|
|
13589
|
-
}
|
|
13590
|
-
} catch {
|
|
13614
|
+
if (fullContent.trim()) {
|
|
13615
|
+
console.log(`[curriculum-kit:VercelAI] \u2705 ${candidate.provider} (${candidate.model}) streamed in ${Date.now() - t0}ms`);
|
|
13616
|
+
return fullContent.trim();
|
|
13591
13617
|
}
|
|
13618
|
+
} catch (e) {
|
|
13619
|
+
console.warn(`[curriculum-kit:VercelAI] Candidate ${candidate.provider} (${candidate.model}) failed in ${Date.now() - t0}ms:`, e?.message || e);
|
|
13592
13620
|
}
|
|
13593
13621
|
}
|
|
13594
13622
|
return null;
|
|
@@ -14455,7 +14483,7 @@ template_contract: "artifact-template-v1"
|
|
|
14455
14483
|
async function withRateLimitBackoff(options) {
|
|
14456
14484
|
let attempt = 1;
|
|
14457
14485
|
try {
|
|
14458
|
-
const meta =
|
|
14486
|
+
const meta = workflow.getStepMetadata();
|
|
14459
14487
|
if (meta && typeof meta.attempt === "number") {
|
|
14460
14488
|
attempt = meta.attempt;
|
|
14461
14489
|
}
|
|
@@ -14471,7 +14499,7 @@ async function withRateLimitBackoff(options) {
|
|
|
14471
14499
|
if (isRateLimited || isTransientError) {
|
|
14472
14500
|
const retryAfterSeconds = Math.min(120, Math.pow(2, attempt) * 2);
|
|
14473
14501
|
const reason = isRateLimited ? "Rate limit exceeded (429)" : `Transient server error (${status || "network"})`;
|
|
14474
|
-
throw new
|
|
14502
|
+
throw new workflow.RetryableError(`[${options.stepName}] ${reason}. Retrying attempt ${attempt + 1}...`, {
|
|
14475
14503
|
retryAfter: `${retryAfterSeconds}s`
|
|
14476
14504
|
});
|
|
14477
14505
|
}
|
|
@@ -14756,10 +14784,10 @@ var approvalPayloadSchema = zod.z.object({
|
|
|
14756
14784
|
feedback: zod.z.string().optional().describe("Actionable feedback or required changes if rejected"),
|
|
14757
14785
|
timestamp: zod.z.string().optional().describe("ISO timestamp of the approval action")
|
|
14758
14786
|
});
|
|
14759
|
-
var
|
|
14760
|
-
|
|
14761
|
-
|
|
14762
|
-
}
|
|
14787
|
+
var lessonApprovalHook = workflow.defineHook();
|
|
14788
|
+
function approvalHookToken(projectId, lessonId, artifactType) {
|
|
14789
|
+
return `approval:${projectId}:${lessonId}:${artifactType}`;
|
|
14790
|
+
}
|
|
14763
14791
|
|
|
14764
14792
|
// src/standards/standardsCoverageGate.ts
|
|
14765
14793
|
function resolveStatementRef(ref, packs) {
|
|
@@ -15109,9 +15137,9 @@ async function generateMilestoneWorkflow(input) {
|
|
|
15109
15137
|
} else if (lessonGate === "HITL") {
|
|
15110
15138
|
const hookToken = "gate:lesson:" + milestoneId;
|
|
15111
15139
|
console.log(" \u23F8 [Gate] LESSON awaiting HUMAN review (HITL) \u2014 hook " + hookToken);
|
|
15112
|
-
const
|
|
15113
|
-
const
|
|
15114
|
-
const hook =
|
|
15140
|
+
const workflowPkg = await import('workflow');
|
|
15141
|
+
const createHook = workflowPkg.createHook;
|
|
15142
|
+
const hook = createHook({ token: hookToken });
|
|
15115
15143
|
const review = await hook;
|
|
15116
15144
|
console.log(" \u2705 [Gate] Human verdict: " + (review.approved ? "APPROVED" : "REJECTED") + (review.feedback ? " \u2014 " + review.feedback : ""));
|
|
15117
15145
|
if (!review.approved) {
|
|
@@ -15131,7 +15159,7 @@ async function generateMilestoneWorkflow(input) {
|
|
|
15131
15159
|
standardsContext: standardsContext || void 0,
|
|
15132
15160
|
modelOptions: input.modelOptions
|
|
15133
15161
|
});
|
|
15134
|
-
const hook2 =
|
|
15162
|
+
const hook2 = createHook({ token: hookToken + ":v2" });
|
|
15135
15163
|
const review2 = await hook2;
|
|
15136
15164
|
console.log(" \u2705 [Gate] Human verdict (v2): " + (review2.approved ? "APPROVED" : "REJECTED"));
|
|
15137
15165
|
if (!review2.approved) {
|
|
@@ -15319,7 +15347,7 @@ async function generateRoadmapWorkflow(input) {
|
|
|
15319
15347
|
}
|
|
15320
15348
|
}
|
|
15321
15349
|
if (i + batchSize < milestones.length) {
|
|
15322
|
-
await
|
|
15350
|
+
await workflow.sleep("2s");
|
|
15323
15351
|
}
|
|
15324
15352
|
}
|
|
15325
15353
|
const successfulMilestones = milestoneOutcomes.filter((m) => m.status === "FULFILLED").length;
|
|
@@ -16741,6 +16769,36 @@ var MediaLedger = class {
|
|
|
16741
16769
|
doneCount() {
|
|
16742
16770
|
return this.listByStatus("done").length;
|
|
16743
16771
|
}
|
|
16772
|
+
/**
|
|
16773
|
+
* Scan artifact content và seed pending entries cho mọi placeholder [IMAGE: slug].
|
|
16774
|
+
* Được gọi sau khi agent sinh artifact (trước khi Curator duyệt) — đây là cầu nối
|
|
16775
|
+
* duy nhất giữa "agent chèn placeholder" và "sổ cái có entry để Curator/Fulfiller xử".
|
|
16776
|
+
* Idempotent: slug đã có entry active (pending/approved/done) → giữ nguyên;
|
|
16777
|
+
* chỉ re-describe khi entry trước bị rejected/failed.
|
|
16778
|
+
*/
|
|
16779
|
+
seedFromArtifact(artifactId, content) {
|
|
16780
|
+
const seeded = [];
|
|
16781
|
+
const re = /\[IMAGE\s*:\s*([a-z0-9][a-z0-9-]*)\s*\]/gi;
|
|
16782
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16783
|
+
let m;
|
|
16784
|
+
while ((m = re.exec(content)) !== null) {
|
|
16785
|
+
const slug = m[1].toLowerCase();
|
|
16786
|
+
if (seen.has(slug)) continue;
|
|
16787
|
+
seen.add(slug);
|
|
16788
|
+
const existing = this.entries.get(slug);
|
|
16789
|
+
const isActive = existing && (existing.status === "pending" || existing.status === "approved" || existing.status === "done");
|
|
16790
|
+
if (isActive) continue;
|
|
16791
|
+
seeded.push(this.upsert({
|
|
16792
|
+
slug,
|
|
16793
|
+
artifactId,
|
|
16794
|
+
name: slug.replace(/^(img-|image-)/, "").replace(/-/g, " "),
|
|
16795
|
+
description: `Placeholder ch\xE8n b\u1EDFi agent trong artifact ${artifactId} \u2014 ch\u1EDD Curator enrich (m\xF4 t\u1EA3 g\u1ED1c ch\u01B0a c\xF3).`,
|
|
16796
|
+
mode: "generate",
|
|
16797
|
+
status: "pending"
|
|
16798
|
+
}));
|
|
16799
|
+
}
|
|
16800
|
+
return seeded;
|
|
16801
|
+
}
|
|
16744
16802
|
};
|
|
16745
16803
|
|
|
16746
16804
|
// src/media/curator.ts
|
|
@@ -17155,6 +17213,7 @@ exports.WorksheetSchema = WorksheetSchema;
|
|
|
17155
17213
|
exports.activityTools = activityTools;
|
|
17156
17214
|
exports.analystTools = analystTools;
|
|
17157
17215
|
exports.analyzeProjectCreationIntent = analyzeProjectCreationIntent;
|
|
17216
|
+
exports.approvalHookToken = approvalHookToken;
|
|
17158
17217
|
exports.approvalPayloadSchema = approvalPayloadSchema;
|
|
17159
17218
|
exports.assertAcyclic = assertAcyclic;
|
|
17160
17219
|
exports.assessorTools = assessorTools;
|