@prestyj/ai 4.3.15 → 4.3.34

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.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
- type Provider = "anthropic" | "xiaomi" | "openai" | "glm" | "moonshot" | "minimax" | "palsu";
3
+ type Provider = "anthropic" | "xiaomi" | "openai" | "glm" | "moonshot" | "minimax" | "openrouter" | "palsu";
4
4
  type ThinkingLevel = "low" | "medium" | "high" | "max";
5
5
  type CacheRetention = "none" | "short" | "long";
6
6
  interface TextContent {
@@ -174,6 +174,12 @@ interface StreamOptions {
174
174
  * where the default `globalThis.fetch` doesn't support streaming properly.
175
175
  * Passed directly to the underlying provider SDK. */
176
176
  fetch?: typeof globalThis.fetch;
177
+ /** Use streaming transport (default: true). When false, providers issue a
178
+ * single non-streaming request and synthesize events from the full response.
179
+ * The agent loop flips this to `false` as a fallback after repeated stream
180
+ * stalls — broken SSE connections (transient CDN / proxy issues) often
181
+ * recover when the same request is issued over a plain HTTP request/response. */
182
+ streaming?: boolean;
177
183
  }
178
184
 
179
185
  /**
package/dist/index.js CHANGED
@@ -266,7 +266,7 @@ function toAnthropicToolChoice(choice) {
266
266
  return { type: "tool", name: choice.name };
267
267
  }
268
268
  function supportsAdaptiveThinking(model) {
269
- return /opus-4-6|sonnet-4-6/.test(model);
269
+ return /opus-4-7|opus-4-6|sonnet-4-6/.test(model);
270
270
  }
271
271
  function toAnthropicThinking(level, maxTokens, model) {
272
272
  if (supportsAdaptiveThinking(model)) {
@@ -293,10 +293,10 @@ function toAnthropicThinking(level, maxTokens, model) {
293
293
  };
294
294
  }
295
295
  function remapToolCallId(id, idMap) {
296
- if (id.startsWith("call_")) return id;
296
+ if (!id.startsWith("toolu_")) return id;
297
297
  const existing = idMap.get(id);
298
298
  if (existing) return existing;
299
- const mapped = `call_${id.replace(/^toolu_/, "")}`;
299
+ const mapped = `call_${id.slice(5)}`;
300
300
  idMap.set(id, mapped);
301
301
  return mapped;
302
302
  }
@@ -361,6 +361,8 @@ function toOpenAIMessages(messages, options) {
361
361
  };
362
362
  if (thinkingParts) {
363
363
  assistantMsg.reasoning_content = thinkingParts;
364
+ } else if (options?.thinking && hasToolCalls && options.provider !== "glm") {
365
+ assistantMsg.reasoning_content = " ";
364
366
  }
365
367
  out.push(assistantMsg);
366
368
  continue;
@@ -450,6 +452,7 @@ function streamAnthropic(options) {
450
452
  async function* runStream(options) {
451
453
  const client = createClient(options);
452
454
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
455
+ const useStreaming = options.streaming !== false;
453
456
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
454
457
  const { system: rawSystem, messages } = toAnthropicMessages(options.messages, cacheControl);
455
458
  const system = isOAuth ? [
@@ -495,9 +498,9 @@ async function* runStream(options) {
495
498
  ];
496
499
  return contextEdits.length ? { context_management: { edits: contextEdits } } : {};
497
500
  })(),
498
- stream: true
501
+ stream: useStreaming
499
502
  };
500
- const hasAdaptiveThinking = options.model.includes("opus-4-6") || options.model.includes("opus-4.6") || options.model.includes("sonnet-4-6") || options.model.includes("sonnet-4.6");
503
+ const hasAdaptiveThinking = options.model.includes("opus-4-7") || options.model.includes("opus-4.7") || options.model.includes("opus-4-6") || options.model.includes("opus-4.6") || options.model.includes("sonnet-4-6") || options.model.includes("sonnet-4.6");
501
504
  const betaHeaders = [
502
505
  ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
503
506
  ...options.compaction ? ["compact-2026-01-12"] : [],
@@ -505,10 +508,23 @@ async function* runStream(options) {
505
508
  "fine-grained-tool-streaming-2025-05-14",
506
509
  ...!hasAdaptiveThinking ? ["interleaved-thinking-2025-05-14"] : []
507
510
  ];
508
- const stream2 = client.messages.stream(params, {
511
+ const requestOptions = {
509
512
  signal: options.signal ?? void 0,
510
513
  ...betaHeaders.length ? { headers: { "anthropic-beta": betaHeaders.join(",") } } : {}
511
- });
514
+ };
515
+ if (!useStreaming) {
516
+ try {
517
+ const message = await client.messages.create(
518
+ { ...params, stream: false },
519
+ requestOptions
520
+ );
521
+ yield* synthesizeEventsFromMessage(message);
522
+ return messageToResponse(message);
523
+ } catch (err) {
524
+ throw toError(err);
525
+ }
526
+ }
527
+ const stream2 = client.messages.stream(params, requestOptions);
512
528
  const contentParts = [];
513
529
  const blocks = /* @__PURE__ */ new Map();
514
530
  let inputTokens = 0;
@@ -701,6 +717,105 @@ async function* runStream(options) {
701
717
  yield { type: "done", stopReason: normalizedStop };
702
718
  return response;
703
719
  }
720
+ function* synthesizeEventsFromMessage(message) {
721
+ for (const block of message.content) {
722
+ const blk = block;
723
+ const type = blk.type;
724
+ if (type === "text") {
725
+ const text = blk.text;
726
+ if (text) yield { type: "text_delta", text };
727
+ } else if (type === "thinking") {
728
+ const text = blk.thinking;
729
+ if (text) yield { type: "thinking_delta", text };
730
+ } else if (type === "tool_use") {
731
+ const argsJson = JSON.stringify(blk.input ?? {});
732
+ yield {
733
+ type: "toolcall_delta",
734
+ id: blk.id,
735
+ name: blk.name,
736
+ argsJson
737
+ };
738
+ yield {
739
+ type: "toolcall_done",
740
+ id: blk.id,
741
+ name: blk.name,
742
+ args: blk.input ?? {}
743
+ };
744
+ } else if (type === "server_tool_use") {
745
+ yield {
746
+ type: "server_toolcall",
747
+ id: blk.id,
748
+ name: blk.name,
749
+ input: blk.input
750
+ };
751
+ } else if (type === "web_search_tool_result") {
752
+ yield {
753
+ type: "server_toolresult",
754
+ toolUseId: blk.tool_use_id,
755
+ resultType: type,
756
+ data: blk
757
+ };
758
+ }
759
+ }
760
+ yield { type: "done", stopReason: normalizeAnthropicStopReason(message.stop_reason) };
761
+ }
762
+ function messageToResponse(message) {
763
+ const contentParts = [];
764
+ for (const block of message.content) {
765
+ const blk = block;
766
+ const type = blk.type;
767
+ if (type === "text") {
768
+ contentParts.push({ type: "text", text: blk.text });
769
+ } else if (type === "thinking") {
770
+ contentParts.push({
771
+ type: "thinking",
772
+ text: blk.thinking,
773
+ signature: blk.signature ?? ""
774
+ });
775
+ } else if (type === "tool_use") {
776
+ contentParts.push({
777
+ type: "tool_call",
778
+ id: blk.id,
779
+ name: blk.name,
780
+ args: blk.input ?? {}
781
+ });
782
+ } else if (type === "server_tool_use") {
783
+ contentParts.push({
784
+ type: "server_tool_call",
785
+ id: blk.id,
786
+ name: blk.name,
787
+ input: blk.input
788
+ });
789
+ } else if (type === "web_search_tool_result") {
790
+ contentParts.push({
791
+ type: "server_tool_result",
792
+ toolUseId: blk.tool_use_id,
793
+ resultType: type,
794
+ data: blk
795
+ });
796
+ } else {
797
+ contentParts.push({ type: "raw", data: blk });
798
+ }
799
+ }
800
+ const usage = message.usage;
801
+ const inputTokens = usage.input_tokens ?? 0;
802
+ const outputTokens = usage.output_tokens ?? 0;
803
+ const cacheRead = usage.cache_read_input_tokens;
804
+ const cacheWrite = usage.cache_creation_input_tokens;
805
+ return {
806
+ message: {
807
+ role: "assistant",
808
+ content: contentParts.length > 0 ? contentParts : ""
809
+ },
810
+ stopReason: normalizeAnthropicStopReason(message.stop_reason),
811
+ usage: {
812
+ inputTokens,
813
+ outputTokens,
814
+ ...cacheRead != null && { cacheRead },
815
+ ...cacheWrite != null && { cacheWrite }
816
+ }
817
+ };
818
+ }
704
819
  function toError(err) {
705
820
  if (err instanceof Anthropic.APIError) {
706
821
  return new ProviderError("anthropic", err.message, {
@@ -728,15 +843,19 @@ function streamOpenAI(options) {
728
843
  }
729
844
  async function* runStream2(options) {
730
845
  const providerName = options.provider ?? "openai";
846
+ const useStreaming = options.streaming !== false;
731
847
  const client = createClient2(options);
732
848
  const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
733
- const messages = toOpenAIMessages(options.messages, { provider: options.provider });
849
+ const messages = toOpenAIMessages(options.messages, {
850
+ provider: options.provider,
851
+ thinking: !!options.thinking
852
+ });
734
853
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
735
854
  const effectiveTemp = options.temperature ?? defaultTemp;
736
855
  const params = {
737
856
  model: options.model,
738
857
  messages,
739
- stream: true,
858
+ stream: useStreaming,
740
859
  ...options.maxTokens ? { max_completion_tokens: options.maxTokens } : {},
741
860
  ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
742
861
  ...options.topP != null ? { top_p: options.topP } : {},
@@ -744,14 +863,14 @@ async function* runStream2(options) {
744
863
  ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking) } : {},
745
864
  ...options.tools?.length ? { tools: toOpenAITools(options.tools) } : {},
746
865
  ...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
747
- stream_options: { include_usage: true }
866
+ ...useStreaming ? { stream_options: { include_usage: true } } : {}
748
867
  };
749
- if (options.webSearch) {
750
- if (options.provider === "moonshot") {
751
- const raw = params;
752
- const tools = (raw.tools ?? []).slice();
753
- tools.push({ type: "builtin_function", function: { name: "$web_search" } });
754
- raw.tools = tools;
868
+ if (options.provider === "openai" || options.provider === "moonshot") {
869
+ const paramsAny = params;
870
+ paramsAny.prompt_cache_key = "ezcoder";
871
+ const retention = options.cacheRetention ?? "short";
872
+ if (retention === "long") {
873
+ paramsAny.prompt_cache_retention = "24h";
755
874
  }
756
875
  }
757
876
  if (usesThinkingParam) {
@@ -772,6 +891,17 @@ async function* runStream2(options) {
772
891
  `
773
892
  );
774
893
  }
894
+ if (!useStreaming) {
895
+ try {
896
+ const completion = await client.chat.completions.create(params, {
897
+ signal: options.signal ?? void 0
898
+ });
899
+ yield* synthesizeEventsFromCompletion(completion, !!options.thinking);
900
+ return completionToResponse(completion);
901
+ } catch (err) {
902
+ throw toError2(err, providerName);
903
+ }
904
+ }
775
905
  let stream2;
776
906
  try {
777
907
  stream2 = await client.chat.completions.create(params, {
@@ -796,6 +926,10 @@ async function* runStream2(options) {
796
926
  if (details?.cached_tokens) {
797
927
  cacheRead = details.cached_tokens;
798
928
  }
929
+ const usageAny = chunk.usage;
930
+ if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
931
+ cacheRead = usageAny.cached_tokens;
932
+ }
799
933
  inputTokens = chunk.usage.prompt_tokens - cacheRead;
800
934
  }
801
935
  if (!choice) continue;
@@ -877,6 +1011,102 @@ async function* runStream2(options) {
877
1011
  yield { type: "done", stopReason };
878
1012
  return response;
879
1013
  }
1014
+ function* synthesizeEventsFromCompletion(completion, thinkingEnabled) {
1015
+ const choice = completion.choices?.[0];
1016
+ if (!choice) {
1017
+ yield { type: "done", stopReason: normalizeOpenAIStopReason(null) };
1018
+ return;
1019
+ }
1020
+ const msg = choice.message;
1021
+ const reasoning = msg.reasoning_content;
1022
+ if (typeof reasoning === "string" && reasoning && thinkingEnabled) {
1023
+ yield { type: "thinking_delta", text: reasoning };
1024
+ }
1025
+ if (typeof msg.content === "string" && msg.content) {
1026
+ yield { type: "text_delta", text: msg.content };
1027
+ }
1028
+ const toolCalls = msg.tool_calls;
1029
+ if (toolCalls) {
1030
+ for (const tc of toolCalls) {
1031
+ const argsJson = tc.function?.arguments ?? "";
1032
+ if (argsJson) {
1033
+ yield {
1034
+ type: "toolcall_delta",
1035
+ id: tc.id,
1036
+ name: tc.function?.name ?? "",
1037
+ argsJson
1038
+ };
1039
+ }
1040
+ let args = {};
1041
+ try {
1042
+ args = JSON.parse(argsJson);
1043
+ } catch {
1044
+ }
1045
+ yield {
1046
+ type: "toolcall_done",
1047
+ id: tc.id,
1048
+ name: tc.function?.name ?? "",
1049
+ args
1050
+ };
1051
+ }
1052
+ }
1053
+ yield { type: "done", stopReason: normalizeOpenAIStopReason(choice.finish_reason ?? null) };
1054
+ }
1055
+ function completionToResponse(completion) {
1056
+ const choice = completion.choices?.[0];
1057
+ const contentParts = [];
1058
+ let textAccum = "";
1059
+ if (choice) {
1060
+ const msg = choice.message;
1061
+ const reasoning = msg.reasoning_content;
1062
+ if (typeof reasoning === "string" && reasoning) {
1063
+ contentParts.push({ type: "thinking", text: reasoning });
1064
+ }
1065
+ if (typeof msg.content === "string" && msg.content) {
1066
+ textAccum = msg.content;
1067
+ contentParts.push({ type: "text", text: msg.content });
1068
+ }
1069
+ const toolCalls = msg.tool_calls;
1070
+ if (toolCalls) {
1071
+ for (const tc of toolCalls) {
1072
+ let args = {};
1073
+ try {
1074
+ args = JSON.parse(tc.function?.arguments ?? "{}");
1075
+ } catch {
1076
+ }
1077
+ const toolCall = {
1078
+ type: "tool_call",
1079
+ id: tc.id,
1080
+ name: tc.function?.name ?? "",
1081
+ args
1082
+ };
1083
+ contentParts.push(toolCall);
1084
+ }
1085
+ }
1086
+ }
1087
+ let inputTokens = 0;
1088
+ let outputTokens = 0;
1089
+ let cacheRead = 0;
1090
+ if (completion.usage) {
1091
+ outputTokens = completion.usage.completion_tokens;
1092
+ const details = completion.usage.prompt_tokens_details;
1093
+ if (details?.cached_tokens) cacheRead = details.cached_tokens;
1094
+ const usageAny = completion.usage;
1095
+ if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1096
+ cacheRead = usageAny.cached_tokens;
1097
+ }
1098
+ inputTokens = completion.usage.prompt_tokens - cacheRead;
1099
+ }
1100
+ const stopReason = normalizeOpenAIStopReason(choice?.finish_reason ?? null);
1101
+ return {
1102
+ message: {
1103
+ role: "assistant",
1104
+ content: contentParts.length > 0 ? contentParts : textAccum
1105
+ },
1106
+ stopReason,
1107
+ usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
1108
+ };
1109
+ }
880
1110
  function toError2(err, provider = "openai") {
881
1111
  if (err instanceof OpenAI.APIError) {
882
1112
  let msg = err.message;
@@ -1273,6 +1503,12 @@ providerRegistry.register("moonshot", {
1273
1503
  baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
1274
1504
  })
1275
1505
  });
1506
+ providerRegistry.register("openrouter", {
1507
+ stream: (options) => streamOpenAI({
1508
+ ...options,
1509
+ baseUrl: options.baseUrl ?? "https://openrouter.ai/api/v1"
1510
+ })
1511
+ });
1276
1512
  providerRegistry.register("minimax", {
1277
1513
  stream: (options) => streamAnthropic({
1278
1514
  ...options,