@thanh01.pmt/curriculum-kit 1.0.14 → 1.0.16

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
@@ -140,6 +140,10 @@ var init_errors = __esm({
140
140
  // src/ai/streamRunner.ts
141
141
  var streamRunner_exports = {};
142
142
  __export(streamRunner_exports, {
143
+ DEFAULT_ARTIFACT_STREAM_IDLE_MS: () => exports.DEFAULT_ARTIFACT_STREAM_IDLE_MS,
144
+ DEFAULT_ARTIFACT_STREAM_TOTAL_MS: () => exports.DEFAULT_ARTIFACT_STREAM_TOTAL_MS,
145
+ createIdleAbortController: () => createIdleAbortController,
146
+ createStreamAbortController: () => createStreamAbortController,
143
147
  extractThoughtAndContent: () => extractThoughtAndContent,
144
148
  getDesignatedFallbackChain: () => getDesignatedFallbackChain,
145
149
  getDesignatedFallbackConfig: () => getDesignatedFallbackConfig,
@@ -291,29 +295,44 @@ function extractThoughtAndContent(rawText) {
291
295
  content: content.trim()
292
296
  };
293
297
  }
294
- function createIdleAbortController(idleMs) {
298
+ function createStreamAbortController(options) {
299
+ const envIdle = Number(process.env.ARTIFACT_STREAM_IDLE_TIMEOUT_MS || process.env.STREAM_IDLE_TIMEOUT_MS);
300
+ const envTotal = Number(process.env.ARTIFACT_STREAM_TOTAL_TIMEOUT_MS || process.env.STREAM_TOTAL_TIMEOUT_MS);
301
+ const idleMs = Number.isFinite(options?.idleMs) && options.idleMs > 0 ? options.idleMs : Number.isFinite(envIdle) && envIdle > 0 ? envIdle : exports.DEFAULT_ARTIFACT_STREAM_IDLE_MS;
302
+ const totalMs = Number.isFinite(options?.totalMs) && options.totalMs > 0 ? options.totalMs : Number.isFinite(envTotal) && envTotal > 0 ? envTotal : exports.DEFAULT_ARTIFACT_STREAM_TOTAL_MS;
295
303
  const controller = new AbortController();
296
- let timer = null;
297
- const arm = () => {
298
- if (timer) clearTimeout(timer);
299
- timer = setTimeout(
304
+ let idleTimer = null;
305
+ let totalTimer = null;
306
+ const armIdle = () => {
307
+ if (idleTimer) clearTimeout(idleTimer);
308
+ idleTimer = setTimeout(
300
309
  () => controller.abort(new Error(`Idle timeout: no data for ${Math.round(idleMs / 1e3)}s`)),
301
310
  idleMs
302
311
  );
303
- timer?.unref?.();
312
+ idleTimer?.unref?.();
304
313
  };
305
- arm();
314
+ armIdle();
315
+ if (totalMs > 0) {
316
+ totalTimer = setTimeout(
317
+ () => controller.abort(new Error(`Total stream timeout after ${Math.round(totalMs / 1e3)}s`)),
318
+ totalMs
319
+ );
320
+ totalTimer?.unref?.();
321
+ }
306
322
  return {
307
323
  signal: controller.signal,
308
- /** Reset the idle window (call on every received chunk). */
309
- kick: arm,
310
- /** Clear the pending timer once the request lifecycle is over. */
324
+ kick: armIdle,
311
325
  dispose: () => {
312
- if (timer) clearTimeout(timer);
313
- timer = null;
326
+ if (idleTimer) clearTimeout(idleTimer);
327
+ if (totalTimer) clearTimeout(totalTimer);
328
+ idleTimer = null;
329
+ totalTimer = null;
314
330
  }
315
331
  };
316
332
  }
333
+ function createIdleAbortController(idleMs, totalMs) {
334
+ return createStreamAbortController({ idleMs, totalMs });
335
+ }
317
336
  async function processOpenAISSEStream(response, onChunk, idle, toolCallCollector) {
318
337
  if (!response.body) return "";
319
338
  const reader = response.body.getReader();
@@ -497,7 +516,7 @@ Guidelines:
497
516
  if (isNvidiaPreferred || uniqueNvidiaModels.length > 0) {
498
517
  for (const model of uniqueNvidiaModels) {
499
518
  try {
500
- const idle = createIdleAbortController(18e4);
519
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
501
520
  const res = await fetch("https://integrate.api.nvidia.com/v1/chat/completions", {
502
521
  method: "POST",
503
522
  headers: {
@@ -541,7 +560,7 @@ Guidelines:
541
560
  const uniqueOrModels = Array.from(new Set(openrouterModels));
542
561
  for (const model of uniqueOrModels) {
543
562
  try {
544
- const idle = createIdleAbortController(18e4);
563
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
545
564
  const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
546
565
  method: "POST",
547
566
  headers: {
@@ -580,7 +599,7 @@ Guidelines:
580
599
  const dsModel = options.model?.includes("deepseek-reasoner") ? "deepseek-reasoner" : "deepseek-chat";
581
600
  if (isModelAllowed(dsModel)) {
582
601
  try {
583
- const idle = createIdleAbortController(18e4);
602
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
584
603
  const res = await fetch("https://api.deepseek.com/chat/completions", {
585
604
  method: "POST",
586
605
  headers: {
@@ -615,7 +634,7 @@ Guidelines:
615
634
  const baseUrl = alibabaKey.startsWith("sk-sp-") ? "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions" : "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions";
616
635
  for (const qwenModel of qwenModels) {
617
636
  try {
618
- const idle = createIdleAbortController(18e4);
637
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
619
638
  const payload = {
620
639
  model: qwenModel,
621
640
  messages: formattedMessages,
@@ -658,7 +677,7 @@ Guidelines:
658
677
  ];
659
678
  for (const gModel of geminiModels) {
660
679
  try {
661
- const idle = createIdleAbortController(18e4);
680
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
662
681
  const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${gModel}:streamGenerateContent?alt=sse&key=${geminiKey}`, {
663
682
  method: "POST",
664
683
  headers: { "Content-Type": "application/json" },
@@ -694,7 +713,7 @@ Guidelines:
694
713
  const { provider, model } = target;
695
714
  if (provider === "nvidia" && nvidiaKey && isProviderEnabled("nvidia")) {
696
715
  try {
697
- const idle = createIdleAbortController(18e4);
716
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
698
717
  const res = await fetch("https://integrate.api.nvidia.com/v1/chat/completions", {
699
718
  method: "POST",
700
719
  headers: {
@@ -726,7 +745,7 @@ Guidelines:
726
745
  }
727
746
  } else if (provider === "openrouter" && openrouterKey && isProviderEnabled("openrouter")) {
728
747
  try {
729
- const idle = createIdleAbortController(18e4);
748
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
730
749
  const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
731
750
  method: "POST",
732
751
  headers: {
@@ -761,7 +780,7 @@ Guidelines:
761
780
  } else if ((provider === "alibaba" || provider === "dashscope") && alibabaKey && isProviderEnabled("alibaba")) {
762
781
  const baseUrl = alibabaKey.startsWith("sk-sp-") ? "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions" : "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions";
763
782
  try {
764
- const idle = createIdleAbortController(18e4);
783
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
765
784
  const res = await fetch(baseUrl, {
766
785
  method: "POST",
767
786
  headers: {
@@ -793,7 +812,7 @@ Guidelines:
793
812
  }
794
813
  } else if ((provider === "google" || provider === "gemini") && geminiKey && (isProviderEnabled("gemini") || isProviderEnabled("google"))) {
795
814
  try {
796
- const idle = createIdleAbortController(18e4);
815
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
797
816
  const res = await fetch(
798
817
  `https://generativelanguage.googleapis.com/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${geminiKey}`,
799
818
  {
@@ -844,9 +863,12 @@ Guidelines:
844
863
  async function runCurriculumAIInference(messages, projectContext, options = {}, onChunk) {
845
864
  return await streamCurriculumAIInference(messages, projectContext, options, onChunk);
846
865
  }
866
+ exports.DEFAULT_ARTIFACT_STREAM_IDLE_MS = void 0; exports.DEFAULT_ARTIFACT_STREAM_TOTAL_MS = void 0;
847
867
  var init_streamRunner = __esm({
848
868
  "src/ai/streamRunner.ts"() {
849
869
  init_errors();
870
+ exports.DEFAULT_ARTIFACT_STREAM_IDLE_MS = 45e3;
871
+ exports.DEFAULT_ARTIFACT_STREAM_TOTAL_MS = 42e4;
850
872
  }
851
873
  });
852
874
 
@@ -13525,11 +13547,11 @@ var DeterministicPipelineRunner = class {
13525
13547
  // src/services/prefillService.ts
13526
13548
  init_streamRunner();
13527
13549
  var DEFAULT_STREAM_IDLE_MS = 45e3;
13528
- var DEFAULT_STREAM_TOTAL_MS = 3e5;
13550
+ var DEFAULT_STREAM_TOTAL_MS = 42e4;
13529
13551
  var LAYER_TOTAL_BUDGET_MS = {
13530
- 1: 18e4,
13531
- 2: 36e4,
13532
- 3: 36e4
13552
+ 1: 42e4,
13553
+ 2: 42e4,
13554
+ 3: 42e4
13533
13555
  };
13534
13556
  function resolveStreamBudget(layer, opts) {
13535
13557
  const envIdle = Number(process.env.WIZARD_PREFILL_IDLE_TIMEOUT_MS);
@@ -13596,29 +13618,111 @@ function createStreamAbortSignal(budget) {
13596
13618
  }
13597
13619
  };
13598
13620
  }
13621
+ function closeTruncatedJson(candidate) {
13622
+ let inStr = false;
13623
+ let esc2 = false;
13624
+ const stack = [];
13625
+ for (let i = 0; i < candidate.length; i++) {
13626
+ const ch = candidate[i];
13627
+ if (esc2) {
13628
+ esc2 = false;
13629
+ continue;
13630
+ }
13631
+ if (inStr) {
13632
+ if (ch === "\\") esc2 = true;
13633
+ else if (ch === '"') inStr = false;
13634
+ continue;
13635
+ }
13636
+ if (ch === '"') inStr = true;
13637
+ else if (ch === "{" || ch === "[") stack.push(ch);
13638
+ else if (ch === "}" || ch === "]") stack.pop();
13639
+ }
13640
+ if (stack.length === 0 && !inStr) return null;
13641
+ let out = candidate;
13642
+ out = out.replace(/,\s*$/, "");
13643
+ if (inStr) {
13644
+ out = out.replace(/\\+$/, "");
13645
+ out += '"';
13646
+ }
13647
+ while (stack.length > 0) {
13648
+ out += stack.pop() === "{" ? "}" : "]";
13649
+ }
13650
+ return out;
13651
+ }
13652
+ function stripLlmJsonWrappers(rawText) {
13653
+ let cleaned = rawText.replace(/<think>[\s\S]*?<\/think>/gi, "").replace(/```json\s*/gi, "").replace(/```\s*/g, "").trim();
13654
+ const firstBrace = cleaned.indexOf("{");
13655
+ if (firstBrace > 0) {
13656
+ cleaned = cleaned.slice(firstBrace);
13657
+ }
13658
+ return cleaned.trim();
13659
+ }
13599
13660
  function safeParseJson(rawText) {
13600
13661
  if (!rawText) return null;
13601
- const cleaned = rawText.replace(/```json\s*/gi, "").replace(/```\s*/g, "").trim();
13662
+ const cleaned = stripLlmJsonWrappers(rawText);
13663
+ if (!cleaned) return null;
13602
13664
  try {
13603
13665
  return JSON.parse(cleaned);
13604
13666
  } catch {
13605
- const firstBrace = cleaned.indexOf("{");
13667
+ }
13668
+ const firstBrace = cleaned.indexOf("{");
13669
+ if (firstBrace === -1) return null;
13670
+ const buildCandidates = () => {
13671
+ const list = [];
13672
+ const closed = closeTruncatedJson(cleaned.slice(firstBrace));
13673
+ if (closed) list.push(closed);
13606
13674
  const lastBrace = cleaned.lastIndexOf("}");
13607
- if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
13608
- const candidate = cleaned.slice(firstBrace, lastBrace + 1);
13609
- try {
13610
- return JSON.parse(candidate);
13611
- } catch {
13675
+ if (lastBrace > firstBrace) {
13676
+ list.push(cleaned.slice(firstBrace, lastBrace + 1));
13677
+ }
13678
+ return list;
13679
+ };
13680
+ for (const candidate of buildCandidates()) {
13681
+ try {
13682
+ return JSON.parse(candidate);
13683
+ } catch {
13684
+ }
13685
+ try {
13686
+ return JSON.parse(jsonrepair.jsonrepair(candidate));
13687
+ } catch {
13688
+ }
13689
+ }
13690
+ let depth = 0;
13691
+ let inStr = false;
13692
+ let esc2 = false;
13693
+ for (let i = firstBrace; i < cleaned.length; i++) {
13694
+ const ch = cleaned[i];
13695
+ if (esc2) {
13696
+ esc2 = false;
13697
+ continue;
13698
+ }
13699
+ if (inStr) {
13700
+ if (ch === "\\") esc2 = true;
13701
+ else if (ch === '"') inStr = false;
13702
+ continue;
13703
+ }
13704
+ if (ch === '"') inStr = true;
13705
+ else if (ch === "{") depth++;
13706
+ else if (ch === "}") {
13707
+ depth--;
13708
+ if (depth === 0) {
13709
+ const candidate = cleaned.slice(firstBrace, i + 1);
13710
+ try {
13711
+ return JSON.parse(candidate);
13712
+ } catch {
13713
+ }
13612
13714
  try {
13613
- const repaired = jsonrepair.jsonrepair(candidate);
13614
- return JSON.parse(repaired);
13715
+ return JSON.parse(jsonrepair.jsonrepair(candidate));
13615
13716
  } catch {
13616
- return null;
13617
13717
  }
13618
13718
  }
13619
13719
  }
13620
- return null;
13621
13720
  }
13721
+ try {
13722
+ return JSON.parse(jsonrepair.jsonrepair(cleaned));
13723
+ } catch {
13724
+ }
13725
+ return null;
13622
13726
  }
13623
13727
  async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onChunk, preferredModel, preferredProvider, budget) {
13624
13728
  const alibabaKey = apiKeys.alibaba || apiKeys.dashscope || process.env.ALIBABA_API_KEY || process.env.DASHSCOPE_API_KEY;
@@ -16131,6 +16235,10 @@ function resolveApiKey2(options) {
16131
16235
  }
16132
16236
  return { provider, key };
16133
16237
  }
16238
+ function resolveImageTimeout(options) {
16239
+ const envTimeout = Number(process.env.IMAGE_GEN_TIMEOUT_MS || process.env.ARTIFACT_STREAM_TOTAL_TIMEOUT_MS);
16240
+ return Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : 42e4;
16241
+ }
16134
16242
  async function generateWithGemini(options, key) {
16135
16243
  const finalPrompt = buildImagePrompt(options);
16136
16244
  const model = "gemini-2.0-flash-exp";
@@ -16141,7 +16249,7 @@ async function generateWithGemini(options, key) {
16141
16249
  contents: [{ parts: [{ text: finalPrompt }] }],
16142
16250
  generationConfig: { responseModalities: ["TEXT", "IMAGE"] }
16143
16251
  }),
16144
- signal: AbortSignal.timeout(9e4)
16252
+ signal: AbortSignal.timeout(resolveImageTimeout(options))
16145
16253
  });
16146
16254
  if (!res.ok) {
16147
16255
  const body = await res.text().catch(() => "");
@@ -16174,7 +16282,7 @@ async function generateWithOpenAI(options, key) {
16174
16282
  size: sizeMap[options.aspectRatio || "1:1"] || "1024x1024",
16175
16283
  n: 1
16176
16284
  }),
16177
- signal: AbortSignal.timeout(9e4)
16285
+ signal: AbortSignal.timeout(resolveImageTimeout(options))
16178
16286
  });
16179
16287
  if (!res.ok) {
16180
16288
  const body = await res.text().catch(() => "");
@@ -16790,6 +16898,7 @@ exports.buildStandardsContext = buildStandardsContext;
16790
16898
  exports.buildStandardsContextBlock = buildStandardsContextBlock;
16791
16899
  exports.buildTeacherGuidePrompt = buildTeacherGuidePrompt;
16792
16900
  exports.buildWorksheetPrompt = buildWorksheetPrompt;
16901
+ exports.closeTruncatedJson = closeTruncatedJson;
16793
16902
  exports.computeContentHash = computeContentHash;
16794
16903
  exports.computePackingSpec = computePackingSpec;
16795
16904
  exports.conductPreliminaryResearch = conductPreliminaryResearch;
@@ -16797,6 +16906,8 @@ exports.contentTools = contentTools;
16797
16906
  exports.convertRoadmapToFoundationSot = convertRoadmapToFoundationSot;
16798
16907
  exports.createAiInferenceError = createAiInferenceError;
16799
16908
  exports.createCurriculumStorage = createCurriculumStorage;
16909
+ exports.createIdleAbortController = createIdleAbortController;
16910
+ exports.createStreamAbortController = createStreamAbortController;
16800
16911
  exports.createStreamAbortSignal = createStreamAbortSignal;
16801
16912
  exports.curateMediaLedger = curateMediaLedger;
16802
16913
  exports.designerTools = designerTools;
@@ -16896,6 +17007,7 @@ exports.streamLLMWithFallback = streamLLMWithFallback;
16896
17007
  exports.streamLayerPrefillWithFallback = streamLayerPrefillWithFallback;
16897
17008
  exports.streamLessonMasterFlow = streamLessonMasterFlow;
16898
17009
  exports.streamSelfLabFlow = streamSelfLabFlow;
17010
+ exports.stripLlmJsonWrappers = stripLlmJsonWrappers;
16899
17011
  exports.techSmeTools = techSmeTools;
16900
17012
  exports.topoSort = topoSort;
16901
17013
  exports.uploadAssetToBucket = uploadAssetToBucket;