@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.
@@ -223,89 +223,6 @@ var geminiThinkingConfig = (model, reasoning) => {
223
223
  return { thinkingBudget: reasoning.budgetTokens ?? budgets[effort] };
224
224
  };
225
225
 
226
- // src/ai/inputCapacity.ts
227
- class AIInputError extends Error {
228
- code;
229
- providerStatus;
230
- requestId;
231
- constructor(code, message, providerStatus, requestId) {
232
- super(message);
233
- this.code = code;
234
- this.providerStatus = providerStatus;
235
- this.requestId = requestId;
236
- this.name = "AIInputError";
237
- }
238
- }
239
- var positiveTokenLimit = (value) => {
240
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0)
241
- throw new AIInputError("capacity_unavailable", "The provider did not supply a valid model limit.");
242
- return value;
243
- };
244
- var inputTokenCount = (value) => {
245
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
246
- throw new AIInputError("capacity_unavailable", "The provider did not supply a valid token count.");
247
- return value;
248
- };
249
- var inspectAIInput = async (provider, params) => {
250
- params.signal?.throwIfAborted();
251
- if (!provider.inputCapacity)
252
- throw new AIInputError("capacity_unavailable", "This provider does not expose model capacity and token counting.");
253
- const [limits, tokens] = await Promise.all([
254
- provider.inputCapacity.getLimits(params),
255
- provider.inputCapacity.countTokens(params)
256
- ]).catch((error) => {
257
- if (error instanceof Error && error.name === "AIInputError" && "code" in error && (error.code === "capacity_unavailable" || error.code === "input_too_large" || error.code === "unsupported_content"))
258
- 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);
259
- throw error;
260
- });
261
- const inputTokens = inputTokenCount(tokens);
262
- const outputTokens = positiveTokenLimit(provider.inputCapacity.outputTokens?.(params) ?? params.maxTokens ?? limits.maxOutputTokens);
263
- const maxInputTokens = positiveTokenLimit(limits.maxInputTokens);
264
- const maxOutputTokens = positiveTokenLimit(limits.maxOutputTokens);
265
- const availableInputTokens = Math.max(0, Math.min(maxInputTokens, limits.contextWindowTokens === undefined ? maxInputTokens : positiveTokenLimit(limits.contextWindowTokens) - outputTokens));
266
- return {
267
- limits,
268
- inputTokens,
269
- inputTokenScope: provider.inputCapacity.countScope?.(params) ?? "request",
270
- outputTokens,
271
- availableInputTokens,
272
- fits: outputTokens <= maxOutputTokens && inputTokens <= availableInputTokens
273
- };
274
- };
275
- var cacheModelLimits = (load, ttlMs = 300000) => {
276
- const cache = new Map;
277
- return async (params) => {
278
- const entry = cache.get(params.model);
279
- if (entry && Date.now() - entry.at < ttlMs)
280
- return entry.limits;
281
- const limits = await load(params);
282
- positiveTokenLimit(limits.maxInputTokens);
283
- positiveTokenLimit(limits.maxOutputTokens);
284
- if (cache.size >= 100)
285
- cache.clear();
286
- cache.set(params.model, { at: Date.now(), limits });
287
- return limits;
288
- };
289
- };
290
- var capacityJson = async (response) => {
291
- if (!response.ok) {
292
- const payload = await response.json().catch(() => {
293
- return;
294
- });
295
- const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : undefined;
296
- const providerError = record?.error;
297
- const nested = providerError && typeof providerError === "object" && !Array.isArray(providerError) ? providerError : undefined;
298
- const message = nested?.message ?? record?.message;
299
- const detail = typeof message === "string" ? message.slice(0, 1000) : undefined;
300
- const id = response.headers.get("request-id") ?? response.headers.get("x-request-id") ?? record?.request_id;
301
- throw new AIInputError("capacity_unavailable", `Model capacity request failed (${response.status}).${detail ? ` ${detail}` : ""}`, response.status, typeof id === "string" ? id.slice(0, 200) : undefined);
302
- }
303
- const value = await response.json();
304
- if (!value || typeof value !== "object" || Array.isArray(value))
305
- throw new AIInputError("capacity_unavailable", "Model capacity response was invalid.");
306
- return value;
307
- };
308
-
309
226
  // src/ai/errors/providerError.ts
310
227
  var PROVIDER_STATUS_PAGES = {
311
228
  anthropic: "https://status.claude.com",
@@ -365,7 +282,7 @@ class ProviderError extends Error {
365
282
  this.provider = init.provider;
366
283
  this.status = init.status ?? null;
367
284
  this.type = init.type ?? (typeof init.metadata?.code === "string" ? init.metadata.code : typeof init.metadata?.type === "string" ? init.metadata.type : null);
368
- this.billingUnavailable = BILLING_ERROR_TYPES.has(this.type ?? "") || init.status === 402;
285
+ 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));
369
286
  this.notSent = init.notSent === true;
370
287
  this.retryable = !this.billingUnavailable && init.retryable;
371
288
  this.metadata = init.metadata;
@@ -391,6 +308,17 @@ class ProviderError extends Error {
391
308
  static from(err, provider) {
392
309
  if (err instanceof ProviderError)
393
310
  return err;
311
+ if (isProviderError(err))
312
+ return new ProviderError({
313
+ provider: err.provider,
314
+ message: err.message,
315
+ status: err.status,
316
+ type: err.type,
317
+ retryable: err.retryable,
318
+ notSent: err.notSent,
319
+ metadata: err.metadata,
320
+ cause: err
321
+ });
394
322
  if (isAbortError(err))
395
323
  throw err;
396
324
  const rawMessage = err instanceof Error ? err.message : String(err ?? "");
@@ -408,6 +336,92 @@ class ProviderError extends Error {
408
336
  }
409
337
  }
410
338
  var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
339
+ 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";
340
+
341
+ // src/ai/inputCapacity.ts
342
+ class AIInputError extends Error {
343
+ code;
344
+ providerStatus;
345
+ requestId;
346
+ providerError;
347
+ constructor(code, message, providerStatus, requestId, providerError) {
348
+ super(message);
349
+ this.code = code;
350
+ this.providerStatus = providerStatus;
351
+ this.requestId = requestId;
352
+ this.providerError = providerError;
353
+ this.name = "AIInputError";
354
+ }
355
+ }
356
+ var positiveTokenLimit = (value) => {
357
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0)
358
+ throw new AIInputError("capacity_unavailable", "The provider did not supply a valid model limit.");
359
+ return value;
360
+ };
361
+ var inputTokenCount = (value) => {
362
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
363
+ throw new AIInputError("capacity_unavailable", "The provider did not supply a valid token count.");
364
+ return value;
365
+ };
366
+ var inspectAIInput = async (provider, params) => {
367
+ params.signal?.throwIfAborted();
368
+ if (!provider.inputCapacity)
369
+ throw new AIInputError("capacity_unavailable", "This provider does not expose model capacity and token counting.");
370
+ const [limits, tokens] = await Promise.all([
371
+ provider.inputCapacity.getLimits(params),
372
+ provider.inputCapacity.countTokens(params)
373
+ ]).catch((error) => {
374
+ if (error instanceof Error && error.name === "AIInputError" && "code" in error && (error.code === "capacity_unavailable" || error.code === "input_too_large" || error.code === "unsupported_content"))
375
+ 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);
376
+ throw error;
377
+ });
378
+ const inputTokens = inputTokenCount(tokens);
379
+ const outputTokens = positiveTokenLimit(provider.inputCapacity.outputTokens?.(params) ?? params.maxTokens ?? limits.maxOutputTokens);
380
+ const maxInputTokens = positiveTokenLimit(limits.maxInputTokens);
381
+ const maxOutputTokens = positiveTokenLimit(limits.maxOutputTokens);
382
+ const availableInputTokens = Math.max(0, Math.min(maxInputTokens, limits.contextWindowTokens === undefined ? maxInputTokens : positiveTokenLimit(limits.contextWindowTokens) - outputTokens));
383
+ return {
384
+ limits,
385
+ inputTokens,
386
+ inputTokenScope: provider.inputCapacity.countScope?.(params) ?? "request",
387
+ outputTokens,
388
+ availableInputTokens,
389
+ fits: outputTokens <= maxOutputTokens && inputTokens <= availableInputTokens
390
+ };
391
+ };
392
+ var cacheModelLimits = (load, ttlMs = 300000) => {
393
+ const cache = new Map;
394
+ return async (params) => {
395
+ const entry = cache.get(params.model);
396
+ if (entry && Date.now() - entry.at < ttlMs)
397
+ return entry.limits;
398
+ const limits = await load(params);
399
+ positiveTokenLimit(limits.maxInputTokens);
400
+ positiveTokenLimit(limits.maxOutputTokens);
401
+ if (cache.size >= 100)
402
+ cache.clear();
403
+ cache.set(params.model, { at: Date.now(), limits });
404
+ return limits;
405
+ };
406
+ };
407
+ var capacityJson = async (response, provider = "unknown") => {
408
+ if (!response.ok) {
409
+ const payload = await response.json().catch(() => {
410
+ return;
411
+ });
412
+ const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : undefined;
413
+ const providerError = record?.error;
414
+ const nested = providerError && typeof providerError === "object" && !Array.isArray(providerError) ? providerError : undefined;
415
+ const message = nested?.message ?? record?.message;
416
+ const detail = typeof message === "string" ? message.slice(0, 1000) : undefined;
417
+ const id = response.headers.get("request-id") ?? response.headers.get("x-request-id") ?? record?.request_id;
418
+ 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 ?? {} })));
419
+ }
420
+ const value = await response.json();
421
+ if (!value || typeof value !== "object" || Array.isArray(value))
422
+ throw new AIInputError("capacity_unavailable", "Model capacity response was invalid.");
423
+ return value;
424
+ };
411
425
 
412
426
  // src/ai/resilience.ts
413
427
  var DEFAULT_CONFIG = {
@@ -538,6 +552,29 @@ var withResilience = (provider, providerName = "unknown") => {
538
552
  let billingFailure = null;
539
553
  let billingRetryAt = 0;
540
554
  let billingProbe = false;
555
+ const inspectAvailable = async (inspect) => {
556
+ if (billingFailure && Date.now() < billingRetryAt) {
557
+ throw new ProviderError({
558
+ provider: providerName,
559
+ message: "Provider billing is unavailable",
560
+ type: billingFailure.type,
561
+ status: billingFailure.status,
562
+ metadata: billingFailure.metadata,
563
+ retryable: false,
564
+ notSent: true
565
+ });
566
+ }
567
+ try {
568
+ return await inspect();
569
+ } catch (error) {
570
+ const nested = error instanceof Error && "providerError" in error ? error.providerError : error;
571
+ if (isProviderError(nested) && nested.billingUnavailable) {
572
+ billingFailure = ProviderError.from(nested, providerName);
573
+ billingRetryAt = Date.now() + billingCooldownMs;
574
+ }
575
+ throw error;
576
+ }
577
+ };
541
578
  const billingCooldownMs = 5 * 60000;
542
579
  const attempt = async function* (params, attemptNo) {
543
580
  let yielded = false;
@@ -565,33 +602,39 @@ var withResilience = (provider, providerName = "unknown") => {
565
602
  if (providerError.retryable)
566
603
  noteFailure(providerName, providerError);
567
604
  throw providerError;
605
+ } finally {
606
+ billingProbe = false;
568
607
  }
569
608
  };
570
609
  return {
571
610
  ...provider,
572
- stream: (params) => {
611
+ ...provider.inputCapacity ? {
612
+ inputCapacity: {
613
+ ...provider.inputCapacity,
614
+ getLimits: (params) => inspectAvailable(() => provider.inputCapacity.getLimits(params)),
615
+ countTokens: (params) => inspectAvailable(() => provider.inputCapacity.countTokens(params))
616
+ }
617
+ } : {},
618
+ stream: async function* (params) {
573
619
  if (billingFailure && (Date.now() < billingRetryAt || billingProbe)) {
574
620
  const failure = billingFailure;
575
- return async function* () {
576
- throw new ProviderError({
577
- provider: providerName,
578
- message: "Provider billing is unavailable",
579
- type: failure.type,
580
- status: failure.status,
581
- retryable: false,
582
- notSent: true
583
- });
584
- }();
621
+ throw new ProviderError({
622
+ provider: providerName,
623
+ message: "Provider billing is unavailable",
624
+ type: failure.type,
625
+ status: failure.status,
626
+ metadata: failure.metadata,
627
+ retryable: false,
628
+ notSent: true
629
+ });
585
630
  }
586
- if (billingFailure)
587
- billingProbe = true;
588
631
  const tripped = circuitOpen(providerName);
589
632
  if (tripped) {
590
- return async function* () {
591
- throw tripped;
592
- }();
633
+ throw tripped;
593
634
  }
594
- return attempt(params, 0);
635
+ if (billingFailure)
636
+ billingProbe = true;
637
+ yield* attempt(params, 0);
595
638
  }
596
639
  };
597
640
  };
@@ -600,7 +643,7 @@ var withResilience = (provider, providerName = "unknown") => {
600
643
  var instrumentAIProvider = (provider, providerName) => {
601
644
  const resilient = withResilience(provider, providerName);
602
645
  return {
603
- ...provider,
646
+ ...resilient,
604
647
  stream: (params) => {
605
648
  if (!params.onUsage && !params.onSpan) {
606
649
  return resilient.stream(params);
@@ -999,5 +1042,5 @@ export {
999
1042
  gemini2 as gemini
1000
1043
  };
1001
1044
 
1002
- //# debugId=5DE0258869713E2764756E2164756E21
1045
+ //# debugId=E6B87AD1F8EC179264756E2164756E21
1003
1046
  //# sourceMappingURL=gemini.js.map