@ax-llm/ax 22.0.6 → 22.0.7

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/index.d.cts CHANGED
@@ -432,6 +432,20 @@ declare class AxBaseAI<TModel, TEmbedModel, TChatRequest, TEmbedRequest, TChatRe
432
432
  chat(req: Readonly<AxChatRequest<TModel | TModelKey>>, options?: Readonly<AxAIServiceOptions>): Promise<AxChatResponse | ReadableStream<AxChatResponse>>;
433
433
  private _chat1;
434
434
  private cleanupFunctionSchema;
435
+ /**
436
+ * Peeks the first raw streaming delta and, if the provider classifies it as a retryable
437
+ * transient error (e.g. an Anthropic `overloaded_error` SSE event), re-issues the request
438
+ * with exponential backoff — the same policy {@link apiCall} applies to an HTTP 529 status,
439
+ * so streaming and non-streaming overloads behave identically. The first delta is classified
440
+ * on the raw chunk (no stateful transform runs), so peeking has no side effects. After the
441
+ * retry budget is exhausted the original error delta is replayed so it surfaces normally
442
+ * (and the balancer can still fail over). A non-error first delta is replayed unchanged.
443
+ *
444
+ * Note: re-issuing cancels the previous stream's reader best-effort; the underlying fetch
445
+ * body of the abandoned overloaded request is released by GC rather than promptly aborted,
446
+ * since apiCall's streams don't propagate cancel. Acceptable for the transient-overload case.
447
+ */
448
+ private retryTransientStreamStart;
435
449
  private _chat2;
436
450
  embed(req: Readonly<AxEmbedRequest<TEmbedModel>>, options?: Readonly<AxAIServiceOptions>): Promise<AxEmbedResponse>;
437
451
  private _embed1;
@@ -1384,6 +1398,14 @@ interface AxAIServiceImpl<TModel, TEmbedModel, TChatRequest, TEmbedRequest, TCha
1384
1398
  createChatReq(req: Readonly<AxInternalChatRequest<TModel>>, config?: Readonly<AxAIServiceOptions>): Promise<[AxAPI, TChatRequest]> | [AxAPI, TChatRequest];
1385
1399
  createChatResp(resp: Readonly<TChatResponse>): AxChatResponse;
1386
1400
  createChatStreamResp?(resp: Readonly<TChatResponseDelta>, state: object): AxChatResponse;
1401
+ /**
1402
+ * Optional: classify a raw streaming delta that carries a transient error into the
1403
+ * HTTP status it corresponds to (e.g. Anthropic's HTTP-200 `overloaded_error` SSE event
1404
+ * → 529). The base layer applies the same retryable-status policy used for real HTTP
1405
+ * status errors, so a streaming overload is retried-with-backoff before any failover —
1406
+ * matching the non-streaming path. Return undefined for normal deltas (the common case).
1407
+ */
1408
+ classifyStreamErrorStatus?(resp: Readonly<TChatResponseDelta>): number | undefined;
1387
1409
  createEmbedReq?(req: Readonly<AxInternalEmbedRequest<TEmbedModel>>, config?: Readonly<AxAIServiceOptions>): Promise<[AxAPI, TEmbedRequest]> | [AxAPI, TEmbedRequest];
1388
1410
  createEmbedResp?(resp: Readonly<TEmbedResponse>): AxEmbedResponse;
1389
1411
  getModelConfig(): AxModelConfig;
@@ -6259,7 +6281,7 @@ type AxAIAnthropicChatResponse = {
6259
6281
  type AxAIAnthropicChatError = {
6260
6282
  type: 'error';
6261
6283
  error: {
6262
- type: 'authentication_error';
6284
+ type: 'overloaded_error' | 'api_error' | 'rate_limit_error' | 'invalid_request_error' | 'authentication_error' | 'permission_error' | 'not_found_error' | 'request_too_large';
6263
6285
  message: string;
6264
6286
  };
6265
6287
  };
@@ -6349,7 +6371,7 @@ interface AxAIAnthropicPingEvent {
6349
6371
  interface AxAIAnthropicErrorEvent {
6350
6372
  type: 'error';
6351
6373
  error: {
6352
- type: 'overloaded_error';
6374
+ type: 'overloaded_error' | 'api_error' | 'rate_limit_error' | 'invalid_request_error' | 'authentication_error' | 'permission_error' | 'not_found_error' | 'request_too_large';
6353
6375
  message: string;
6354
6376
  };
6355
6377
  }
@@ -6858,6 +6880,7 @@ declare class AxBalancer<TServices extends readonly AxAIService<any, any, any>[]
6858
6880
  private maxBackoffMs;
6859
6881
  private maxRetries;
6860
6882
  private serviceFailures;
6883
+ private static readonly RETRYABLE_STATUS_CODES;
6861
6884
  constructor(services: TServices, options?: AxBalancerOptions<TModelKey>);
6862
6885
  /**
6863
6886
  * Static factory method for type-safe balancer creation with automatic model key inference.
@@ -6885,6 +6908,26 @@ declare class AxBalancer<TServices extends readonly AxAIService<any, any, any>[]
6885
6908
  private canRetryService;
6886
6909
  private handleFailure;
6887
6910
  private handleSuccess;
6911
+ /**
6912
+ * Whether an error should route to another service rather than fail the request.
6913
+ * Mirrors the decisions made in chat()'s catch block so the streaming peek path and
6914
+ * the synchronous path agree on what "retryable" means.
6915
+ */
6916
+ private isRetryableServiceError;
6917
+ /**
6918
+ * Wraps a streaming response to make it participate in failover. Two responsibilities:
6919
+ *
6920
+ * 1. Pre-content errors: eagerly reads the first chunk so a provider error thrown while
6921
+ * reading (e.g. Anthropic's HTTP-200 `overloaded_error` SSE) rejects here, where
6922
+ * {@link chat}'s try/catch can route it through the normal failover path. On success it
6923
+ * returns a new stream that re-emits the buffered first chunk then pumps the rest.
6924
+ * 2. Mid-stream errors: a retryable error *after* the first chunk can't fail over
6925
+ * transparently (partial output is already committed), so it is surfaced via
6926
+ * `controller.error`. But we record the failure first (see {@link handleFailure}) —
6927
+ * otherwise the failed service stays out of backoff and an app-level retry via chat()
6928
+ * (which restarts at index 0 and doesn't reset()) would route straight back to it.
6929
+ */
6930
+ private peekStreamForFailover;
6888
6931
  chat(req: Readonly<AxChatRequest<TModelKey>>, options?: Readonly<AxAIServiceOptions>): Promise<AxChatResponse | ReadableStream<AxChatResponse>>;
6889
6932
  embed(req: Readonly<AxEmbedRequest<TModelKey>>, options?: Readonly<AxAIServiceOptions>): Promise<AxEmbedResponse>;
6890
6933
  transcribe(req: Readonly<AxTranscriptionRequest<TModelKey>>, options?: Readonly<AxAIServiceOptions>): Promise<AxTranscriptionResponse>;
package/index.d.ts CHANGED
@@ -432,6 +432,20 @@ declare class AxBaseAI<TModel, TEmbedModel, TChatRequest, TEmbedRequest, TChatRe
432
432
  chat(req: Readonly<AxChatRequest<TModel | TModelKey>>, options?: Readonly<AxAIServiceOptions>): Promise<AxChatResponse | ReadableStream<AxChatResponse>>;
433
433
  private _chat1;
434
434
  private cleanupFunctionSchema;
435
+ /**
436
+ * Peeks the first raw streaming delta and, if the provider classifies it as a retryable
437
+ * transient error (e.g. an Anthropic `overloaded_error` SSE event), re-issues the request
438
+ * with exponential backoff — the same policy {@link apiCall} applies to an HTTP 529 status,
439
+ * so streaming and non-streaming overloads behave identically. The first delta is classified
440
+ * on the raw chunk (no stateful transform runs), so peeking has no side effects. After the
441
+ * retry budget is exhausted the original error delta is replayed so it surfaces normally
442
+ * (and the balancer can still fail over). A non-error first delta is replayed unchanged.
443
+ *
444
+ * Note: re-issuing cancels the previous stream's reader best-effort; the underlying fetch
445
+ * body of the abandoned overloaded request is released by GC rather than promptly aborted,
446
+ * since apiCall's streams don't propagate cancel. Acceptable for the transient-overload case.
447
+ */
448
+ private retryTransientStreamStart;
435
449
  private _chat2;
436
450
  embed(req: Readonly<AxEmbedRequest<TEmbedModel>>, options?: Readonly<AxAIServiceOptions>): Promise<AxEmbedResponse>;
437
451
  private _embed1;
@@ -1384,6 +1398,14 @@ interface AxAIServiceImpl<TModel, TEmbedModel, TChatRequest, TEmbedRequest, TCha
1384
1398
  createChatReq(req: Readonly<AxInternalChatRequest<TModel>>, config?: Readonly<AxAIServiceOptions>): Promise<[AxAPI, TChatRequest]> | [AxAPI, TChatRequest];
1385
1399
  createChatResp(resp: Readonly<TChatResponse>): AxChatResponse;
1386
1400
  createChatStreamResp?(resp: Readonly<TChatResponseDelta>, state: object): AxChatResponse;
1401
+ /**
1402
+ * Optional: classify a raw streaming delta that carries a transient error into the
1403
+ * HTTP status it corresponds to (e.g. Anthropic's HTTP-200 `overloaded_error` SSE event
1404
+ * → 529). The base layer applies the same retryable-status policy used for real HTTP
1405
+ * status errors, so a streaming overload is retried-with-backoff before any failover —
1406
+ * matching the non-streaming path. Return undefined for normal deltas (the common case).
1407
+ */
1408
+ classifyStreamErrorStatus?(resp: Readonly<TChatResponseDelta>): number | undefined;
1387
1409
  createEmbedReq?(req: Readonly<AxInternalEmbedRequest<TEmbedModel>>, config?: Readonly<AxAIServiceOptions>): Promise<[AxAPI, TEmbedRequest]> | [AxAPI, TEmbedRequest];
1388
1410
  createEmbedResp?(resp: Readonly<TEmbedResponse>): AxEmbedResponse;
1389
1411
  getModelConfig(): AxModelConfig;
@@ -6259,7 +6281,7 @@ type AxAIAnthropicChatResponse = {
6259
6281
  type AxAIAnthropicChatError = {
6260
6282
  type: 'error';
6261
6283
  error: {
6262
- type: 'authentication_error';
6284
+ type: 'overloaded_error' | 'api_error' | 'rate_limit_error' | 'invalid_request_error' | 'authentication_error' | 'permission_error' | 'not_found_error' | 'request_too_large';
6263
6285
  message: string;
6264
6286
  };
6265
6287
  };
@@ -6349,7 +6371,7 @@ interface AxAIAnthropicPingEvent {
6349
6371
  interface AxAIAnthropicErrorEvent {
6350
6372
  type: 'error';
6351
6373
  error: {
6352
- type: 'overloaded_error';
6374
+ type: 'overloaded_error' | 'api_error' | 'rate_limit_error' | 'invalid_request_error' | 'authentication_error' | 'permission_error' | 'not_found_error' | 'request_too_large';
6353
6375
  message: string;
6354
6376
  };
6355
6377
  }
@@ -6858,6 +6880,7 @@ declare class AxBalancer<TServices extends readonly AxAIService<any, any, any>[]
6858
6880
  private maxBackoffMs;
6859
6881
  private maxRetries;
6860
6882
  private serviceFailures;
6883
+ private static readonly RETRYABLE_STATUS_CODES;
6861
6884
  constructor(services: TServices, options?: AxBalancerOptions<TModelKey>);
6862
6885
  /**
6863
6886
  * Static factory method for type-safe balancer creation with automatic model key inference.
@@ -6885,6 +6908,26 @@ declare class AxBalancer<TServices extends readonly AxAIService<any, any, any>[]
6885
6908
  private canRetryService;
6886
6909
  private handleFailure;
6887
6910
  private handleSuccess;
6911
+ /**
6912
+ * Whether an error should route to another service rather than fail the request.
6913
+ * Mirrors the decisions made in chat()'s catch block so the streaming peek path and
6914
+ * the synchronous path agree on what "retryable" means.
6915
+ */
6916
+ private isRetryableServiceError;
6917
+ /**
6918
+ * Wraps a streaming response to make it participate in failover. Two responsibilities:
6919
+ *
6920
+ * 1. Pre-content errors: eagerly reads the first chunk so a provider error thrown while
6921
+ * reading (e.g. Anthropic's HTTP-200 `overloaded_error` SSE) rejects here, where
6922
+ * {@link chat}'s try/catch can route it through the normal failover path. On success it
6923
+ * returns a new stream that re-emits the buffered first chunk then pumps the rest.
6924
+ * 2. Mid-stream errors: a retryable error *after* the first chunk can't fail over
6925
+ * transparently (partial output is already committed), so it is surfaced via
6926
+ * `controller.error`. But we record the failure first (see {@link handleFailure}) —
6927
+ * otherwise the failed service stays out of backoff and an app-level retry via chat()
6928
+ * (which restarts at index 0 and doesn't reset()) would route straight back to it.
6929
+ */
6930
+ private peekStreamForFailover;
6888
6931
  chat(req: Readonly<AxChatRequest<TModelKey>>, options?: Readonly<AxAIServiceOptions>): Promise<AxChatResponse | ReadableStream<AxChatResponse>>;
6889
6932
  embed(req: Readonly<AxEmbedRequest<TModelKey>>, options?: Readonly<AxAIServiceOptions>): Promise<AxEmbedResponse>;
6890
6933
  transcribe(req: Readonly<AxTranscriptionRequest<TModelKey>>, options?: Readonly<AxAIServiceOptions>): Promise<AxTranscriptionResponse>;