@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/ai/index.cjs +5 -0
- package/dist/ai/index.cjs.map +1 -1
- package/dist/ai/index.mjs +5 -0
- package/dist/ai/index.mjs.map +1 -1
- package/dist/index.cjs +59 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +59 -12
- package/dist/index.mjs.map +1 -1
- package/dist/pipeline/index.cjs.map +1 -1
- package/dist/pipeline/index.mjs.map +1 -1
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +23 -22
- package/LICENSE +0 -21
package/dist/index.d.cts
CHANGED
|
@@ -365,6 +365,8 @@ interface LayerPrefillOptions {
|
|
|
365
365
|
timeoutMs?: number;
|
|
366
366
|
/** Idle (no-data) budget override for this prefill run (ms). */
|
|
367
367
|
idleTimeoutMs?: number;
|
|
368
|
+
/** Optional parent abort signal (e.g. from HTTP request) to stop immediately on client disconnect. */
|
|
369
|
+
signal?: AbortSignal;
|
|
368
370
|
}
|
|
369
371
|
interface PreliminaryResearchResult {
|
|
370
372
|
groundTruthSummary: string;
|
|
@@ -388,7 +390,7 @@ interface StreamBudget {
|
|
|
388
390
|
/** Abort when the whole stream exceeds this total wall time (ms). 0 disables. */
|
|
389
391
|
totalMs?: number;
|
|
390
392
|
}
|
|
391
|
-
declare const DEFAULT_STREAM_IDLE_MS =
|
|
393
|
+
declare const DEFAULT_STREAM_IDLE_MS = 180000;
|
|
392
394
|
declare const DEFAULT_STREAM_TOTAL_MS = 480000;
|
|
393
395
|
declare const LAYER_IDLE_BUDGET_MS: Record<number, number>;
|
|
394
396
|
/**
|
|
@@ -455,7 +457,7 @@ declare function createStreamChunkExtractor(): StreamChunkExtractor;
|
|
|
455
457
|
* createStreamChunkExtractor, whose latch is the authoritative dedupe.
|
|
456
458
|
*/
|
|
457
459
|
declare function extractStreamChunk(part: any): ExtractedStreamChunk;
|
|
458
|
-
declare function createStreamAbortSignal(budget: Required<StreamBudget
|
|
460
|
+
declare function createStreamAbortSignal(budget: Required<StreamBudget>, parentSignal?: AbortSignal): StreamAbortHandle;
|
|
459
461
|
/**
|
|
460
462
|
* Close an unterminated JSON object/array: append the delimiters that are
|
|
461
463
|
* still open, closing any open string first. Handles streams aborted
|
|
@@ -485,7 +487,7 @@ declare function safeParseJson<T = any>(rawText: string | null): T | null;
|
|
|
485
487
|
/**
|
|
486
488
|
* Multi-tiered resilient streaming inference helper using 100% Vercel AI SDK.
|
|
487
489
|
*/
|
|
488
|
-
declare function streamLLMWithFallback(systemPrompt: string, userPrompt: string, apiKeys?: Record<string, string>, onChunk?: LayerChunkCallback, preferredModel?: string, preferredProvider?: AIProviderName, budget?: StreamBudget): Promise<string | null>;
|
|
490
|
+
declare function streamLLMWithFallback(systemPrompt: string, userPrompt: string, apiKeys?: Record<string, string>, onChunk?: LayerChunkCallback, preferredModel?: string, preferredProvider?: AIProviderName, budget?: StreamBudget, signal?: AbortSignal): Promise<string | null>;
|
|
489
491
|
/**
|
|
490
492
|
* Conducts preliminary ecosystem research for a course project.
|
|
491
493
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -365,6 +365,8 @@ interface LayerPrefillOptions {
|
|
|
365
365
|
timeoutMs?: number;
|
|
366
366
|
/** Idle (no-data) budget override for this prefill run (ms). */
|
|
367
367
|
idleTimeoutMs?: number;
|
|
368
|
+
/** Optional parent abort signal (e.g. from HTTP request) to stop immediately on client disconnect. */
|
|
369
|
+
signal?: AbortSignal;
|
|
368
370
|
}
|
|
369
371
|
interface PreliminaryResearchResult {
|
|
370
372
|
groundTruthSummary: string;
|
|
@@ -388,7 +390,7 @@ interface StreamBudget {
|
|
|
388
390
|
/** Abort when the whole stream exceeds this total wall time (ms). 0 disables. */
|
|
389
391
|
totalMs?: number;
|
|
390
392
|
}
|
|
391
|
-
declare const DEFAULT_STREAM_IDLE_MS =
|
|
393
|
+
declare const DEFAULT_STREAM_IDLE_MS = 180000;
|
|
392
394
|
declare const DEFAULT_STREAM_TOTAL_MS = 480000;
|
|
393
395
|
declare const LAYER_IDLE_BUDGET_MS: Record<number, number>;
|
|
394
396
|
/**
|
|
@@ -455,7 +457,7 @@ declare function createStreamChunkExtractor(): StreamChunkExtractor;
|
|
|
455
457
|
* createStreamChunkExtractor, whose latch is the authoritative dedupe.
|
|
456
458
|
*/
|
|
457
459
|
declare function extractStreamChunk(part: any): ExtractedStreamChunk;
|
|
458
|
-
declare function createStreamAbortSignal(budget: Required<StreamBudget
|
|
460
|
+
declare function createStreamAbortSignal(budget: Required<StreamBudget>, parentSignal?: AbortSignal): StreamAbortHandle;
|
|
459
461
|
/**
|
|
460
462
|
* Close an unterminated JSON object/array: append the delimiters that are
|
|
461
463
|
* still open, closing any open string first. Handles streams aborted
|
|
@@ -485,7 +487,7 @@ declare function safeParseJson<T = any>(rawText: string | null): T | null;
|
|
|
485
487
|
/**
|
|
486
488
|
* Multi-tiered resilient streaming inference helper using 100% Vercel AI SDK.
|
|
487
489
|
*/
|
|
488
|
-
declare function streamLLMWithFallback(systemPrompt: string, userPrompt: string, apiKeys?: Record<string, string>, onChunk?: LayerChunkCallback, preferredModel?: string, preferredProvider?: AIProviderName, budget?: StreamBudget): Promise<string | null>;
|
|
490
|
+
declare function streamLLMWithFallback(systemPrompt: string, userPrompt: string, apiKeys?: Record<string, string>, onChunk?: LayerChunkCallback, preferredModel?: string, preferredProvider?: AIProviderName, budget?: StreamBudget, signal?: AbortSignal): Promise<string | null>;
|
|
489
491
|
/**
|
|
490
492
|
* Conducts preliminary ecosystem research for a course project.
|
|
491
493
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -5448,6 +5448,11 @@ Every slide MUST include notes with:
|
|
|
5448
5448
|
- Cold-Call: 1 check question
|
|
5449
5449
|
- Scaffolding Tip: 1 analogy or hint
|
|
5450
5450
|
|
|
5451
|
+
### \u26A0\uFE0F STRICT SLIDE FORMATTING INVARIANTS:
|
|
5452
|
+
1. Every slide MUST store its content fields inside "slots": { ... }. NEVER put "title", "content", or points at the top level of a slide!
|
|
5453
|
+
2. All code slots MUST contain real, executable, high-fidelity code. Never use placeholders like "<CODE>" or "...".
|
|
5454
|
+
3. All point and card slots MUST contain concrete, rich pedagogical explanations. Never use placeholder dots or empty cards.
|
|
5455
|
+
|
|
5451
5456
|
### Output JSON Format:
|
|
5452
5457
|
\`\`\`json
|
|
5453
5458
|
{
|
|
@@ -25315,10 +25320,15 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
25315
25320
|
const isHtmlEngine = options.slidesEngine === "html";
|
|
25316
25321
|
if (isHtmlEngine) {
|
|
25317
25322
|
let presentationKitAi = null;
|
|
25323
|
+
let presentationKitCore = null;
|
|
25318
25324
|
try {
|
|
25319
25325
|
presentationKitAi = await import('@thanh01.pmt/presentation-kit/ai');
|
|
25320
25326
|
} catch {
|
|
25321
25327
|
}
|
|
25328
|
+
try {
|
|
25329
|
+
presentationKitCore = await import('@thanh01.pmt/presentation-kit');
|
|
25330
|
+
} catch {
|
|
25331
|
+
}
|
|
25322
25332
|
const slidePrompt = buildHtmlDeckSlidePrompt({
|
|
25323
25333
|
lessonCode,
|
|
25324
25334
|
lessonTitle,
|
|
@@ -25353,12 +25363,21 @@ ${glossaryContext}` : void 0,
|
|
|
25353
25363
|
const candidate = fenceMatch ? fenceMatch[1] : str;
|
|
25354
25364
|
return candidate.trim();
|
|
25355
25365
|
};
|
|
25366
|
+
const normalizeDeckSlides = (deck) => {
|
|
25367
|
+
const normalizer = presentationKitCore?.normalizeSlideSlots || presentationKitAi?.normalizeSlideSlots;
|
|
25368
|
+
if (deck && Array.isArray(deck.slides) && normalizer) {
|
|
25369
|
+
deck.slides = deck.slides.map((s) => normalizer(s));
|
|
25370
|
+
}
|
|
25371
|
+
return deck;
|
|
25372
|
+
};
|
|
25356
25373
|
try {
|
|
25357
25374
|
deckJson = JSON.parse(extractCleanJson(rawSlide));
|
|
25375
|
+
deckJson = normalizeDeckSlides(deckJson);
|
|
25358
25376
|
} catch {
|
|
25359
25377
|
try {
|
|
25360
25378
|
const { jsonrepair: jsonrepair2 } = await import('jsonrepair');
|
|
25361
25379
|
deckJson = JSON.parse(jsonrepair2(extractCleanJson(rawSlide)));
|
|
25380
|
+
deckJson = normalizeDeckSlides(deckJson);
|
|
25362
25381
|
} catch {
|
|
25363
25382
|
}
|
|
25364
25383
|
}
|
|
@@ -25380,11 +25399,13 @@ Fix ALL issues and output the complete corrected JSON object strictly matching t
|
|
|
25380
25399
|
);
|
|
25381
25400
|
try {
|
|
25382
25401
|
deckJson = JSON.parse(extractCleanJson(repairedRaw));
|
|
25402
|
+
deckJson = normalizeDeckSlides(deckJson);
|
|
25383
25403
|
validation = presentationKitAi.validateHtmlSlideDeck(deckJson, true);
|
|
25384
25404
|
} catch {
|
|
25385
25405
|
try {
|
|
25386
25406
|
const { jsonrepair: jsonrepair2 } = await import('jsonrepair');
|
|
25387
25407
|
deckJson = JSON.parse(jsonrepair2(extractCleanJson(repairedRaw)));
|
|
25408
|
+
deckJson = normalizeDeckSlides(deckJson);
|
|
25388
25409
|
validation = presentationKitAi.validateHtmlSlideDeck(deckJson, true);
|
|
25389
25410
|
} catch {
|
|
25390
25411
|
}
|
|
@@ -25412,9 +25433,10 @@ ${JSON.stringify(deckJson || {}, null, 2)}
|
|
|
25412
25433
|
const deckJsonStr = JSON.stringify(deckJson, null, 2);
|
|
25413
25434
|
await storage.saveArtifact(projectId, `_content/${unitCode}/SLIDE_${lessonCode}.deck.json`, deckJsonStr);
|
|
25414
25435
|
producedArtifacts.push(`SLIDE_${lessonCode}.deck.json`);
|
|
25415
|
-
|
|
25436
|
+
const compiler = presentationKitCore?.compileHtmlDeck || presentationKitAi?.compileHtmlDeck;
|
|
25437
|
+
if (compiler) {
|
|
25416
25438
|
try {
|
|
25417
|
-
const compiled =
|
|
25439
|
+
const compiled = compiler(deckJson);
|
|
25418
25440
|
if (compiled?.html) {
|
|
25419
25441
|
await storage.saveArtifact(projectId, `_content/${unitCode}/SLIDE_${lessonCode}.html`, compiled.html);
|
|
25420
25442
|
producedArtifacts.push(`SLIDE_${lessonCode}.html`);
|
|
@@ -27291,13 +27313,13 @@ var DeterministicPipelineRunner = class {
|
|
|
27291
27313
|
|
|
27292
27314
|
// src/services/prefillService.ts
|
|
27293
27315
|
init_streamRunner();
|
|
27294
|
-
var DEFAULT_STREAM_IDLE_MS =
|
|
27316
|
+
var DEFAULT_STREAM_IDLE_MS = 18e4;
|
|
27295
27317
|
var DEFAULT_STREAM_TOTAL_MS = 48e4;
|
|
27296
27318
|
var LAYER_IDLE_BUDGET_MS = {
|
|
27297
|
-
1:
|
|
27298
|
-
2:
|
|
27319
|
+
1: 18e4,
|
|
27320
|
+
2: 18e4,
|
|
27299
27321
|
3: 18e4
|
|
27300
|
-
//
|
|
27322
|
+
// 3 minutes idle protection across all layers for free-tier reasoning models
|
|
27301
27323
|
};
|
|
27302
27324
|
var LAYER_TOTAL_BUDGET_MS = {
|
|
27303
27325
|
1: 48e4,
|
|
@@ -27358,10 +27380,20 @@ function extractStreamChunk(part) {
|
|
|
27358
27380
|
}
|
|
27359
27381
|
return {};
|
|
27360
27382
|
}
|
|
27361
|
-
function createStreamAbortSignal(budget) {
|
|
27383
|
+
function createStreamAbortSignal(budget, parentSignal) {
|
|
27362
27384
|
const controller = new AbortController();
|
|
27363
27385
|
let idleTimer = null;
|
|
27364
27386
|
let totalTimer = null;
|
|
27387
|
+
const onParentAbort = () => {
|
|
27388
|
+
controller.abort(parentSignal?.reason || new Error("Aborted by parent signal"));
|
|
27389
|
+
};
|
|
27390
|
+
if (parentSignal) {
|
|
27391
|
+
if (parentSignal.aborted) {
|
|
27392
|
+
controller.abort(parentSignal.reason || new Error("Parent signal already aborted"));
|
|
27393
|
+
} else {
|
|
27394
|
+
parentSignal.addEventListener("abort", onParentAbort, { once: true });
|
|
27395
|
+
}
|
|
27396
|
+
}
|
|
27365
27397
|
const armIdle = () => {
|
|
27366
27398
|
if (idleTimer) clearTimeout(idleTimer);
|
|
27367
27399
|
if (budget.idleMs > 0) {
|
|
@@ -27388,6 +27420,9 @@ function createStreamAbortSignal(budget) {
|
|
|
27388
27420
|
dispose: () => {
|
|
27389
27421
|
if (idleTimer) clearTimeout(idleTimer);
|
|
27390
27422
|
if (totalTimer) clearTimeout(totalTimer);
|
|
27423
|
+
if (parentSignal) {
|
|
27424
|
+
parentSignal.removeEventListener("abort", onParentAbort);
|
|
27425
|
+
}
|
|
27391
27426
|
idleTimer = null;
|
|
27392
27427
|
totalTimer = null;
|
|
27393
27428
|
}
|
|
@@ -27499,7 +27534,7 @@ function safeParseJson(rawText) {
|
|
|
27499
27534
|
}
|
|
27500
27535
|
return null;
|
|
27501
27536
|
}
|
|
27502
|
-
async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onChunk, preferredModel, preferredProvider, budget) {
|
|
27537
|
+
async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onChunk, preferredModel, preferredProvider, budget, signal) {
|
|
27503
27538
|
const alibabaKey = apiKeys.alibaba || apiKeys.dashscope || process.env.ALIBABA_API_KEY || process.env.DASHSCOPE_API_KEY;
|
|
27504
27539
|
const nvidiaKey = apiKeys.nvidia || process.env.NVIDIA_API_KEY;
|
|
27505
27540
|
const deepseekKey = apiKeys.deepseek || process.env.DEEPSEEK_API_KEY;
|
|
@@ -27531,7 +27566,6 @@ async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onC
|
|
|
27531
27566
|
const nemotronModels = [
|
|
27532
27567
|
"nvidia/nemotron-3-ultra-550b-a55b",
|
|
27533
27568
|
"nvidia/nemotron-3.5-lightning-30b-a3b",
|
|
27534
|
-
"nvidia/nemotron-3-super-120b-a12b",
|
|
27535
27569
|
"nvidia/llama-3.1-nemotron-70b-instruct"
|
|
27536
27570
|
].filter((m) => isModelAllowed(m));
|
|
27537
27571
|
for (const m of nemotronModels) {
|
|
@@ -27582,8 +27616,12 @@ THINKING DIRECTIVE: Do ALL your reasoning internally BEFORE emitting output. Do
|
|
|
27582
27616
|
totalMs: budget?.totalMs ?? DEFAULT_STREAM_TOTAL_MS
|
|
27583
27617
|
};
|
|
27584
27618
|
for (const candidate of candidates) {
|
|
27619
|
+
if (signal?.aborted) {
|
|
27620
|
+
console.log(`[curriculum-kit:VercelAI] AbortSignal already aborted, cancelling candidate loop.`);
|
|
27621
|
+
break;
|
|
27622
|
+
}
|
|
27585
27623
|
const t0 = Date.now();
|
|
27586
|
-
const abort = createStreamAbortSignal(resolvedBudget);
|
|
27624
|
+
const abort = createStreamAbortSignal(resolvedBudget, signal);
|
|
27587
27625
|
try {
|
|
27588
27626
|
const modelInstance = getAIModel({
|
|
27589
27627
|
provider: candidate.provider,
|
|
@@ -27615,6 +27653,13 @@ THINKING DIRECTIVE: Do ALL your reasoning internally BEFORE emitting output. Do
|
|
|
27615
27653
|
}
|
|
27616
27654
|
} catch (e) {
|
|
27617
27655
|
console.warn(`[curriculum-kit:VercelAI] Candidate ${candidate.provider} (${candidate.model}) failed in ${Date.now() - t0}ms:`, e?.message || e);
|
|
27656
|
+
const isClientAborted = Boolean(
|
|
27657
|
+
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"))
|
|
27658
|
+
);
|
|
27659
|
+
if (isClientAborted) {
|
|
27660
|
+
console.log(`[curriculum-kit:VercelAI] Client aborted or stream closed, halting candidate fallback loop.`);
|
|
27661
|
+
break;
|
|
27662
|
+
}
|
|
27618
27663
|
} finally {
|
|
27619
27664
|
abort.dispose();
|
|
27620
27665
|
}
|
|
@@ -27666,7 +27711,8 @@ Return concise JSON matching:
|
|
|
27666
27711
|
totalMs: options.timeoutMs,
|
|
27667
27712
|
model: options.model,
|
|
27668
27713
|
provider: options.provider
|
|
27669
|
-
})
|
|
27714
|
+
}),
|
|
27715
|
+
options.signal
|
|
27670
27716
|
);
|
|
27671
27717
|
let parsedResearch = {
|
|
27672
27718
|
groundTruthSummary: `Verified ecosystem standards for ${projectName}`,
|
|
@@ -27876,7 +27922,8 @@ REQUIREMENT: Synthesize ALL parameters above \u2014 every single line in FULL AC
|
|
|
27876
27922
|
totalMs: options.timeoutMs,
|
|
27877
27923
|
model: options.model,
|
|
27878
27924
|
provider: options.provider
|
|
27879
|
-
})
|
|
27925
|
+
}),
|
|
27926
|
+
options.signal
|
|
27880
27927
|
);
|
|
27881
27928
|
const parsed = safeParseJson(rawContent);
|
|
27882
27929
|
return {
|