@thanh01.pmt/curriculum-kit 1.4.9 → 1.4.11

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 CHANGED
@@ -5459,6 +5459,11 @@ Every slide MUST include notes with:
5459
5459
  - Cold-Call: 1 check question
5460
5460
  - Scaffolding Tip: 1 analogy or hint
5461
5461
 
5462
+ ### \u26A0\uFE0F STRICT SLIDE FORMATTING INVARIANTS:
5463
+ 1. Every slide MUST store its content fields inside "slots": { ... }. NEVER put "title", "content", or points at the top level of a slide!
5464
+ 2. All code slots MUST contain real, executable, high-fidelity code. Never use placeholders like "<CODE>" or "...".
5465
+ 3. All point and card slots MUST contain concrete, rich pedagogical explanations. Never use placeholder dots or empty cards.
5466
+
5462
5467
  ### Output JSON Format:
5463
5468
  \`\`\`json
5464
5469
  {
@@ -25326,10 +25331,15 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
25326
25331
  const isHtmlEngine = options.slidesEngine === "html";
25327
25332
  if (isHtmlEngine) {
25328
25333
  let presentationKitAi = null;
25334
+ let presentationKitCore = null;
25329
25335
  try {
25330
25336
  presentationKitAi = await import('@thanh01.pmt/presentation-kit/ai');
25331
25337
  } catch {
25332
25338
  }
25339
+ try {
25340
+ presentationKitCore = await import('@thanh01.pmt/presentation-kit');
25341
+ } catch {
25342
+ }
25333
25343
  const slidePrompt = buildHtmlDeckSlidePrompt({
25334
25344
  lessonCode,
25335
25345
  lessonTitle,
@@ -25364,12 +25374,21 @@ ${glossaryContext}` : void 0,
25364
25374
  const candidate = fenceMatch ? fenceMatch[1] : str;
25365
25375
  return candidate.trim();
25366
25376
  };
25377
+ const normalizeDeckSlides = (deck) => {
25378
+ const normalizer = presentationKitCore?.normalizeSlideSlots || presentationKitAi?.normalizeSlideSlots;
25379
+ if (deck && Array.isArray(deck.slides) && normalizer) {
25380
+ deck.slides = deck.slides.map((s) => normalizer(s));
25381
+ }
25382
+ return deck;
25383
+ };
25367
25384
  try {
25368
25385
  deckJson = JSON.parse(extractCleanJson(rawSlide));
25386
+ deckJson = normalizeDeckSlides(deckJson);
25369
25387
  } catch {
25370
25388
  try {
25371
25389
  const { jsonrepair: jsonrepair2 } = await import('jsonrepair');
25372
25390
  deckJson = JSON.parse(jsonrepair2(extractCleanJson(rawSlide)));
25391
+ deckJson = normalizeDeckSlides(deckJson);
25373
25392
  } catch {
25374
25393
  }
25375
25394
  }
@@ -25391,11 +25410,13 @@ Fix ALL issues and output the complete corrected JSON object strictly matching t
25391
25410
  );
25392
25411
  try {
25393
25412
  deckJson = JSON.parse(extractCleanJson(repairedRaw));
25413
+ deckJson = normalizeDeckSlides(deckJson);
25394
25414
  validation = presentationKitAi.validateHtmlSlideDeck(deckJson, true);
25395
25415
  } catch {
25396
25416
  try {
25397
25417
  const { jsonrepair: jsonrepair2 } = await import('jsonrepair');
25398
25418
  deckJson = JSON.parse(jsonrepair2(extractCleanJson(repairedRaw)));
25419
+ deckJson = normalizeDeckSlides(deckJson);
25399
25420
  validation = presentationKitAi.validateHtmlSlideDeck(deckJson, true);
25400
25421
  } catch {
25401
25422
  }
@@ -25423,9 +25444,10 @@ ${JSON.stringify(deckJson || {}, null, 2)}
25423
25444
  const deckJsonStr = JSON.stringify(deckJson, null, 2);
25424
25445
  await storage.saveArtifact(projectId, `_content/${unitCode}/SLIDE_${lessonCode}.deck.json`, deckJsonStr);
25425
25446
  producedArtifacts.push(`SLIDE_${lessonCode}.deck.json`);
25426
- if (presentationKitAi?.compileHtmlDeck) {
25447
+ const compiler = presentationKitCore?.compileHtmlDeck || presentationKitAi?.compileHtmlDeck;
25448
+ if (compiler) {
25427
25449
  try {
25428
- const compiled = presentationKitAi.compileHtmlDeck(deckJson);
25450
+ const compiled = compiler(deckJson);
25429
25451
  if (compiled?.html) {
25430
25452
  await storage.saveArtifact(projectId, `_content/${unitCode}/SLIDE_${lessonCode}.html`, compiled.html);
25431
25453
  producedArtifacts.push(`SLIDE_${lessonCode}.html`);
@@ -27302,13 +27324,13 @@ var DeterministicPipelineRunner = class {
27302
27324
 
27303
27325
  // src/services/prefillService.ts
27304
27326
  init_streamRunner();
27305
- var DEFAULT_STREAM_IDLE_MS = 9e4;
27327
+ var DEFAULT_STREAM_IDLE_MS = 18e4;
27306
27328
  var DEFAULT_STREAM_TOTAL_MS = 48e4;
27307
27329
  var LAYER_IDLE_BUDGET_MS = {
27308
- 1: 9e4,
27309
- 2: 9e4,
27330
+ 1: 18e4,
27331
+ 2: 18e4,
27310
27332
  3: 18e4
27311
- // Layer 3 synthesizes all accumulated context + pathways (3 minutes idle protection)
27333
+ // 3 minutes idle protection across all layers for free-tier reasoning models
27312
27334
  };
27313
27335
  var LAYER_TOTAL_BUDGET_MS = {
27314
27336
  1: 48e4,
@@ -27369,10 +27391,20 @@ function extractStreamChunk(part) {
27369
27391
  }
27370
27392
  return {};
27371
27393
  }
27372
- function createStreamAbortSignal(budget) {
27394
+ function createStreamAbortSignal(budget, parentSignal) {
27373
27395
  const controller = new AbortController();
27374
27396
  let idleTimer = null;
27375
27397
  let totalTimer = null;
27398
+ const onParentAbort = () => {
27399
+ controller.abort(parentSignal?.reason || new Error("Aborted by parent signal"));
27400
+ };
27401
+ if (parentSignal) {
27402
+ if (parentSignal.aborted) {
27403
+ controller.abort(parentSignal.reason || new Error("Parent signal already aborted"));
27404
+ } else {
27405
+ parentSignal.addEventListener("abort", onParentAbort, { once: true });
27406
+ }
27407
+ }
27376
27408
  const armIdle = () => {
27377
27409
  if (idleTimer) clearTimeout(idleTimer);
27378
27410
  if (budget.idleMs > 0) {
@@ -27399,6 +27431,9 @@ function createStreamAbortSignal(budget) {
27399
27431
  dispose: () => {
27400
27432
  if (idleTimer) clearTimeout(idleTimer);
27401
27433
  if (totalTimer) clearTimeout(totalTimer);
27434
+ if (parentSignal) {
27435
+ parentSignal.removeEventListener("abort", onParentAbort);
27436
+ }
27402
27437
  idleTimer = null;
27403
27438
  totalTimer = null;
27404
27439
  }
@@ -27510,7 +27545,7 @@ function safeParseJson(rawText) {
27510
27545
  }
27511
27546
  return null;
27512
27547
  }
27513
- async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onChunk, preferredModel, preferredProvider, budget) {
27548
+ async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onChunk, preferredModel, preferredProvider, budget, signal) {
27514
27549
  const alibabaKey = apiKeys.alibaba || apiKeys.dashscope || process.env.ALIBABA_API_KEY || process.env.DASHSCOPE_API_KEY;
27515
27550
  const nvidiaKey = apiKeys.nvidia || process.env.NVIDIA_API_KEY;
27516
27551
  const deepseekKey = apiKeys.deepseek || process.env.DEEPSEEK_API_KEY;
@@ -27542,7 +27577,6 @@ async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onC
27542
27577
  const nemotronModels = [
27543
27578
  "nvidia/nemotron-3-ultra-550b-a55b",
27544
27579
  "nvidia/nemotron-3.5-lightning-30b-a3b",
27545
- "nvidia/nemotron-3-super-120b-a12b",
27546
27580
  "nvidia/llama-3.1-nemotron-70b-instruct"
27547
27581
  ].filter((m) => isModelAllowed(m));
27548
27582
  for (const m of nemotronModels) {
@@ -27593,8 +27627,12 @@ THINKING DIRECTIVE: Do ALL your reasoning internally BEFORE emitting output. Do
27593
27627
  totalMs: budget?.totalMs ?? DEFAULT_STREAM_TOTAL_MS
27594
27628
  };
27595
27629
  for (const candidate of candidates) {
27630
+ if (signal?.aborted) {
27631
+ console.log(`[curriculum-kit:VercelAI] AbortSignal already aborted, cancelling candidate loop.`);
27632
+ break;
27633
+ }
27596
27634
  const t0 = Date.now();
27597
- const abort = createStreamAbortSignal(resolvedBudget);
27635
+ const abort = createStreamAbortSignal(resolvedBudget, signal);
27598
27636
  try {
27599
27637
  const modelInstance = getAIModel({
27600
27638
  provider: candidate.provider,
@@ -27626,6 +27664,13 @@ THINKING DIRECTIVE: Do ALL your reasoning internally BEFORE emitting output. Do
27626
27664
  }
27627
27665
  } catch (e) {
27628
27666
  console.warn(`[curriculum-kit:VercelAI] Candidate ${candidate.provider} (${candidate.model}) failed in ${Date.now() - t0}ms:`, e?.message || e);
27667
+ const isClientAborted = Boolean(
27668
+ signal?.aborted || e?.name === "AbortError" && signal?.aborted || e?.message && (e.message.includes("Controller is already closed") || e.message.includes("The operation was aborted") || e.message.includes("Aborted by parent signal"))
27669
+ );
27670
+ if (isClientAborted) {
27671
+ console.log(`[curriculum-kit:VercelAI] Client aborted or stream closed, halting candidate fallback loop.`);
27672
+ break;
27673
+ }
27629
27674
  } finally {
27630
27675
  abort.dispose();
27631
27676
  }
@@ -27677,7 +27722,8 @@ Return concise JSON matching:
27677
27722
  totalMs: options.timeoutMs,
27678
27723
  model: options.model,
27679
27724
  provider: options.provider
27680
- })
27725
+ }),
27726
+ options.signal
27681
27727
  );
27682
27728
  let parsedResearch = {
27683
27729
  groundTruthSummary: `Verified ecosystem standards for ${projectName}`,
@@ -27887,7 +27933,8 @@ REQUIREMENT: Synthesize ALL parameters above \u2014 every single line in FULL AC
27887
27933
  totalMs: options.timeoutMs,
27888
27934
  model: options.model,
27889
27935
  provider: options.provider
27890
- })
27936
+ }),
27937
+ options.signal
27891
27938
  );
27892
27939
  const parsed = safeParseJson(rawContent);
27893
27940
  return {