@thanh01.pmt/curriculum-kit 1.4.32 → 1.4.33

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.
@@ -693,6 +693,52 @@ function getDesignatedFallbackConfig() {
693
693
  }
694
694
 
695
695
  // src/ai/provider-factory.ts
696
+ function cleanNonStreamingFetch() {
697
+ return async (input, init) => {
698
+ const res = await fetch(input, init);
699
+ let wantsStream = false;
700
+ try {
701
+ const body = init?.body;
702
+ if (typeof body === "string") wantsStream = /"stream"\s*:\s*true/.test(body);
703
+ } catch {
704
+ }
705
+ if (wantsStream) return res;
706
+ const contentType = res.headers.get("content-type") || "";
707
+ if (!contentType.includes("text/event-stream") && !contentType.includes("application/json")) return res;
708
+ const text = await res.text();
709
+ const trimmed = text.trim();
710
+ if (trimmed.startsWith("data:")) {
711
+ let content = "";
712
+ let lastChunk = null;
713
+ for (const line of trimmed.split("\n")) {
714
+ const l = line.trim();
715
+ if (!l || !l.startsWith("data:")) continue;
716
+ const jsonStr = l.slice(5).trim();
717
+ if (jsonStr === "[DONE]") continue;
718
+ try {
719
+ const chunk = JSON.parse(jsonStr);
720
+ lastChunk = chunk;
721
+ content += chunk.choices?.[0]?.delta?.content || "";
722
+ } catch {
723
+ }
724
+ }
725
+ const assembled = {
726
+ id: lastChunk?.id || "chatcmpl-" + Date.now(),
727
+ object: "chat.completion",
728
+ created: lastChunk?.created || Math.floor(Date.now() / 1e3),
729
+ model: lastChunk?.model || "9router",
730
+ choices: [{ index: 0, message: { role: "assistant", content }, finish_reason: "stop" }],
731
+ usage: lastChunk?.usage || void 0
732
+ };
733
+ const headers = new Headers(res.headers);
734
+ headers.set("content-type", "application/json");
735
+ return new Response(JSON.stringify(assembled), { status: res.status, statusText: res.statusText, headers });
736
+ }
737
+ const doneIndex = text.indexOf("data: [DONE]");
738
+ if (doneIndex === -1) return res;
739
+ return new Response(text.slice(0, doneIndex).trim(), { status: res.status, statusText: res.statusText, headers: res.headers });
740
+ };
741
+ }
696
742
  var DEFAULT_MODELS = {
697
743
  "9router": "fast-reasoning",
698
744
  ninerouter: "fast-reasoning",
@@ -723,7 +769,8 @@ function getAIModel(options = {}) {
723
769
  const rawUrl = options.baseURL || resolveApiKey("NINEROUTER_BASE_URL") || resolveApiKey("NINE_ROUTER_BASE_URL") || "https://ai-router.orchable.app/v1";
724
770
  const ninerouter = createOpenAI({
725
771
  apiKey,
726
- baseURL: rawUrl.replace(/^["']|["']$/g, "").trim()
772
+ baseURL: rawUrl.replace(/^["']|["']$/g, "").trim(),
773
+ fetch: cleanNonStreamingFetch()
727
774
  });
728
775
  return ninerouter.chat(resolvedModelName || "fast-reasoning");
729
776
  }