@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/README.md CHANGED
@@ -40,7 +40,7 @@ Tool parameters are Zod schemas. Converted to JSON Schema at the provider bounda
40
40
 
41
41
  | Provider | Models | Notes |
42
42
  |---|---|---|
43
- | `anthropic` | Claude Opus 4.6, Sonnet 4.6, Haiku 4.5 | Extended thinking, prompt caching, server-side compaction |
43
+ | `anthropic` | Claude Opus 4.7, Sonnet 4.6, Haiku 4.5 | Extended thinking, prompt caching, server-side compaction |
44
44
  | `openai` | GPT-4.1, o3, o4-mini | Supports OAuth (codex endpoint) and API keys |
45
45
  | `glm` | GLM-5.1, GLM-4.7 | Z.AI platform, OpenAI-compatible |
46
46
  | `moonshot` | Kimi K2.5 | Moonshot platform, OpenAI-compatible |
package/dist/index.cjs CHANGED
@@ -312,7 +312,7 @@ function toAnthropicToolChoice(choice) {
312
312
  return { type: "tool", name: choice.name };
313
313
  }
314
314
  function supportsAdaptiveThinking(model) {
315
- return /opus-4-6|sonnet-4-6/.test(model);
315
+ return /opus-4-7|opus-4-6|sonnet-4-6/.test(model);
316
316
  }
317
317
  function toAnthropicThinking(level, maxTokens, model) {
318
318
  if (supportsAdaptiveThinking(model)) {
@@ -339,10 +339,10 @@ function toAnthropicThinking(level, maxTokens, model) {
339
339
  };
340
340
  }
341
341
  function remapToolCallId(id, idMap) {
342
- if (id.startsWith("call_")) return id;
342
+ if (!id.startsWith("toolu_")) return id;
343
343
  const existing = idMap.get(id);
344
344
  if (existing) return existing;
345
- const mapped = `call_${id.replace(/^toolu_/, "")}`;
345
+ const mapped = `call_${id.slice(5)}`;
346
346
  idMap.set(id, mapped);
347
347
  return mapped;
348
348
  }
@@ -407,6 +407,8 @@ function toOpenAIMessages(messages, options) {
407
407
  };
408
408
  if (thinkingParts) {
409
409
  assistantMsg.reasoning_content = thinkingParts;
410
+ } else if (options?.thinking && hasToolCalls && options.provider !== "glm") {
411
+ assistantMsg.reasoning_content = " ";
410
412
  }
411
413
  out.push(assistantMsg);
412
414
  continue;
@@ -496,6 +498,7 @@ function streamAnthropic(options) {
496
498
  async function* runStream(options) {
497
499
  const client = createClient(options);
498
500
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
501
+ const useStreaming = options.streaming !== false;
499
502
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
500
503
  const { system: rawSystem, messages } = toAnthropicMessages(options.messages, cacheControl);
501
504
  const system = isOAuth ? [
@@ -541,9 +544,9 @@ async function* runStream(options) {
541
544
  ];
542
545
  return contextEdits.length ? { context_management: { edits: contextEdits } } : {};
543
546
  })(),
544
- stream: true
547
+ stream: useStreaming
545
548
  };
546
- 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");
549
+ 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");
547
550
  const betaHeaders = [
548
551
  ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
549
552
  ...options.compaction ? ["compact-2026-01-12"] : [],
@@ -551,10 +554,23 @@ async function* runStream(options) {
551
554
  "fine-grained-tool-streaming-2025-05-14",
552
555
  ...!hasAdaptiveThinking ? ["interleaved-thinking-2025-05-14"] : []
553
556
  ];
554
- const stream2 = client.messages.stream(params, {
557
+ const requestOptions = {
555
558
  signal: options.signal ?? void 0,
556
559
  ...betaHeaders.length ? { headers: { "anthropic-beta": betaHeaders.join(",") } } : {}
557
- });
560
+ };
561
+ if (!useStreaming) {
562
+ try {
563
+ const message = await client.messages.create(
564
+ { ...params, stream: false },
565
+ requestOptions
566
+ );
567
+ yield* synthesizeEventsFromMessage(message);
568
+ return messageToResponse(message);
569
+ } catch (err) {
570
+ throw toError(err);
571
+ }
572
+ }
573
+ const stream2 = client.messages.stream(params, requestOptions);
558
574
  const contentParts = [];
559
575
  const blocks = /* @__PURE__ */ new Map();
560
576
  let inputTokens = 0;
@@ -747,6 +763,105 @@ async function* runStream(options) {
747
763
  yield { type: "done", stopReason: normalizedStop };
748
764
  return response;
749
765
  }
766
+ function* synthesizeEventsFromMessage(message) {
767
+ for (const block of message.content) {
768
+ const blk = block;
769
+ const type = blk.type;
770
+ if (type === "text") {
771
+ const text = blk.text;
772
+ if (text) yield { type: "text_delta", text };
773
+ } else if (type === "thinking") {
774
+ const text = blk.thinking;
775
+ if (text) yield { type: "thinking_delta", text };
776
+ } else if (type === "tool_use") {
777
+ const argsJson = JSON.stringify(blk.input ?? {});
778
+ yield {
779
+ type: "toolcall_delta",
780
+ id: blk.id,
781
+ name: blk.name,
782
+ argsJson
783
+ };
784
+ yield {
785
+ type: "toolcall_done",
786
+ id: blk.id,
787
+ name: blk.name,
788
+ args: blk.input ?? {}
789
+ };
790
+ } else if (type === "server_tool_use") {
791
+ yield {
792
+ type: "server_toolcall",
793
+ id: blk.id,
794
+ name: blk.name,
795
+ input: blk.input
796
+ };
797
+ } else if (type === "web_search_tool_result") {
798
+ yield {
799
+ type: "server_toolresult",
800
+ toolUseId: blk.tool_use_id,
801
+ resultType: type,
802
+ data: blk
803
+ };
804
+ }
805
+ }
806
+ yield { type: "done", stopReason: normalizeAnthropicStopReason(message.stop_reason) };
807
+ }
808
+ function messageToResponse(message) {
809
+ const contentParts = [];
810
+ for (const block of message.content) {
811
+ const blk = block;
812
+ const type = blk.type;
813
+ if (type === "text") {
814
+ contentParts.push({ type: "text", text: blk.text });
815
+ } else if (type === "thinking") {
816
+ contentParts.push({
817
+ type: "thinking",
818
+ text: blk.thinking,
819
+ signature: blk.signature ?? ""
820
+ });
821
+ } else if (type === "tool_use") {
822
+ contentParts.push({
823
+ type: "tool_call",
824
+ id: blk.id,
825
+ name: blk.name,
826
+ args: blk.input ?? {}
827
+ });
828
+ } else if (type === "server_tool_use") {
829
+ contentParts.push({
830
+ type: "server_tool_call",
831
+ id: blk.id,
832
+ name: blk.name,
833
+ input: blk.input
834
+ });
835
+ } else if (type === "web_search_tool_result") {
836
+ contentParts.push({
837
+ type: "server_tool_result",
838
+ toolUseId: blk.tool_use_id,
839
+ resultType: type,
840
+ data: blk
841
+ });
842
+ } else {
843
+ contentParts.push({ type: "raw", data: blk });
844
+ }
845
+ }
846
+ const usage = message.usage;
847
+ const inputTokens = usage.input_tokens ?? 0;
848
+ const outputTokens = usage.output_tokens ?? 0;
849
+ const cacheRead = usage.cache_read_input_tokens;
850
+ const cacheWrite = usage.cache_creation_input_tokens;
851
+ return {
852
+ message: {
853
+ role: "assistant",
854
+ content: contentParts.length > 0 ? contentParts : ""
855
+ },
856
+ stopReason: normalizeAnthropicStopReason(message.stop_reason),
857
+ usage: {
858
+ inputTokens,
859
+ outputTokens,
860
+ ...cacheRead != null && { cacheRead },
861
+ ...cacheWrite != null && { cacheWrite }
862
+ }
863
+ };
864
+ }
750
865
  function toError(err) {
751
866
  if (err instanceof import_sdk.default.APIError) {
752
867
  return new ProviderError("anthropic", err.message, {
@@ -774,15 +889,19 @@ function streamOpenAI(options) {
774
889
  }
775
890
  async function* runStream2(options) {
776
891
  const providerName = options.provider ?? "openai";
892
+ const useStreaming = options.streaming !== false;
777
893
  const client = createClient2(options);
778
894
  const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
779
- const messages = toOpenAIMessages(options.messages, { provider: options.provider });
895
+ const messages = toOpenAIMessages(options.messages, {
896
+ provider: options.provider,
897
+ thinking: !!options.thinking
898
+ });
780
899
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
781
900
  const effectiveTemp = options.temperature ?? defaultTemp;
782
901
  const params = {
783
902
  model: options.model,
784
903
  messages,
785
- stream: true,
904
+ stream: useStreaming,
786
905
  ...options.maxTokens ? { max_completion_tokens: options.maxTokens } : {},
787
906
  ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
788
907
  ...options.topP != null ? { top_p: options.topP } : {},
@@ -790,14 +909,14 @@ async function* runStream2(options) {
790
909
  ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking) } : {},
791
910
  ...options.tools?.length ? { tools: toOpenAITools(options.tools) } : {},
792
911
  ...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
793
- stream_options: { include_usage: true }
912
+ ...useStreaming ? { stream_options: { include_usage: true } } : {}
794
913
  };
795
- if (options.webSearch) {
796
- if (options.provider === "moonshot") {
797
- const raw = params;
798
- const tools = (raw.tools ?? []).slice();
799
- tools.push({ type: "builtin_function", function: { name: "$web_search" } });
800
- raw.tools = tools;
914
+ if (options.provider === "openai" || options.provider === "moonshot") {
915
+ const paramsAny = params;
916
+ paramsAny.prompt_cache_key = "ezcoder";
917
+ const retention = options.cacheRetention ?? "short";
918
+ if (retention === "long") {
919
+ paramsAny.prompt_cache_retention = "24h";
801
920
  }
802
921
  }
803
922
  if (usesThinkingParam) {
@@ -818,6 +937,17 @@ async function* runStream2(options) {
818
937
  `
819
938
  );
820
939
  }
940
+ if (!useStreaming) {
941
+ try {
942
+ const completion = await client.chat.completions.create(params, {
943
+ signal: options.signal ?? void 0
944
+ });
945
+ yield* synthesizeEventsFromCompletion(completion, !!options.thinking);
946
+ return completionToResponse(completion);
947
+ } catch (err) {
948
+ throw toError2(err, providerName);
949
+ }
950
+ }
821
951
  let stream2;
822
952
  try {
823
953
  stream2 = await client.chat.completions.create(params, {
@@ -842,6 +972,10 @@ async function* runStream2(options) {
842
972
  if (details?.cached_tokens) {
843
973
  cacheRead = details.cached_tokens;
844
974
  }
975
+ const usageAny = chunk.usage;
976
+ if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
977
+ cacheRead = usageAny.cached_tokens;
978
+ }
845
979
  inputTokens = chunk.usage.prompt_tokens - cacheRead;
846
980
  }
847
981
  if (!choice) continue;
@@ -923,6 +1057,102 @@ async function* runStream2(options) {
923
1057
  yield { type: "done", stopReason };
924
1058
  return response;
925
1059
  }
1060
+ function* synthesizeEventsFromCompletion(completion, thinkingEnabled) {
1061
+ const choice = completion.choices?.[0];
1062
+ if (!choice) {
1063
+ yield { type: "done", stopReason: normalizeOpenAIStopReason(null) };
1064
+ return;
1065
+ }
1066
+ const msg = choice.message;
1067
+ const reasoning = msg.reasoning_content;
1068
+ if (typeof reasoning === "string" && reasoning && thinkingEnabled) {
1069
+ yield { type: "thinking_delta", text: reasoning };
1070
+ }
1071
+ if (typeof msg.content === "string" && msg.content) {
1072
+ yield { type: "text_delta", text: msg.content };
1073
+ }
1074
+ const toolCalls = msg.tool_calls;
1075
+ if (toolCalls) {
1076
+ for (const tc of toolCalls) {
1077
+ const argsJson = tc.function?.arguments ?? "";
1078
+ if (argsJson) {
1079
+ yield {
1080
+ type: "toolcall_delta",
1081
+ id: tc.id,
1082
+ name: tc.function?.name ?? "",
1083
+ argsJson
1084
+ };
1085
+ }
1086
+ let args = {};
1087
+ try {
1088
+ args = JSON.parse(argsJson);
1089
+ } catch {
1090
+ }
1091
+ yield {
1092
+ type: "toolcall_done",
1093
+ id: tc.id,
1094
+ name: tc.function?.name ?? "",
1095
+ args
1096
+ };
1097
+ }
1098
+ }
1099
+ yield { type: "done", stopReason: normalizeOpenAIStopReason(choice.finish_reason ?? null) };
1100
+ }
1101
+ function completionToResponse(completion) {
1102
+ const choice = completion.choices?.[0];
1103
+ const contentParts = [];
1104
+ let textAccum = "";
1105
+ if (choice) {
1106
+ const msg = choice.message;
1107
+ const reasoning = msg.reasoning_content;
1108
+ if (typeof reasoning === "string" && reasoning) {
1109
+ contentParts.push({ type: "thinking", text: reasoning });
1110
+ }
1111
+ if (typeof msg.content === "string" && msg.content) {
1112
+ textAccum = msg.content;
1113
+ contentParts.push({ type: "text", text: msg.content });
1114
+ }
1115
+ const toolCalls = msg.tool_calls;
1116
+ if (toolCalls) {
1117
+ for (const tc of toolCalls) {
1118
+ let args = {};
1119
+ try {
1120
+ args = JSON.parse(tc.function?.arguments ?? "{}");
1121
+ } catch {
1122
+ }
1123
+ const toolCall = {
1124
+ type: "tool_call",
1125
+ id: tc.id,
1126
+ name: tc.function?.name ?? "",
1127
+ args
1128
+ };
1129
+ contentParts.push(toolCall);
1130
+ }
1131
+ }
1132
+ }
1133
+ let inputTokens = 0;
1134
+ let outputTokens = 0;
1135
+ let cacheRead = 0;
1136
+ if (completion.usage) {
1137
+ outputTokens = completion.usage.completion_tokens;
1138
+ const details = completion.usage.prompt_tokens_details;
1139
+ if (details?.cached_tokens) cacheRead = details.cached_tokens;
1140
+ const usageAny = completion.usage;
1141
+ if (!cacheRead && typeof usageAny.cached_tokens === "number" && usageAny.cached_tokens > 0) {
1142
+ cacheRead = usageAny.cached_tokens;
1143
+ }
1144
+ inputTokens = completion.usage.prompt_tokens - cacheRead;
1145
+ }
1146
+ const stopReason = normalizeOpenAIStopReason(choice?.finish_reason ?? null);
1147
+ return {
1148
+ message: {
1149
+ role: "assistant",
1150
+ content: contentParts.length > 0 ? contentParts : textAccum
1151
+ },
1152
+ stopReason,
1153
+ usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
1154
+ };
1155
+ }
926
1156
  function toError2(err, provider = "openai") {
927
1157
  if (err instanceof import_openai.default.APIError) {
928
1158
  let msg = err.message;
@@ -1319,6 +1549,12 @@ providerRegistry.register("moonshot", {
1319
1549
  baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
1320
1550
  })
1321
1551
  });
1552
+ providerRegistry.register("openrouter", {
1553
+ stream: (options) => streamOpenAI({
1554
+ ...options,
1555
+ baseUrl: options.baseUrl ?? "https://openrouter.ai/api/v1"
1556
+ })
1557
+ });
1322
1558
  providerRegistry.register("minimax", {
1323
1559
  stream: (options) => streamAnthropic({
1324
1560
  ...options,