@thanh01.pmt/curriculum-kit 1.0.15 → 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.
@@ -48,6 +48,10 @@ function resolveApiKey(options) {
48
48
  }
49
49
  return { provider, key };
50
50
  }
51
+ function resolveImageTimeout(options) {
52
+ const envTimeout = Number(process.env.IMAGE_GEN_TIMEOUT_MS || process.env.ARTIFACT_STREAM_TOTAL_TIMEOUT_MS);
53
+ return Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : 42e4;
54
+ }
51
55
  async function generateWithGemini(options, key) {
52
56
  const finalPrompt = buildImagePrompt(options);
53
57
  const model = "gemini-2.0-flash-exp";
@@ -58,7 +62,7 @@ async function generateWithGemini(options, key) {
58
62
  contents: [{ parts: [{ text: finalPrompt }] }],
59
63
  generationConfig: { responseModalities: ["TEXT", "IMAGE"] }
60
64
  }),
61
- signal: AbortSignal.timeout(9e4)
65
+ signal: AbortSignal.timeout(resolveImageTimeout(options))
62
66
  });
63
67
  if (!res.ok) {
64
68
  const body = await res.text().catch(() => "");
@@ -91,7 +95,7 @@ async function generateWithOpenAI(options, key) {
91
95
  size: sizeMap[options.aspectRatio || "1:1"] || "1024x1024",
92
96
  n: 1
93
97
  }),
94
- signal: AbortSignal.timeout(9e4)
98
+ signal: AbortSignal.timeout(resolveImageTimeout(options))
95
99
  });
96
100
  if (!res.ok) {
97
101
  const body = await res.text().catch(() => "");
@@ -502,26 +506,40 @@ function extractThoughtAndContent(rawText) {
502
506
  content: content.trim()
503
507
  };
504
508
  }
505
- function createIdleAbortController(idleMs) {
509
+ var DEFAULT_ARTIFACT_STREAM_IDLE_MS = 45e3;
510
+ var DEFAULT_ARTIFACT_STREAM_TOTAL_MS = 42e4;
511
+ function createStreamAbortController(options) {
512
+ const envIdle = Number(process.env.ARTIFACT_STREAM_IDLE_TIMEOUT_MS || process.env.STREAM_IDLE_TIMEOUT_MS);
513
+ const envTotal = Number(process.env.ARTIFACT_STREAM_TOTAL_TIMEOUT_MS || process.env.STREAM_TOTAL_TIMEOUT_MS);
514
+ const idleMs = Number.isFinite(options?.idleMs) && options.idleMs > 0 ? options.idleMs : Number.isFinite(envIdle) && envIdle > 0 ? envIdle : DEFAULT_ARTIFACT_STREAM_IDLE_MS;
515
+ const totalMs = Number.isFinite(options?.totalMs) && options.totalMs > 0 ? options.totalMs : Number.isFinite(envTotal) && envTotal > 0 ? envTotal : DEFAULT_ARTIFACT_STREAM_TOTAL_MS;
506
516
  const controller = new AbortController();
507
- let timer = null;
508
- const arm = () => {
509
- if (timer) clearTimeout(timer);
510
- timer = setTimeout(
517
+ let idleTimer = null;
518
+ let totalTimer = null;
519
+ const armIdle = () => {
520
+ if (idleTimer) clearTimeout(idleTimer);
521
+ idleTimer = setTimeout(
511
522
  () => controller.abort(new Error(`Idle timeout: no data for ${Math.round(idleMs / 1e3)}s`)),
512
523
  idleMs
513
524
  );
514
- timer?.unref?.();
525
+ idleTimer?.unref?.();
515
526
  };
516
- arm();
527
+ armIdle();
528
+ if (totalMs > 0) {
529
+ totalTimer = setTimeout(
530
+ () => controller.abort(new Error(`Total stream timeout after ${Math.round(totalMs / 1e3)}s`)),
531
+ totalMs
532
+ );
533
+ totalTimer?.unref?.();
534
+ }
517
535
  return {
518
536
  signal: controller.signal,
519
- /** Reset the idle window (call on every received chunk). */
520
- kick: arm,
521
- /** Clear the pending timer once the request lifecycle is over. */
537
+ kick: armIdle,
522
538
  dispose: () => {
523
- if (timer) clearTimeout(timer);
524
- timer = null;
539
+ if (idleTimer) clearTimeout(idleTimer);
540
+ if (totalTimer) clearTimeout(totalTimer);
541
+ idleTimer = null;
542
+ totalTimer = null;
525
543
  }
526
544
  };
527
545
  }
@@ -704,7 +722,7 @@ Guidelines:
704
722
  if (isNvidiaPreferred || uniqueNvidiaModels.length > 0) {
705
723
  for (const model of uniqueNvidiaModels) {
706
724
  try {
707
- const idle = createIdleAbortController(18e4);
725
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
708
726
  const res = await fetch("https://integrate.api.nvidia.com/v1/chat/completions", {
709
727
  method: "POST",
710
728
  headers: {
@@ -748,7 +766,7 @@ Guidelines:
748
766
  const uniqueOrModels = Array.from(new Set(openrouterModels));
749
767
  for (const model of uniqueOrModels) {
750
768
  try {
751
- const idle = createIdleAbortController(18e4);
769
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
752
770
  const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
753
771
  method: "POST",
754
772
  headers: {
@@ -787,7 +805,7 @@ Guidelines:
787
805
  const dsModel = options.model?.includes("deepseek-reasoner") ? "deepseek-reasoner" : "deepseek-chat";
788
806
  if (isModelAllowed(dsModel)) {
789
807
  try {
790
- const idle = createIdleAbortController(18e4);
808
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
791
809
  const res = await fetch("https://api.deepseek.com/chat/completions", {
792
810
  method: "POST",
793
811
  headers: {
@@ -822,7 +840,7 @@ Guidelines:
822
840
  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";
823
841
  for (const qwenModel of qwenModels) {
824
842
  try {
825
- const idle = createIdleAbortController(18e4);
843
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
826
844
  const payload = {
827
845
  model: qwenModel,
828
846
  messages: formattedMessages,
@@ -865,7 +883,7 @@ Guidelines:
865
883
  ];
866
884
  for (const gModel of geminiModels) {
867
885
  try {
868
- const idle = createIdleAbortController(18e4);
886
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
869
887
  const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${gModel}:streamGenerateContent?alt=sse&key=${geminiKey}`, {
870
888
  method: "POST",
871
889
  headers: { "Content-Type": "application/json" },
@@ -901,7 +919,7 @@ Guidelines:
901
919
  const { provider, model } = target;
902
920
  if (provider === "nvidia" && nvidiaKey && isProviderEnabled("nvidia")) {
903
921
  try {
904
- const idle = createIdleAbortController(18e4);
922
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
905
923
  const res = await fetch("https://integrate.api.nvidia.com/v1/chat/completions", {
906
924
  method: "POST",
907
925
  headers: {
@@ -933,7 +951,7 @@ Guidelines:
933
951
  }
934
952
  } else if (provider === "openrouter" && openrouterKey && isProviderEnabled("openrouter")) {
935
953
  try {
936
- const idle = createIdleAbortController(18e4);
954
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
937
955
  const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
938
956
  method: "POST",
939
957
  headers: {
@@ -968,7 +986,7 @@ Guidelines:
968
986
  } else if ((provider === "alibaba" || provider === "dashscope") && alibabaKey && isProviderEnabled("alibaba")) {
969
987
  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";
970
988
  try {
971
- const idle = createIdleAbortController(18e4);
989
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
972
990
  const res = await fetch(baseUrl, {
973
991
  method: "POST",
974
992
  headers: {
@@ -1000,7 +1018,7 @@ Guidelines:
1000
1018
  }
1001
1019
  } else if ((provider === "google" || provider === "gemini") && geminiKey && (isProviderEnabled("gemini") || isProviderEnabled("google"))) {
1002
1020
  try {
1003
- const idle = createIdleAbortController(18e4);
1021
+ const idle = createStreamAbortController({ idleMs: options.idleTimeoutMs, totalMs: options.timeoutMs });
1004
1022
  const res = await fetch(
1005
1023
  `https://generativelanguage.googleapis.com/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${geminiKey}`,
1006
1024
  {