ai-gateway-proxy 0.4.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
@@ -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({
@@ -255,9 +255,9 @@ Apart from forwarding the gateway's own responses, the handler can answer with a
255
255
  | 400 | `invalid_request_error` | `Invalid JSON body` | A request other than `GET` or `HEAD` has a non-empty body that is not JSON. |
256
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. |
257
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. |
258
- | 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. |
259
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. |
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. 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. |
261
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. |
262
262
  | 500 | `proxy_error` | `No response body` | A successful streamed response arrived without a body. |
263
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
@@ -886,7 +886,20 @@ async function runAfterResponse(res, requestBody, afterResponse) {
886
886
  }
887
887
  /**
888
888
  * Runs afterRawResponse and returns the response to send.
889
- */ 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) {
890
903
  let result;
891
904
  try {
892
905
  result = await afterRawResponse(context);
@@ -1014,7 +1027,7 @@ async function runAfterResponse(res, requestBody, afterResponse) {
1014
1027
  ...rawContext,
1015
1028
  request: requestBody,
1016
1029
  response
1017
- });
1030
+ }, request.signal);
1018
1031
  }
1019
1032
  return response;
1020
1033
  } catch (error) {
package/dist/index.mjs CHANGED
@@ -884,7 +884,20 @@ async function runAfterResponse(res, requestBody, afterResponse) {
884
884
  }
885
885
  /**
886
886
  * Runs afterRawResponse and returns the response to send.
887
- */ 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) {
888
901
  let result;
889
902
  try {
890
903
  result = await afterRawResponse(context);
@@ -1012,7 +1025,7 @@ async function runAfterResponse(res, requestBody, afterResponse) {
1012
1025
  ...rawContext,
1013
1026
  request: requestBody,
1014
1027
  response
1015
- });
1028
+ }, request.signal);
1016
1029
  }
1017
1030
  return response;
1018
1031
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-gateway-proxy",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",