@thanh01.pmt/curriculum-kit 1.4.33 → 1.4.35

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.
@@ -2,8 +2,8 @@ import { M as ModelResolutionOptions } from '../provider-factory-Bv-eIkhg.cjs';
2
2
  export { A as AIProviderName, N as NVIDIA_MODELS, g as getAIModel } from '../provider-factory-Bv-eIkhg.cjs';
3
3
  import { MilestoneInput, LessonPlan, ActivityLab, ProjectGraph, CodeLab, Worksheet, Handout, SlideDeck, TeacherGuide, Extension, CurriculumQualityReport, ProjectInstruction, SelfLab, DiagnosticQuiz } from '../schemas/index.cjs';
4
4
  import * as ai from 'ai';
5
- import { C as ChatMessage, S as StreamRunnerOptions, n as ToolLoopConfig, c as ChunkType } from '../streamRunner-B0Z1nAG5.cjs';
6
- export { A as AggregatedToolCall, D as DEFAULT_ARTIFACT_STREAM_IDLE_MS, d as DEFAULT_ARTIFACT_STREAM_TOTAL_MS, F as FallbackTarget, I as IdleAbort, f as StreamAbortController, T as ToolDefinition, l as ToolExecutionResult, m as ToolExecutor, j as createIdleAbortController, h as createStreamAbortController, e as extractThoughtAndContent, g as getDesignatedFallbackChain, b as getDesignatedFallbackConfig, a as isModelAllowed, i as isProviderEnabled, r as resolveApiKey, k as runCurriculumAIInference, s as streamCurriculumAIInference } from '../streamRunner-B0Z1nAG5.cjs';
5
+ import { a as ChatMessage, S as StreamRunnerOptions, n as ToolLoopConfig, C as ChunkType } from '../streamRunner-BzlJrAGZ.cjs';
6
+ export { A as AggregatedToolCall, D as DEFAULT_ARTIFACT_STREAM_IDLE_MS, d as DEFAULT_ARTIFACT_STREAM_TOTAL_MS, F as FallbackTarget, I as IdleAbort, f as StreamAbortController, T as ToolDefinition, l as ToolExecutionResult, m as ToolExecutor, j as createIdleAbortController, h as createStreamAbortController, e as extractThoughtAndContent, g as getDesignatedFallbackChain, c as getDesignatedFallbackConfig, b as isModelAllowed, i as isProviderEnabled, r as resolveApiKey, k as runCurriculumAIInference, s as streamCurriculumAIInference } from '../streamRunner-BzlJrAGZ.cjs';
7
7
  import { z } from 'zod';
8
8
 
9
9
  interface LessonPromptInput {
@@ -2,8 +2,8 @@ import { M as ModelResolutionOptions } from '../provider-factory-Bv-eIkhg.js';
2
2
  export { A as AIProviderName, N as NVIDIA_MODELS, g as getAIModel } from '../provider-factory-Bv-eIkhg.js';
3
3
  import { MilestoneInput, LessonPlan, ActivityLab, ProjectGraph, CodeLab, Worksheet, Handout, SlideDeck, TeacherGuide, Extension, CurriculumQualityReport, ProjectInstruction, SelfLab, DiagnosticQuiz } from '../schemas/index.js';
4
4
  import * as ai from 'ai';
5
- import { C as ChatMessage, S as StreamRunnerOptions, n as ToolLoopConfig, c as ChunkType } from '../streamRunner-B0Z1nAG5.js';
6
- export { A as AggregatedToolCall, D as DEFAULT_ARTIFACT_STREAM_IDLE_MS, d as DEFAULT_ARTIFACT_STREAM_TOTAL_MS, F as FallbackTarget, I as IdleAbort, f as StreamAbortController, T as ToolDefinition, l as ToolExecutionResult, m as ToolExecutor, j as createIdleAbortController, h as createStreamAbortController, e as extractThoughtAndContent, g as getDesignatedFallbackChain, b as getDesignatedFallbackConfig, a as isModelAllowed, i as isProviderEnabled, r as resolveApiKey, k as runCurriculumAIInference, s as streamCurriculumAIInference } from '../streamRunner-B0Z1nAG5.js';
5
+ import { a as ChatMessage, S as StreamRunnerOptions, n as ToolLoopConfig, C as ChunkType } from '../streamRunner-BzlJrAGZ.js';
6
+ export { A as AggregatedToolCall, D as DEFAULT_ARTIFACT_STREAM_IDLE_MS, d as DEFAULT_ARTIFACT_STREAM_TOTAL_MS, F as FallbackTarget, I as IdleAbort, f as StreamAbortController, T as ToolDefinition, l as ToolExecutionResult, m as ToolExecutor, j as createIdleAbortController, h as createStreamAbortController, e as extractThoughtAndContent, g as getDesignatedFallbackChain, c as getDesignatedFallbackConfig, b as isModelAllowed, i as isProviderEnabled, r as resolveApiKey, k as runCurriculumAIInference, s as streamCurriculumAIInference } from '../streamRunner-BzlJrAGZ.js';
7
7
  import { z } from 'zod';
8
8
 
9
9
  interface LessonPromptInput {
package/dist/index.cjs CHANGED
@@ -29519,7 +29519,7 @@ function safeParseJson(rawText) {
29519
29519
  }
29520
29520
  return null;
29521
29521
  }
29522
- async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onChunk, preferredModel, preferredProvider, budget, signal) {
29522
+ async function streamLLMWithFallback(systemPrompt, userPrompt, apiKeys = {}, onChunk, preferredModel, preferredProvider, budget, signal, onEvent) {
29523
29523
  const alibabaKey = apiKeys.alibaba || apiKeys.dashscope || process.env.ALIBABA_API_KEY || process.env.DASHSCOPE_API_KEY;
29524
29524
  const nvidiaKey = apiKeys.nvidia || process.env.NVIDIA_API_KEY;
29525
29525
  const deepseekKey = apiKeys.deepseek || process.env.DEEPSEEK_API_KEY;
@@ -29600,12 +29600,14 @@ THINKING DIRECTIVE: Do ALL your reasoning internally BEFORE emitting output. Do
29600
29600
  idleMs: budget?.idleMs ?? DEFAULT_STREAM_IDLE_MS,
29601
29601
  totalMs: budget?.totalMs ?? DEFAULT_STREAM_TOTAL_MS
29602
29602
  };
29603
- for (const candidate of candidates) {
29603
+ for (const [candidateIdx, candidate] of candidates.entries()) {
29604
29604
  if (signal?.aborted) {
29605
29605
  console.log(`[curriculum-kit:VercelAI] AbortSignal already aborted, cancelling candidate loop.`);
29606
29606
  break;
29607
29607
  }
29608
29608
  const t0 = Date.now();
29609
+ const attempt = candidateIdx + 1;
29610
+ onEvent?.({ type: "candidate-start", provider: candidate.provider, model: candidate.model, attempt, total: candidates.length, elapsedMs: 0 });
29609
29611
  const abort = createStreamAbortSignal(resolvedBudget, signal);
29610
29612
  try {
29611
29613
  const modelInstance = getAIModel({
@@ -29613,18 +29615,35 @@ THINKING DIRECTIVE: Do ALL your reasoning internally BEFORE emitting output. Do
29613
29615
  modelName: candidate.model,
29614
29616
  apiKey: candidate.apiKey
29615
29617
  });
29618
+ const wireFetch = (input, init) => fetch(input, init).then((res) => {
29619
+ if (!res.body) return res;
29620
+ const kicked = res.body.pipeThrough(
29621
+ new TransformStream({
29622
+ transform(chunk, ctrl) {
29623
+ abort.kick();
29624
+ ctrl.enqueue(chunk);
29625
+ }
29626
+ })
29627
+ );
29628
+ return new Response(kicked, res);
29629
+ });
29616
29630
  const streamResult = ai.streamText({
29617
29631
  model: modelInstance,
29618
29632
  system: systemInstructions,
29619
29633
  prompt: userPrompt,
29620
29634
  temperature: 0.2,
29621
29635
  includeRawChunks: true,
29622
- abortSignal: abort.signal
29636
+ abortSignal: abort.signal,
29637
+ fetch: wireFetch
29623
29638
  });
29624
29639
  let fullContent = "";
29625
29640
  const extract = createStreamChunkExtractor();
29626
29641
  for await (const part of streamResult.fullStream) {
29627
29642
  abort.kick();
29643
+ if (part?.type === "error") {
29644
+ const errText = part.errorText ?? String(part.error ?? "unknown stream error");
29645
+ throw new Error(`[${candidate.provider}] stream error part: ${errText}`);
29646
+ }
29628
29647
  const extracted = extract(part);
29629
29648
  if (extracted.thought) onChunk?.(extracted.thought, "thought");
29630
29649
  if (extracted.content) {
@@ -29634,10 +29653,14 @@ THINKING DIRECTIVE: Do ALL your reasoning internally BEFORE emitting output. Do
29634
29653
  }
29635
29654
  if (fullContent.trim()) {
29636
29655
  console.log(`[curriculum-kit:VercelAI] \u2705 ${candidate.provider} (${candidate.model}) streamed in ${Date.now() - t0}ms`);
29656
+ onEvent?.({ type: "candidate-success", provider: candidate.provider, model: candidate.model, attempt, total: candidates.length, elapsedMs: Date.now() - t0 });
29637
29657
  return fullContent.trim();
29638
29658
  }
29659
+ console.warn(`[curriculum-kit:VercelAI] Candidate ${candidate.provider} (${candidate.model}) returned EMPTY content in ${Date.now() - t0}ms \u2192 trying next candidate.`);
29660
+ onEvent?.({ type: "candidate-failed", provider: candidate.provider, model: candidate.model, attempt, total: candidates.length, elapsedMs: Date.now() - t0, error: "empty content" });
29639
29661
  } catch (e) {
29640
29662
  console.warn(`[curriculum-kit:VercelAI] Candidate ${candidate.provider} (${candidate.model}) failed in ${Date.now() - t0}ms:`, e?.message || e);
29663
+ onEvent?.({ type: "candidate-failed", provider: candidate.provider, model: candidate.model, attempt, total: candidates.length, elapsedMs: Date.now() - t0, error: e?.message || String(e) });
29641
29664
  const isClientAborted = Boolean(
29642
29665
  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"))
29643
29666
  );
@@ -29680,25 +29703,40 @@ Return concise JSON matching:
29680
29703
  "technicalGotchas": ["Important safety or compatibility note 1", "Note 2"]
29681
29704
  }`;
29682
29705
  let rawContent = "";
29683
- await streamLLMWithFallback(
29684
- "You are an authoritative STEM & CS curriculum researcher. Output valid JSON only.",
29685
- researchPrompt,
29686
- apiKeys,
29687
- (chunk, type) => {
29688
- if (type === "content") rawContent += chunk;
29689
- onChunk?.(chunk, type);
29690
- },
29691
- options.model,
29692
- options.provider,
29693
- // Research may run live web grounding — generous budget, still idle-guarded.
29694
- resolveStreamBudget(void 0, {
29695
- idleMs: options.idleTimeoutMs,
29696
- totalMs: options.timeoutMs,
29697
- model: options.model,
29698
- provider: options.provider
29699
- }),
29700
- options.signal
29701
- );
29706
+ if (options.customInference) {
29707
+ rawContent = await options.customInference({
29708
+ systemInstruction: "You are an authoritative STEM & CS curriculum researcher. Output valid JSON only.",
29709
+ userPrompt: researchPrompt,
29710
+ messages: [{ role: "user", content: researchPrompt }],
29711
+ temperature: 0.2,
29712
+ maxTokens: 32768,
29713
+ onChunk: (token, type) => {
29714
+ if (type === "content") rawContent += token;
29715
+ if (type === "content" || type === "thought") onChunk?.(token, type);
29716
+ }
29717
+ }) || "";
29718
+ } else {
29719
+ await streamLLMWithFallback(
29720
+ "You are an authoritative STEM & CS curriculum researcher. Output valid JSON only.",
29721
+ researchPrompt,
29722
+ apiKeys,
29723
+ (chunk, type) => {
29724
+ if (type === "content") rawContent += chunk;
29725
+ onChunk?.(chunk, type);
29726
+ },
29727
+ options.model,
29728
+ options.provider,
29729
+ // Research may run live web grounding — generous budget, still idle-guarded.
29730
+ resolveStreamBudget(void 0, {
29731
+ idleMs: options.idleTimeoutMs,
29732
+ totalMs: options.timeoutMs,
29733
+ model: options.model,
29734
+ provider: options.provider
29735
+ }),
29736
+ options.signal,
29737
+ options.onProviderEvent
29738
+ );
29739
+ }
29702
29740
  let parsedResearch = {
29703
29741
  groundTruthSummary: `Verified ecosystem standards for ${projectName}`,
29704
29742
  hardwareVersion: "Standard Environment",
@@ -29779,6 +29817,23 @@ async function streamLayerPrefillWithFallback(options, onChunk) {
29779
29817
  const research = await conductPreliminaryResearch(options, onChunk);
29780
29818
  return { success: true, research };
29781
29819
  }
29820
+ const runWithCustomInference = async (systemInstruction, userPrompt2) => {
29821
+ if (!options.customInference) return "";
29822
+ return options.customInference({
29823
+ systemInstruction,
29824
+ userPrompt: userPrompt2,
29825
+ messages: [{ role: "user", content: userPrompt2 }],
29826
+ temperature: 0.2,
29827
+ // Layer 3 synthesizes full context — give reasoning models the same
29828
+ // generous ceiling the artifact paths use (32k) so JSON is never truncated.
29829
+ maxTokens: 32768,
29830
+ onChunk: (token, type) => {
29831
+ if (type === "content") rawContent += token;
29832
+ if (type === "content" || type === "thought") onChunk?.(token, type);
29833
+ }
29834
+ });
29835
+ };
29836
+ let rawContent = "";
29782
29837
  const {
29783
29838
  projectName = "Curriculum Course",
29784
29839
  projectCode = "curriculum-course",
@@ -29890,27 +29945,31 @@ FULL ACCUMULATED CONTEXT (ALL CONFIRMED PARAMETERS FROM EVERY PREVIOUS STEP \u20
29890
29945
  ${buildRemainingContextBlock(accumulatedData) || "(no additional context)"}
29891
29946
 
29892
29947
  REQUIREMENT: Synthesize ALL parameters above \u2014 every single line in FULL ACCUMULATED CONTEXT is a confirmed user decision or system constraint and MUST influence the generated configuration. Produce the high-fidelity pre-fill configuration for LAYER ${targetLayer}.`;
29893
- let rawContent = "";
29894
- await streamLLMWithFallback(
29895
- systemPrompt,
29896
- userPrompt,
29897
- apiKeys,
29898
- (chunk, type) => {
29899
- if (type === "content") rawContent += chunk;
29900
- onChunk?.(chunk, type);
29901
- },
29902
- options.model,
29903
- options.provider,
29904
- // RC-W1: layer-aware budget — Layer 3 synthesizes full context and free-tier
29905
- // models (especially NVIDIA NIM) may think/stream for minutes. Idle window still catches hangs.
29906
- resolveStreamBudget(targetLayer, {
29907
- idleMs: options.idleTimeoutMs,
29908
- totalMs: options.timeoutMs,
29909
- model: options.model,
29910
- provider: options.provider
29911
- }),
29912
- options.signal
29913
- );
29948
+ if (options.customInference) {
29949
+ rawContent = await runWithCustomInference(systemPrompt, userPrompt) || "";
29950
+ } else {
29951
+ await streamLLMWithFallback(
29952
+ systemPrompt,
29953
+ userPrompt,
29954
+ apiKeys,
29955
+ (chunk, type) => {
29956
+ if (type === "content") rawContent += chunk;
29957
+ onChunk?.(chunk, type);
29958
+ },
29959
+ options.model,
29960
+ options.provider,
29961
+ // RC-W1: layer-aware budget — Layer 3 synthesizes full context and free-tier
29962
+ // models (especially NVIDIA NIM) may think/stream for minutes. Idle window still catches hangs.
29963
+ resolveStreamBudget(targetLayer, {
29964
+ idleMs: options.idleTimeoutMs,
29965
+ totalMs: options.timeoutMs,
29966
+ model: options.model,
29967
+ provider: options.provider
29968
+ }),
29969
+ options.signal,
29970
+ options.onProviderEvent
29971
+ );
29972
+ }
29914
29973
  const parsed = safeParseJson(rawContent);
29915
29974
  return {
29916
29975
  success: !!parsed,