@absolutejs/ai 0.6.4 → 0.6.6

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.
@@ -1,87 +1,4 @@
1
1
  // @bun
2
- // src/ai/inputCapacity.ts
3
- class AIInputError extends Error {
4
- code;
5
- providerStatus;
6
- requestId;
7
- constructor(code, message, providerStatus, requestId) {
8
- super(message);
9
- this.code = code;
10
- this.providerStatus = providerStatus;
11
- this.requestId = requestId;
12
- this.name = "AIInputError";
13
- }
14
- }
15
- var positiveTokenLimit = (value) => {
16
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0)
17
- throw new AIInputError("capacity_unavailable", "The provider did not supply a valid model limit.");
18
- return value;
19
- };
20
- var inputTokenCount = (value) => {
21
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
22
- throw new AIInputError("capacity_unavailable", "The provider did not supply a valid token count.");
23
- return value;
24
- };
25
- var inspectAIInput = async (provider, params) => {
26
- params.signal?.throwIfAborted();
27
- if (!provider.inputCapacity)
28
- throw new AIInputError("capacity_unavailable", "This provider does not expose model capacity and token counting.");
29
- const [limits, tokens] = await Promise.all([
30
- provider.inputCapacity.getLimits(params),
31
- provider.inputCapacity.countTokens(params)
32
- ]).catch((error) => {
33
- if (error instanceof Error && error.name === "AIInputError" && "code" in error && (error.code === "capacity_unavailable" || error.code === "input_too_large" || error.code === "unsupported_content"))
34
- throw new AIInputError(error.code, error.message, "providerStatus" in error && typeof error.providerStatus === "number" ? error.providerStatus : undefined, "requestId" in error && typeof error.requestId === "string" ? error.requestId : undefined);
35
- throw error;
36
- });
37
- const inputTokens = inputTokenCount(tokens);
38
- const outputTokens = positiveTokenLimit(provider.inputCapacity.outputTokens?.(params) ?? params.maxTokens ?? limits.maxOutputTokens);
39
- const maxInputTokens = positiveTokenLimit(limits.maxInputTokens);
40
- const maxOutputTokens = positiveTokenLimit(limits.maxOutputTokens);
41
- const availableInputTokens = Math.max(0, Math.min(maxInputTokens, limits.contextWindowTokens === undefined ? maxInputTokens : positiveTokenLimit(limits.contextWindowTokens) - outputTokens));
42
- return {
43
- limits,
44
- inputTokens,
45
- inputTokenScope: provider.inputCapacity.countScope?.(params) ?? "request",
46
- outputTokens,
47
- availableInputTokens,
48
- fits: outputTokens <= maxOutputTokens && inputTokens <= availableInputTokens
49
- };
50
- };
51
- var cacheModelLimits = (load, ttlMs = 300000) => {
52
- const cache = new Map;
53
- return async (params) => {
54
- const entry = cache.get(params.model);
55
- if (entry && Date.now() - entry.at < ttlMs)
56
- return entry.limits;
57
- const limits = await load(params);
58
- positiveTokenLimit(limits.maxInputTokens);
59
- positiveTokenLimit(limits.maxOutputTokens);
60
- if (cache.size >= 100)
61
- cache.clear();
62
- cache.set(params.model, { at: Date.now(), limits });
63
- return limits;
64
- };
65
- };
66
- var capacityJson = async (response) => {
67
- if (!response.ok) {
68
- const payload = await response.json().catch(() => {
69
- return;
70
- });
71
- const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : undefined;
72
- const providerError = record?.error;
73
- const nested = providerError && typeof providerError === "object" && !Array.isArray(providerError) ? providerError : undefined;
74
- const message = nested?.message ?? record?.message;
75
- const detail = typeof message === "string" ? message.slice(0, 1000) : undefined;
76
- const id = response.headers.get("request-id") ?? response.headers.get("x-request-id") ?? record?.request_id;
77
- throw new AIInputError("capacity_unavailable", `Model capacity request failed (${response.status}).${detail ? ` ${detail}` : ""}`, response.status, typeof id === "string" ? id.slice(0, 200) : undefined);
78
- }
79
- const value = await response.json();
80
- if (!value || typeof value !== "object" || Array.isArray(value))
81
- throw new AIInputError("capacity_unavailable", "Model capacity response was invalid.");
82
- return value;
83
- };
84
-
85
2
  // src/ai/errors/providerError.ts
86
3
  var PROVIDER_STATUS_PAGES = {
87
4
  anthropic: "https://status.claude.com",
@@ -141,7 +58,7 @@ class ProviderError extends Error {
141
58
  this.provider = init.provider;
142
59
  this.status = init.status ?? null;
143
60
  this.type = init.type ?? (typeof init.metadata?.code === "string" ? init.metadata.code : typeof init.metadata?.type === "string" ? init.metadata.type : null);
144
- this.billingUnavailable = BILLING_ERROR_TYPES.has(this.type ?? "") || init.status === 402;
61
+ this.billingUnavailable = BILLING_ERROR_TYPES.has(this.type ?? "") || init.status === 402 || init.provider === "anthropic" && init.status === 400 && /credit balance is too low/i.test(String(init.metadata?.message ?? init.message));
145
62
  this.notSent = init.notSent === true;
146
63
  this.retryable = !this.billingUnavailable && init.retryable;
147
64
  this.metadata = init.metadata;
@@ -167,6 +84,17 @@ class ProviderError extends Error {
167
84
  static from(err, provider) {
168
85
  if (err instanceof ProviderError)
169
86
  return err;
87
+ if (isProviderError(err))
88
+ return new ProviderError({
89
+ provider: err.provider,
90
+ message: err.message,
91
+ status: err.status,
92
+ type: err.type,
93
+ retryable: err.retryable,
94
+ notSent: err.notSent,
95
+ metadata: err.metadata,
96
+ cause: err
97
+ });
170
98
  if (isAbortError(err))
171
99
  throw err;
172
100
  const rawMessage = err instanceof Error ? err.message : String(err ?? "");
@@ -184,6 +112,92 @@ class ProviderError extends Error {
184
112
  }
185
113
  }
186
114
  var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
115
+ var isProviderError = (error) => error instanceof Error && error.name === "ProviderError" && ("provider" in error) && typeof error.provider === "string" && ("status" in error) && (error.status === null || typeof error.status === "number") && ("retryable" in error) && typeof error.retryable === "boolean" && ("billingUnavailable" in error) && typeof error.billingUnavailable === "boolean" && ("notSent" in error) && typeof error.notSent === "boolean";
116
+
117
+ // src/ai/inputCapacity.ts
118
+ class AIInputError extends Error {
119
+ code;
120
+ providerStatus;
121
+ requestId;
122
+ providerError;
123
+ constructor(code, message, providerStatus, requestId, providerError) {
124
+ super(message);
125
+ this.code = code;
126
+ this.providerStatus = providerStatus;
127
+ this.requestId = requestId;
128
+ this.providerError = providerError;
129
+ this.name = "AIInputError";
130
+ }
131
+ }
132
+ var positiveTokenLimit = (value) => {
133
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0)
134
+ throw new AIInputError("capacity_unavailable", "The provider did not supply a valid model limit.");
135
+ return value;
136
+ };
137
+ var inputTokenCount = (value) => {
138
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
139
+ throw new AIInputError("capacity_unavailable", "The provider did not supply a valid token count.");
140
+ return value;
141
+ };
142
+ var inspectAIInput = async (provider, params) => {
143
+ params.signal?.throwIfAborted();
144
+ if (!provider.inputCapacity)
145
+ throw new AIInputError("capacity_unavailable", "This provider does not expose model capacity and token counting.");
146
+ const [limits, tokens] = await Promise.all([
147
+ provider.inputCapacity.getLimits(params),
148
+ provider.inputCapacity.countTokens(params)
149
+ ]).catch((error) => {
150
+ if (error instanceof Error && error.name === "AIInputError" && "code" in error && (error.code === "capacity_unavailable" || error.code === "input_too_large" || error.code === "unsupported_content"))
151
+ throw new AIInputError(error.code, error.message, "providerStatus" in error && typeof error.providerStatus === "number" ? error.providerStatus : undefined, "requestId" in error && typeof error.requestId === "string" ? error.requestId : undefined, "providerError" in error && isProviderError(error.providerError) ? ProviderError.from(error.providerError, error.providerError.provider) : undefined);
152
+ throw error;
153
+ });
154
+ const inputTokens = inputTokenCount(tokens);
155
+ const outputTokens = positiveTokenLimit(provider.inputCapacity.outputTokens?.(params) ?? params.maxTokens ?? limits.maxOutputTokens);
156
+ const maxInputTokens = positiveTokenLimit(limits.maxInputTokens);
157
+ const maxOutputTokens = positiveTokenLimit(limits.maxOutputTokens);
158
+ const availableInputTokens = Math.max(0, Math.min(maxInputTokens, limits.contextWindowTokens === undefined ? maxInputTokens : positiveTokenLimit(limits.contextWindowTokens) - outputTokens));
159
+ return {
160
+ limits,
161
+ inputTokens,
162
+ inputTokenScope: provider.inputCapacity.countScope?.(params) ?? "request",
163
+ outputTokens,
164
+ availableInputTokens,
165
+ fits: outputTokens <= maxOutputTokens && inputTokens <= availableInputTokens
166
+ };
167
+ };
168
+ var cacheModelLimits = (load, ttlMs = 300000) => {
169
+ const cache = new Map;
170
+ return async (params) => {
171
+ const entry = cache.get(params.model);
172
+ if (entry && Date.now() - entry.at < ttlMs)
173
+ return entry.limits;
174
+ const limits = await load(params);
175
+ positiveTokenLimit(limits.maxInputTokens);
176
+ positiveTokenLimit(limits.maxOutputTokens);
177
+ if (cache.size >= 100)
178
+ cache.clear();
179
+ cache.set(params.model, { at: Date.now(), limits });
180
+ return limits;
181
+ };
182
+ };
183
+ var capacityJson = async (response, provider = "unknown") => {
184
+ if (!response.ok) {
185
+ const payload = await response.json().catch(() => {
186
+ return;
187
+ });
188
+ const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : undefined;
189
+ const providerError = record?.error;
190
+ const nested = providerError && typeof providerError === "object" && !Array.isArray(providerError) ? providerError : undefined;
191
+ const message = nested?.message ?? record?.message;
192
+ const detail = typeof message === "string" ? message.slice(0, 1000) : undefined;
193
+ const id = response.headers.get("request-id") ?? response.headers.get("x-request-id") ?? record?.request_id;
194
+ throw new AIInputError("capacity_unavailable", `Model capacity request failed (${response.status}).${detail ? ` ${detail}` : ""}`, response.status, typeof id === "string" ? id.slice(0, 200) : undefined, ProviderError.fromResponse(provider, response.status, JSON.stringify({ error: nested ?? record ?? {} })));
195
+ }
196
+ const value = await response.json();
197
+ if (!value || typeof value !== "object" || Array.isArray(value))
198
+ throw new AIInputError("capacity_unavailable", "Model capacity response was invalid.");
199
+ return value;
200
+ };
187
201
 
188
202
  // src/ai/resilience.ts
189
203
  var DEFAULT_CONFIG = {
@@ -314,6 +328,29 @@ var withResilience = (provider, providerName = "unknown") => {
314
328
  let billingFailure = null;
315
329
  let billingRetryAt = 0;
316
330
  let billingProbe = false;
331
+ const inspectAvailable = async (inspect) => {
332
+ if (billingFailure && Date.now() < billingRetryAt) {
333
+ throw new ProviderError({
334
+ provider: providerName,
335
+ message: "Provider billing is unavailable",
336
+ type: billingFailure.type,
337
+ status: billingFailure.status,
338
+ metadata: billingFailure.metadata,
339
+ retryable: false,
340
+ notSent: true
341
+ });
342
+ }
343
+ try {
344
+ return await inspect();
345
+ } catch (error) {
346
+ const nested = error instanceof Error && "providerError" in error ? error.providerError : error;
347
+ if (isProviderError(nested) && nested.billingUnavailable) {
348
+ billingFailure = ProviderError.from(nested, providerName);
349
+ billingRetryAt = Date.now() + billingCooldownMs;
350
+ }
351
+ throw error;
352
+ }
353
+ };
317
354
  const billingCooldownMs = 5 * 60000;
318
355
  const attempt = async function* (params, attemptNo) {
319
356
  let yielded = false;
@@ -341,33 +378,39 @@ var withResilience = (provider, providerName = "unknown") => {
341
378
  if (providerError.retryable)
342
379
  noteFailure(providerName, providerError);
343
380
  throw providerError;
381
+ } finally {
382
+ billingProbe = false;
344
383
  }
345
384
  };
346
385
  return {
347
386
  ...provider,
348
- stream: (params) => {
387
+ ...provider.inputCapacity ? {
388
+ inputCapacity: {
389
+ ...provider.inputCapacity,
390
+ getLimits: (params) => inspectAvailable(() => provider.inputCapacity.getLimits(params)),
391
+ countTokens: (params) => inspectAvailable(() => provider.inputCapacity.countTokens(params))
392
+ }
393
+ } : {},
394
+ stream: async function* (params) {
349
395
  if (billingFailure && (Date.now() < billingRetryAt || billingProbe)) {
350
396
  const failure = billingFailure;
351
- return async function* () {
352
- throw new ProviderError({
353
- provider: providerName,
354
- message: "Provider billing is unavailable",
355
- type: failure.type,
356
- status: failure.status,
357
- retryable: false,
358
- notSent: true
359
- });
360
- }();
397
+ throw new ProviderError({
398
+ provider: providerName,
399
+ message: "Provider billing is unavailable",
400
+ type: failure.type,
401
+ status: failure.status,
402
+ metadata: failure.metadata,
403
+ retryable: false,
404
+ notSent: true
405
+ });
361
406
  }
362
- if (billingFailure)
363
- billingProbe = true;
364
407
  const tripped = circuitOpen(providerName);
365
408
  if (tripped) {
366
- return async function* () {
367
- throw tripped;
368
- }();
409
+ throw tripped;
369
410
  }
370
- return attempt(params, 0);
411
+ if (billingFailure)
412
+ billingProbe = true;
413
+ yield* attempt(params, 0);
371
414
  }
372
415
  };
373
416
  };
@@ -376,7 +419,7 @@ var withResilience = (provider, providerName = "unknown") => {
376
419
  var instrumentAIProvider = (provider, providerName) => {
377
420
  const resilient = withResilience(provider, providerName);
378
421
  return {
379
- ...provider,
422
+ ...resilient,
380
423
  stream: (params) => {
381
424
  if (!params.onUsage && !params.onSpan) {
382
425
  return resilient.stream(params);
@@ -1366,7 +1409,7 @@ var anthropic = (config) => {
1366
1409
  headers,
1367
1410
  body: body ? JSON.stringify(body) : undefined,
1368
1411
  signal: params.signal ? AbortSignal.any([params.signal, AbortSignal.timeout(15000)]) : AbortSignal.timeout(15000)
1369
- }));
1412
+ }), "anthropic");
1370
1413
  };
1371
1414
  const countingRequest = (params) => {
1372
1415
  const built = buildRequestBody(params, configuredMax, promptCaching);
@@ -1433,5 +1476,5 @@ export {
1433
1476
  anthropic
1434
1477
  };
1435
1478
 
1436
- //# debugId=0FD58AA008DDDBEF64756E2164756E21
1479
+ //# debugId=E6E9FD6C4A0FEEBF64756E2164756E21
1437
1480
  //# sourceMappingURL=anthropic.js.map