@prestyj/ai 4.2.15 → 4.2.44

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.js CHANGED
@@ -16,9 +16,6 @@ var ProviderError = class extends GGAIError {
16
16
  }
17
17
  };
18
18
 
19
- // src/providers/anthropic.ts
20
- import Anthropic from "@anthropic-ai/sdk";
21
-
22
19
  // src/utils/event-stream.ts
23
20
  var EventStream = class {
24
21
  queue = [];
@@ -26,8 +23,8 @@ var EventStream = class {
26
23
  done = false;
27
24
  error = null;
28
25
  push(event) {
29
- if (this.queue.length > 5e4) {
30
- this.queue.splice(0, this.queue.length - 25e3);
26
+ if (this.queue.length > 1e4) {
27
+ this.queue.splice(0, this.queue.length - 5e3);
31
28
  }
32
29
  this.queue.push(event);
33
30
  this.resolve?.();
@@ -101,6 +98,9 @@ var StreamResult = class {
101
98
  }
102
99
  };
103
100
 
101
+ // src/providers/anthropic.ts
102
+ import Anthropic from "@anthropic-ai/sdk";
103
+
104
104
  // src/utils/zod-to-json-schema.ts
105
105
  import { z } from "zod";
106
106
  function zodToJsonSchema(schema) {
@@ -280,15 +280,24 @@ function remapToolCallId(id, idMap) {
280
280
  idMap.set(id, mapped);
281
281
  return mapped;
282
282
  }
283
- function toOpenAIMessages(messages) {
283
+ function toOpenAIMessages(messages, options) {
284
284
  const out = [];
285
285
  const idMap = /* @__PURE__ */ new Map();
286
+ const mergeToolResultText = options?.provider === "glm";
286
287
  for (const msg of messages) {
287
288
  if (msg.role === "system") {
288
289
  out.push({ role: "system", content: msg.content });
289
290
  continue;
290
291
  }
291
292
  if (msg.role === "user") {
293
+ if (mergeToolResultText && out.length > 0 && out[out.length - 1].role === "tool") {
294
+ const userText = typeof msg.content === "string" ? msg.content : msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
295
+ if (userText) {
296
+ const lastTool = out[out.length - 1];
297
+ lastTool.content = (lastTool.content ?? "") + "\n\n" + userText;
298
+ continue;
299
+ }
300
+ }
292
301
  if (typeof msg.content === "string") {
293
302
  out.push({ role: "user", content: msg.content });
294
303
  } else {
@@ -403,10 +412,24 @@ async function runStream(options, result) {
403
412
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
404
413
  const client = new Anthropic({
405
414
  ...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
406
- ...options.baseUrl ? { baseURL: options.baseUrl } : {}
415
+ ...options.baseUrl ? { baseURL: options.baseUrl } : {},
416
+ ...options.fetch ? { fetch: options.fetch } : {},
417
+ ...isOAuth ? {
418
+ defaultHeaders: {
419
+ "user-agent": "claude-cli/2.1.75",
420
+ "x-app": "cli"
421
+ }
422
+ } : {}
407
423
  });
408
424
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
409
- const { system, messages } = toAnthropicMessages(options.messages, cacheControl);
425
+ const { system: rawSystem, messages } = toAnthropicMessages(options.messages, cacheControl);
426
+ const system = isOAuth ? [
427
+ {
428
+ type: "text",
429
+ text: "You are Claude Code, Anthropic's official CLI for Claude."
430
+ },
431
+ ...rawSystem ?? []
432
+ ] : rawSystem;
410
433
  let maxTokens = options.maxTokens ?? 4096;
411
434
  let thinking;
412
435
  let outputConfig;
@@ -440,7 +463,7 @@ async function runStream(options, result) {
440
463
  stream: true
441
464
  };
442
465
  const betaHeaders = [
443
- ...isOAuth ? ["oauth-2025-04-20"] : [],
466
+ ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
444
467
  ...options.compaction ? ["compact-2026-01-12"] : []
445
468
  ];
446
469
  const stream2 = client.messages.stream(params, {
@@ -583,16 +606,19 @@ function streamOpenAI(options) {
583
606
  async function runStream2(options, result) {
584
607
  const client = new OpenAI({
585
608
  apiKey: options.apiKey,
586
- ...options.baseUrl ? { baseURL: options.baseUrl } : {}
609
+ ...options.baseUrl ? { baseURL: options.baseUrl } : {},
610
+ ...options.fetch ? { fetch: options.fetch } : {}
587
611
  });
588
612
  const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot";
589
- const messages = toOpenAIMessages(options.messages);
613
+ const messages = toOpenAIMessages(options.messages, { provider: options.provider });
614
+ const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
615
+ const effectiveTemp = options.temperature ?? defaultTemp;
590
616
  const params = {
591
617
  model: options.model,
592
618
  messages,
593
619
  stream: true,
594
620
  ...options.maxTokens ? { max_tokens: options.maxTokens } : {},
595
- ...options.temperature != null && !options.thinking ? { temperature: options.temperature } : {},
621
+ ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
596
622
  ...options.topP != null ? { top_p: options.topP } : {},
597
623
  ...options.stop ? { stop: options.stop } : {},
598
624
  ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking) } : {},
@@ -709,7 +735,7 @@ async function runStream2(options, result) {
709
735
  result.push({ type: "done", stopReason });
710
736
  result.complete(response);
711
737
  }
712
- function toError2(err, provider) {
738
+ function toError2(err, provider = "openai") {
713
739
  if (err instanceof OpenAI.APIError) {
714
740
  let msg = err.message;
715
741
  const body = err.error;
@@ -1035,37 +1061,129 @@ function toError3(err) {
1035
1061
  return new ProviderError("openai", String(err));
1036
1062
  }
1037
1063
 
1064
+ // src/provider-registry.ts
1065
+ var ProviderRegistryImpl = class {
1066
+ providers = /* @__PURE__ */ new Map();
1067
+ /**
1068
+ * Register a provider. Overwrites any existing provider with the same name.
1069
+ *
1070
+ * ```ts
1071
+ * import { providerRegistry } from "@prestyj/ai";
1072
+ *
1073
+ * providerRegistry.register("deepseek", {
1074
+ * stream: (options) => streamOpenAI({ ...options, baseUrl: "https://api.deepseek.com/v1" }),
1075
+ * });
1076
+ * ```
1077
+ */
1078
+ register(name, entry) {
1079
+ this.providers.set(name, entry);
1080
+ }
1081
+ /** Remove a registered provider. */
1082
+ unregister(name) {
1083
+ return this.providers.delete(name);
1084
+ }
1085
+ /** Get a provider entry by name. */
1086
+ get(name) {
1087
+ return this.providers.get(name);
1088
+ }
1089
+ /** Check if a provider is registered. */
1090
+ has(name) {
1091
+ return this.providers.has(name);
1092
+ }
1093
+ /** List all registered provider names. */
1094
+ list() {
1095
+ return [...this.providers.keys()];
1096
+ }
1097
+ };
1098
+ var providerRegistry = new ProviderRegistryImpl();
1099
+
1038
1100
  // src/stream.ts
1101
+ var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
1102
+ var GLM_REGULAR_BASE_URL = "https://api.z.ai/api/paas/v4";
1103
+ providerRegistry.register("anthropic", {
1104
+ stream: (options) => streamAnthropic(options)
1105
+ });
1106
+ providerRegistry.register("openai", {
1107
+ stream: (options) => {
1108
+ if (options.accountId) {
1109
+ return streamOpenAICodex(options);
1110
+ }
1111
+ return streamOpenAI(options);
1112
+ }
1113
+ });
1114
+ providerRegistry.register("glm", {
1115
+ stream: (options) => {
1116
+ if (options.baseUrl) {
1117
+ return streamOpenAI(options);
1118
+ }
1119
+ return streamGLMWithFallback(options);
1120
+ }
1121
+ });
1122
+ providerRegistry.register("moonshot", {
1123
+ stream: (options) => streamOpenAI({
1124
+ ...options,
1125
+ baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
1126
+ })
1127
+ });
1039
1128
  function stream(options) {
1040
- switch (options.provider) {
1041
- case "anthropic":
1042
- return streamAnthropic(options);
1043
- case "openai":
1044
- if (options.accountId) {
1045
- return streamOpenAICodex(options);
1129
+ const entry = providerRegistry.get(options.provider);
1130
+ if (!entry) {
1131
+ throw new GGAIError(
1132
+ `Unknown provider: "${options.provider}". Registered: ${providerRegistry.list().join(", ")}`
1133
+ );
1134
+ }
1135
+ return entry.stream(options);
1136
+ }
1137
+ function streamGLMWithFallback(options) {
1138
+ const result = new StreamResult();
1139
+ runGLMWithFallback(options, result).catch((err) => {
1140
+ result.abort(err instanceof Error ? err : new Error(String(err)));
1141
+ });
1142
+ return result;
1143
+ }
1144
+ async function runGLMWithFallback(options, result) {
1145
+ const codingResult = streamOpenAI({ ...options, baseUrl: GLM_CODING_BASE_URL });
1146
+ try {
1147
+ for await (const event of codingResult) {
1148
+ result.push(event);
1149
+ }
1150
+ const response = await codingResult.response;
1151
+ result.complete(response);
1152
+ } catch (err) {
1153
+ if (isEndpointMismatchError(err)) {
1154
+ const regularResult = streamOpenAI({ ...options, baseUrl: GLM_REGULAR_BASE_URL });
1155
+ try {
1156
+ for await (const event of regularResult) {
1157
+ result.push(event);
1158
+ }
1159
+ const response = await regularResult.response;
1160
+ result.complete(response);
1161
+ } catch (fallbackErr) {
1162
+ result.abort(fallbackErr instanceof Error ? fallbackErr : new Error(String(fallbackErr)));
1046
1163
  }
1047
- return streamOpenAI(options);
1048
- case "glm":
1049
- return streamOpenAI({
1050
- ...options,
1051
- baseUrl: options.baseUrl ?? "https://api.z.ai/api/coding/paas/v4"
1052
- });
1053
- case "moonshot":
1054
- return streamOpenAI({
1055
- ...options,
1056
- baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1"
1057
- });
1058
- default:
1059
- throw new GGAIError(
1060
- `Unknown provider: ${options.provider}. Supported: "anthropic", "openai", "glm", "moonshot"`
1061
- );
1164
+ } else {
1165
+ result.abort(err instanceof Error ? err : new Error(String(err)));
1166
+ }
1167
+ }
1168
+ }
1169
+ function isEndpointMismatchError(err) {
1170
+ if (err instanceof ProviderError) {
1171
+ if (err.statusCode === 404 || err.statusCode === 403) return true;
1172
+ }
1173
+ if (err instanceof Error) {
1174
+ const msg = err.message.toLowerCase();
1175
+ if (msg.includes("model_not_found") || msg.includes("does not exist") || /model.*not.*exist/.test(msg) || /not have access/.test(msg) || msg.includes("resource not found")) {
1176
+ return true;
1177
+ }
1062
1178
  }
1179
+ return false;
1063
1180
  }
1064
1181
  export {
1065
1182
  EventStream,
1066
1183
  GGAIError,
1067
1184
  ProviderError,
1068
1185
  StreamResult,
1186
+ providerRegistry,
1069
1187
  stream
1070
1188
  };
1071
1189
  //# sourceMappingURL=index.js.map