ai-gateway-proxy 0.3.0 → 0.5.0

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
@@ -191,7 +191,7 @@ A result is rejected when it is not an object or when a field is missing or has
191
191
 
192
192
  ### `onError`
193
193
 
194
- Receives `{ request, error, status }`, where `error` is the gateway's JSON error body. When the body is not JSON of the form `{ error: { message } }`, such as an HTML page from a load balancer, `error` is `{ error: { message: "AI Gateway returned <status>: <excerpt>", type: "gateway_error" } }`, with the first 200 characters of the body as the excerpt. Return a replacement body to send it as JSON with the upstream status, or return a `Response` to send that as is. Returning `undefined` or `null`, as a hook that only logs does, forwards the error body unchanged, and so does a result that is not of the form `{ error: { message, type?, code? }, generationId? }` with a string `message`, a string or absent `type`, a string, number or absent `code` and a string or absent `generationId`, which an AI SDK client could not parse; the problem is logged. A hook that throws is logged and the error body is forwarded too, so the client keeps the upstream status and `retry-after` rather than a 500 it would retry. A `generationId` you return reaches the client, which appends it to the error message as `[<id>]`. Without `onError`, the error body is sent with the upstream status.
194
+ Receives `{ request, error, status }`, where `error` is the gateway's JSON error body. When the body is not JSON of the form `{ error: { message } }`, such as an HTML page from a load balancer, `error` is `{ error: { message: "AI Gateway returned <status>: <excerpt>", type: "gateway_error" } }`, with the first 200 characters of the body as the excerpt. Return a replacement body to send it as JSON with the upstream status, or return a `Response` to send that as is. Returning `undefined` or `null`, as a hook that only logs does, forwards the error body unchanged, and so does a result that is not of the form `{ error: { message, type?, code? }, generationId? }` with a string `message`, a string or absent `type`, a string, number or absent `code` and a string or absent `generationId`, which an AI SDK client could not parse; the problem is logged. A hook that throws is logged and the error body is forwarded too, so the client keeps the upstream status and `retry-after` rather than a 500 it would retry. A `generationId` you return reaches the client, which appends it to the error message as `[<id>]`. Without `onError`, the error body is sent with the upstream status. An error typed `authentication_error` is about the proxy's own credential, since the client's never reaches the gateway, so the proxy retypes it as `proxy_error` and prefixes the message with `AI Gateway rejected the proxy's credential: ` before `onError` runs; `@ai-sdk/gateway` would otherwise replace the message with advice about the client's own key. `status` stays the gateway's, such as 401.
195
195
 
196
196
  ### Example: cap, meter and mask
197
197
 
@@ -230,7 +230,7 @@ createGatewayProxy({
230
230
  Both receive `kind` (`"embedding"`, `"image"`, `"video"`, `"speech"`, `"transcription"`, `"reranking"`, `"decision"` or `"batch"`), `path` (such as `video-model/start`), `modelId` (the client's `ai-model-id` header, which is the only place an embedding or image call names its model) and `request`, the parsed JSON body.
231
231
 
232
232
  - **`beforeRawRequest`:** a returned value replaces the body sent upstream; `undefined` or `null` sends it as is.
233
- - **`afterRawResponse`:** also receives `response`, the `Response` the proxy would send: the upstream status, `content-type` and the allowlisted headers below, with the upstream body on success (a video call's event stream included) and the `GatewayError` body described under `onError` on a failure. Return a `Response` to send that instead, or nothing to send `response`. The body can be read once, so read `response.clone()` when returning nothing; a hook that reads `response` itself and returns nothing, or returns anything but a `Response`, is a 500. When the hook sends another `Response`, throws or returns an invalid result, the proxy cancels `response`'s body unless the hook read it, piped it or built the returned `Response` on it, so the upstream request ends. It does not run on a redirect or when the upstream request fails.
233
+ - **`afterRawResponse`:** also receives `response`, the `Response` the proxy would send: the upstream status, `content-type` and the allowlisted headers below, with the upstream body on success (a video call's event stream included) and the `GatewayError` body described under `onError` on a failure. Return a `Response` to send that instead, or nothing to send `response`. The body can be read once, so read `response.clone()` when returning nothing; a hook that reads `response` itself and returns nothing, or returns anything but a `Response`, is a failure like a throw. A failure on a successful `response` is a 424, since the gateway has already answered, and billed, the call and an AI SDK client retries a 5xx but not a 424; on an error `response` it is a 500. When the hook sends another `Response`, throws or returns an invalid result, the proxy cancels `response`'s body unless the hook read it, piped it or built the returned `Response` on it, so the upstream request ends. It does not run on a redirect or when the upstream request fails.
234
234
 
235
235
  ```typescript
236
236
  createGatewayProxy({
@@ -250,13 +250,14 @@ Apart from forwarding the gateway's own responses, the handler can answer with a
250
250
  | Status | Type | Message | When |
251
251
  | --- | --- | --- | --- |
252
252
  | 500 | `proxy_error` | `AI Gateway authentication not configured: set AI_GATEWAY_API_KEY or deploy on Vercel with OIDC enabled` | `getGatewayAuthToken()` returned `null`. The type is not `authentication_error`, which `@ai-sdk/gateway` replaces with advice about the client's own key. |
253
+ | upstream, such as 401 | `proxy_error` | `AI Gateway rejected the proxy's credential: <message>` | The gateway answered a request with a body with an error typed `authentication_error`; see `onError` above. The other fields of the error are kept. |
253
254
  | 400 | `invalid_request_error` | `Invalid path` | The path is not a safe relative path under `baseUrl`. |
254
255
  | 400 | `invalid_request_error` | `Invalid JSON body` | A request other than `GET` or `HEAD` has a non-empty body that is not JSON. |
255
256
  | 400 | `invalid_request_error` | `Invalid request body: a language model call must be a JSON object` | The body is JSON but not an object, such as an array or `null`, on a path that runs `beforeRequest`, `afterResponse` or `onError`, and one of them is set. No hook runs and the gateway is not called. |
256
257
  | 499 | none | none | The client aborted the request (`request.signal`) before the handler returned. The signal is passed to the upstream `fetch`, so the abort also cancels the upstream request. |
257
- | 500 | `proxy_error` | `Error proxying request to AI Gateway` | Anything else that throws: `extractPath`, `beforeRequest`, `beforeRawRequest`, the upstream `fetch` or `afterRawResponse`, or an `afterRawResponse` that read the body and returned no `Response`. |
258
+ | 500 | `proxy_error` | `Error proxying request to AI Gateway` | Anything else that throws: `extractPath`, `beforeRequest`, `beforeRawRequest`, the upstream `fetch`, or an `afterRawResponse` failure on an error response. |
258
259
  | 500 | `proxy_error` | `beforeRequest hook returned an invalid request: the result must be an object` | `beforeRequest` returned a value other than an object, `undefined` or `null`. The gateway is not called. |
259
- | 424 | `proxy_error` | `Error processing the AI Gateway response` | After the gateway answered a non-streamed call with `afterResponse` set: `afterResponse` threw, or the success body was not a JSON object. The status is one AI SDK clients do not retry, since the gateway has billed the call. |
260
+ | 424 | `proxy_error` | `Error processing the AI Gateway response` | After the gateway answered a non-streamed call with `afterResponse` set: `afterResponse` threw, or the success body was not a JSON object. After a successful call with `afterRawResponse` set: the hook threw, returned anything but a `Response` or nothing, or read the body and returned no `Response`. The status is one AI SDK clients do not retry, since the gateway has billed the call. |
260
261
  | 424 | `proxy_error` | `afterResponse hook returned an invalid response: <problem>` | `afterResponse` on a non-streamed request returned a result that fails the result validation above. |
261
262
  | 500 | `proxy_error` | `No response body` | A successful streamed response arrived without a body. |
262
263
  | 502 | `proxy_error` | `Upstream redirected, the proxy does not follow redirects` | The gateway answered with a 3xx status. The proxy sets `redirect: "manual"`, runs no `onError` and forwards no `Location`. |
package/dist/index.cjs CHANGED
@@ -685,6 +685,23 @@ function isGatewayError(value) {
685
685
  }
686
686
  };
687
687
  }
688
+ /**
689
+ * Retypes a gateway `authentication_error` as a `proxy_error` naming the proxy's credential, since
690
+ * the client's own credential never reaches the gateway and `@ai-sdk/gateway` would replace the
691
+ * message with advice about it.
692
+ */ function retypeProxyCredentialError(upstreamError) {
693
+ if (upstreamError.error.type !== "authentication_error") {
694
+ return upstreamError;
695
+ }
696
+ return {
697
+ ...upstreamError,
698
+ error: {
699
+ ...upstreamError.error,
700
+ message: `AI Gateway rejected the proxy's credential: ${upstreamError.error.message}`,
701
+ type: "proxy_error"
702
+ }
703
+ };
704
+ }
688
705
  /**
689
706
  * Builds response headers with the given content type, if any, plus the allowlisted upstream headers.
690
707
  */ function responseHeaders(res, contentType) {
@@ -840,7 +857,7 @@ async function runAfterResponse(res, requestBody, afterResponse) {
840
857
  * Builds the client response for a non-redirect upstream response, running the language model hooks given.
841
858
  */ async function forwardUpstreamResponse(res, requestBody, isStreaming, { afterResponse, onError }, signal) {
842
859
  if (!res.ok) {
843
- const errorBody = await parseErrorBody(res);
860
+ const errorBody = retypeProxyCredentialError(await parseErrorBody(res));
844
861
  return await createErrorResponse(res, errorBody, requestBody, onError);
845
862
  }
846
863
  // A 204 or 205 has no body to parse or hand to afterResponse, and a Response with it cannot carry one.
@@ -869,7 +886,20 @@ async function runAfterResponse(res, requestBody, afterResponse) {
869
886
  }
870
887
  /**
871
888
  * Runs afterRawResponse and returns the response to send.
872
- */ async function applyAfterRawResponse(afterRawResponse, context) {
889
+ * The gateway has answered, and billed, a successful call, so a failure there gets a status clients do not retry.
890
+ */ async function applyAfterRawResponse(afterRawResponse, context, signal) {
891
+ try {
892
+ return await runAfterRawResponse(afterRawResponse, context);
893
+ } catch (error) {
894
+ // The handler answers a client abort with 499, and a failed call costs nothing to retry.
895
+ if (signal.aborted || !context.response.ok) {
896
+ throw error;
897
+ }
898
+ console.error("AI Gateway proxy failed after the gateway answered:", error);
899
+ return proxyErrorResponse(ANSWERED_CALL_FAILURE_STATUS, "proxy_error", "Error processing the AI Gateway response");
900
+ }
901
+ }
902
+ async function runAfterRawResponse(afterRawResponse, context) {
873
903
  let result;
874
904
  try {
875
905
  result = await afterRawResponse(context);
@@ -997,7 +1027,7 @@ async function runAfterResponse(res, requestBody, afterResponse) {
997
1027
  ...rawContext,
998
1028
  request: requestBody,
999
1029
  response
1000
- });
1030
+ }, request.signal);
1001
1031
  }
1002
1032
  return response;
1003
1033
  } catch (error) {
package/dist/index.d.cts CHANGED
@@ -210,6 +210,9 @@ type CreateGatewayProxyOptions = {
210
210
  * non-string `generationId`) logs an error and sends the upstream error body instead, and so
211
211
  * does a hook that throws.
212
212
  *
213
+ * An `authentication_error` arrives retyped as `proxy_error`, its message prefixed with
214
+ * `AI Gateway rejected the proxy's credential: `, since only the proxy's credential reaches the gateway.
215
+ *
213
216
  * Not called for GET, HEAD or other requests without a body, which are forwarded as is.
214
217
  *
215
218
  * Not called when the path relative to `baseUrl` is exactly one of the paths `@ai-sdk/gateway`
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sources":["../src/auth.ts","../src/types.ts","../src/proxy.ts","../src/stream.ts"],"mappings":";;AAAA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,mBAAmB,IAAI,OAAO,CAAC,gBAAgB;;ACRhE;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,aAAa,sBAAsB;AACnC,kBAAkB,2BAA2B;AAC7C;AACA,WAAW,oBAAoB;AAC/B,cAAc,eAAe;AAC7B,uBAAuB,wBAAwB;AAC/C;AACA;AACA;AACA;AACA,eAAe,uBAAuB;AACtC;AACA;AACA;AACA;AACA,KAAK,uBAAuB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA,KAAK,iBAAiB;AACtB,UAAU,aAAa;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,kBAAkB,GAAG,iBAAiB;AAC3C;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,KAAK,yBAAyB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,MAAM;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,OAAO,cAAc,OAAO;AACxD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,UAAU,0BAA0B,GAAG,OAAO,CAAC,0BAA0B;AACzE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,kBAAkB,eAAe;AACjC;AACA,qBAAqB,gBAAgB;AACrC,UAAU,eAAe,GAAG,OAAO,CAAC,eAAe;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,eAAe,YAAY;AAC3B;AACA,UAAU,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ;AACnE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,iBAAiB;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,kBAAkB,KAAK,QAAQ,eAAe,OAAO,CAAC,QAAQ;AAC/F;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,oBAAoB,aAAa,OAAO;AAC7C,YAAY,OAAO,CAAC,MAAM;AAC1B,MAAM,OAAO,CAAC,QAAQ;AACtB;AACA;AACA;AACA,KAAK,wBAAwB,GAAG,oBAAoB;AACpD,SAAS,oBAAoB;AAC7B,UAAU,oBAAoB;AAC9B,SAAS,oBAAoB;AAC7B,YAAY,oBAAoB;AAChC,WAAW,oBAAoB;AAC/B;;AC7PA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,kBAAkB,kIAAkI,yBAAyB,KAAK,wBAAwB;;ACLxN;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,uBAAuB;AACrC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,yBAAyB;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,eAAe;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,cAAc,CAAC,UAAU,gBAAgB,0BAA0B,iBAAiB,WAAW,CAAC,yBAAyB,6BAA6B,WAAW,GAAG,cAAc,CAAC,UAAU;;","names":[]}
1
+ {"version":3,"file":"index.d.cts","sources":["../src/auth.ts","../src/types.ts","../src/proxy.ts","../src/stream.ts"],"mappings":";;AAAA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,mBAAmB,IAAI,OAAO,CAAC,gBAAgB;;ACRhE;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,aAAa,sBAAsB;AACnC,kBAAkB,2BAA2B;AAC7C;AACA,WAAW,oBAAoB;AAC/B,cAAc,eAAe;AAC7B,uBAAuB,wBAAwB;AAC/C;AACA;AACA;AACA;AACA,eAAe,uBAAuB;AACtC;AACA;AACA;AACA;AACA,KAAK,uBAAuB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA,KAAK,iBAAiB;AACtB,UAAU,aAAa;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,kBAAkB,GAAG,iBAAiB;AAC3C;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,KAAK,yBAAyB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,MAAM;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,OAAO,cAAc,OAAO;AACxD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,UAAU,0BAA0B,GAAG,OAAO,CAAC,0BAA0B;AACzE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,kBAAkB,eAAe;AACjC;AACA,qBAAqB,gBAAgB;AACrC,UAAU,eAAe,GAAG,OAAO,CAAC,eAAe;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,eAAe,YAAY;AAC3B;AACA,UAAU,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ;AACnE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,iBAAiB;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,kBAAkB,KAAK,QAAQ,eAAe,OAAO,CAAC,QAAQ;AAC/F;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,oBAAoB,aAAa,OAAO;AAC7C,YAAY,OAAO,CAAC,MAAM;AAC1B,MAAM,OAAO,CAAC,QAAQ;AACtB;AACA;AACA;AACA,KAAK,wBAAwB,GAAG,oBAAoB;AACpD,SAAS,oBAAoB;AAC7B,UAAU,oBAAoB;AAC9B,SAAS,oBAAoB;AAC7B,YAAY,oBAAoB;AAChC,WAAW,oBAAoB;AAC/B;;AChQA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,kBAAkB,kIAAkI,yBAAyB,KAAK,wBAAwB;;ACLxN;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,uBAAuB;AACrC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,yBAAyB;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,eAAe;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,cAAc,CAAC,UAAU,gBAAgB,0BAA0B,iBAAiB,WAAW,CAAC,yBAAyB,6BAA6B,WAAW,GAAG,cAAc,CAAC,UAAU;;","names":[]}
package/dist/index.d.ts CHANGED
@@ -210,6 +210,9 @@ type CreateGatewayProxyOptions = {
210
210
  * non-string `generationId`) logs an error and sends the upstream error body instead, and so
211
211
  * does a hook that throws.
212
212
  *
213
+ * An `authentication_error` arrives retyped as `proxy_error`, its message prefixed with
214
+ * `AI Gateway rejected the proxy's credential: `, since only the proxy's credential reaches the gateway.
215
+ *
213
216
  * Not called for GET, HEAD or other requests without a body, which are forwarded as is.
214
217
  *
215
218
  * Not called when the path relative to `baseUrl` is exactly one of the paths `@ai-sdk/gateway`
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sources":["../src/auth.ts","../src/types.ts","../src/proxy.ts","../src/stream.ts"],"mappings":";;AAAA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,mBAAmB,IAAI,OAAO,CAAC,gBAAgB;;ACRhE;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,aAAa,sBAAsB;AACnC,kBAAkB,2BAA2B;AAC7C;AACA,WAAW,oBAAoB;AAC/B,cAAc,eAAe;AAC7B,uBAAuB,wBAAwB;AAC/C;AACA;AACA;AACA;AACA,eAAe,uBAAuB;AACtC;AACA;AACA;AACA;AACA,KAAK,uBAAuB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA,KAAK,iBAAiB;AACtB,UAAU,aAAa;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,kBAAkB,GAAG,iBAAiB;AAC3C;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,KAAK,yBAAyB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,MAAM;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,OAAO,cAAc,OAAO;AACxD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,UAAU,0BAA0B,GAAG,OAAO,CAAC,0BAA0B;AACzE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,kBAAkB,eAAe;AACjC;AACA,qBAAqB,gBAAgB;AACrC,UAAU,eAAe,GAAG,OAAO,CAAC,eAAe;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,eAAe,YAAY;AAC3B;AACA,UAAU,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ;AACnE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,iBAAiB;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,kBAAkB,KAAK,QAAQ,eAAe,OAAO,CAAC,QAAQ;AAC/F;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,oBAAoB,aAAa,OAAO;AAC7C,YAAY,OAAO,CAAC,MAAM;AAC1B,MAAM,OAAO,CAAC,QAAQ;AACtB;AACA;AACA;AACA,KAAK,wBAAwB,GAAG,oBAAoB;AACpD,SAAS,oBAAoB;AAC7B,UAAU,oBAAoB;AAC9B,SAAS,oBAAoB;AAC7B,YAAY,oBAAoB;AAChC,WAAW,oBAAoB;AAC/B;;AC7PA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,kBAAkB,kIAAkI,yBAAyB,KAAK,wBAAwB;;ACLxN;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,uBAAuB;AACrC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,yBAAyB;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,eAAe;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,cAAc,CAAC,UAAU,gBAAgB,0BAA0B,iBAAiB,WAAW,CAAC,yBAAyB,6BAA6B,WAAW,GAAG,cAAc,CAAC,UAAU;;","names":[]}
1
+ {"version":3,"file":"index.d.ts","sources":["../src/auth.ts","../src/types.ts","../src/proxy.ts","../src/stream.ts"],"mappings":";;AAAA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,mBAAmB,IAAI,OAAO,CAAC,gBAAgB;;ACRhE;AACA;AACA;AACA;AACA,KAAK,eAAe;AACpB,aAAa,sBAAsB;AACnC,kBAAkB,2BAA2B;AAC7C;AACA,WAAW,oBAAoB;AAC/B,cAAc,eAAe;AAC7B,uBAAuB,wBAAwB;AAC/C;AACA;AACA;AACA;AACA,eAAe,uBAAuB;AACtC;AACA;AACA;AACA;AACA,KAAK,uBAAuB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,gBAAgB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,YAAY;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA,KAAK,iBAAiB;AACtB,UAAU,aAAa;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,kBAAkB,GAAG,iBAAiB;AAC3C;AACA;AACA;AACA;AACA;AACA,cAAc,QAAQ;AACtB;AACA;AACA;AACA;AACA,KAAK,yBAAyB;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,MAAM;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,OAAO,cAAc,OAAO;AACxD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,UAAU,0BAA0B,GAAG,OAAO,CAAC,0BAA0B;AACzE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,kBAAkB,eAAe;AACjC;AACA,qBAAqB,gBAAgB;AACrC,UAAU,eAAe,GAAG,OAAO,CAAC,eAAe;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,0BAA0B;AAC3C;AACA;AACA,eAAe,YAAY;AAC3B;AACA,UAAU,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,YAAY,GAAG,QAAQ;AACnE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,iBAAiB;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,kBAAkB,KAAK,QAAQ,eAAe,OAAO,CAAC,QAAQ;AAC/F;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,oBAAoB,aAAa,OAAO;AAC7C,YAAY,OAAO,CAAC,MAAM;AAC1B,MAAM,OAAO,CAAC,QAAQ;AACtB;AACA;AACA;AACA,KAAK,wBAAwB,GAAG,oBAAoB;AACpD,SAAS,oBAAoB;AAC7B,UAAU,oBAAoB;AAC9B,SAAS,oBAAoB;AAC7B,YAAY,oBAAoB;AAChC,WAAW,oBAAoB;AAC/B;;AChQA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,kBAAkB,kIAAkI,yBAAyB,KAAK,wBAAwB;;ACLxN;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,uBAAuB;AACrC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,yBAAyB;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA,mBAAmB,eAAe;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,uBAAuB,OAAO,cAAc,CAAC,UAAU,gBAAgB,0BAA0B,iBAAiB,WAAW,CAAC,yBAAyB,6BAA6B,WAAW,GAAG,cAAc,CAAC,UAAU;;","names":[]}
package/dist/index.mjs CHANGED
@@ -683,6 +683,23 @@ function isGatewayError(value) {
683
683
  }
684
684
  };
685
685
  }
686
+ /**
687
+ * Retypes a gateway `authentication_error` as a `proxy_error` naming the proxy's credential, since
688
+ * the client's own credential never reaches the gateway and `@ai-sdk/gateway` would replace the
689
+ * message with advice about it.
690
+ */ function retypeProxyCredentialError(upstreamError) {
691
+ if (upstreamError.error.type !== "authentication_error") {
692
+ return upstreamError;
693
+ }
694
+ return {
695
+ ...upstreamError,
696
+ error: {
697
+ ...upstreamError.error,
698
+ message: `AI Gateway rejected the proxy's credential: ${upstreamError.error.message}`,
699
+ type: "proxy_error"
700
+ }
701
+ };
702
+ }
686
703
  /**
687
704
  * Builds response headers with the given content type, if any, plus the allowlisted upstream headers.
688
705
  */ function responseHeaders(res, contentType) {
@@ -838,7 +855,7 @@ async function runAfterResponse(res, requestBody, afterResponse) {
838
855
  * Builds the client response for a non-redirect upstream response, running the language model hooks given.
839
856
  */ async function forwardUpstreamResponse(res, requestBody, isStreaming, { afterResponse, onError }, signal) {
840
857
  if (!res.ok) {
841
- const errorBody = await parseErrorBody(res);
858
+ const errorBody = retypeProxyCredentialError(await parseErrorBody(res));
842
859
  return await createErrorResponse(res, errorBody, requestBody, onError);
843
860
  }
844
861
  // A 204 or 205 has no body to parse or hand to afterResponse, and a Response with it cannot carry one.
@@ -867,7 +884,20 @@ async function runAfterResponse(res, requestBody, afterResponse) {
867
884
  }
868
885
  /**
869
886
  * Runs afterRawResponse and returns the response to send.
870
- */ async function applyAfterRawResponse(afterRawResponse, context) {
887
+ * The gateway has answered, and billed, a successful call, so a failure there gets a status clients do not retry.
888
+ */ async function applyAfterRawResponse(afterRawResponse, context, signal) {
889
+ try {
890
+ return await runAfterRawResponse(afterRawResponse, context);
891
+ } catch (error) {
892
+ // The handler answers a client abort with 499, and a failed call costs nothing to retry.
893
+ if (signal.aborted || !context.response.ok) {
894
+ throw error;
895
+ }
896
+ console.error("AI Gateway proxy failed after the gateway answered:", error);
897
+ return proxyErrorResponse(ANSWERED_CALL_FAILURE_STATUS, "proxy_error", "Error processing the AI Gateway response");
898
+ }
899
+ }
900
+ async function runAfterRawResponse(afterRawResponse, context) {
871
901
  let result;
872
902
  try {
873
903
  result = await afterRawResponse(context);
@@ -995,7 +1025,7 @@ async function runAfterResponse(res, requestBody, afterResponse) {
995
1025
  ...rawContext,
996
1026
  request: requestBody,
997
1027
  response
998
- });
1028
+ }, request.signal);
999
1029
  }
1000
1030
  return response;
1001
1031
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-gateway-proxy",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",