ai-gateway-proxy 0.6.0 → 0.7.1
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 +39 -2
- package/dist/index.cjs +23 -4
- package/dist/index.d.cts +13 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +23 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ import { generateText } from "ai";
|
|
|
113
113
|
|
|
114
114
|
const gateway = createGateway({
|
|
115
115
|
baseURL: "https://your-app.example.com/api/ai",
|
|
116
|
-
//
|
|
116
|
+
// Reaches your route only: the proxy sends its own credential upstream. See Protecting the route.
|
|
117
117
|
apiKey: "unused",
|
|
118
118
|
});
|
|
119
119
|
|
|
@@ -123,6 +123,33 @@ const { text } = await generateText({
|
|
|
123
123
|
});
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
+
### Protecting the route
|
|
127
|
+
|
|
128
|
+
The proxy authenticates itself to the gateway but does not check who calls it, so a route set up as above answers anyone who can reach it, on your gateway credit. Check the caller before handing the request to the proxy. The client's `apiKey` reaches your route as `authorization: Bearer <apiKey>`, and the proxy replaces that header upstream, so it can carry the caller's own token:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { createGatewayProxy } from "ai-gateway-proxy";
|
|
132
|
+
|
|
133
|
+
const proxy = createGatewayProxy();
|
|
134
|
+
|
|
135
|
+
async function handler(request: Request, context: { params: Promise<{ segments: string[] }> }) {
|
|
136
|
+
const authorization = request.headers.get("authorization");
|
|
137
|
+
const token = authorization?.startsWith("Bearer ") ? authorization.slice(7) : undefined;
|
|
138
|
+
if (!token || !(await isValidSession(token))) {
|
|
139
|
+
// Not authentication_error: @ai-sdk/gateway replaces that message with advice about gateway keys.
|
|
140
|
+
return Response.json(
|
|
141
|
+
{ error: { message: "Sign in to use the AI route", type: "invalid_request_error" } },
|
|
142
|
+
{ status: 401 },
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return proxy(request, context);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export { handler as GET, handler as POST, handler as PUT, handler as DELETE, handler as PATCH };
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`isValidSession` stands for your own session or token check. On the client, pass the caller's token as `apiKey`, as in `createGateway({ baseURL, apiKey: sessionToken })`. A rejected call throws a `GatewayInvalidRequestError` with `statusCode` 401 and your message, and the AI SDK does not retry it. Do not reject callers through the `apiKey` option instead: a missing key answers `500`, which clients retry.
|
|
152
|
+
|
|
126
153
|
Some gateway client calls do not go through the proxy:
|
|
127
154
|
|
|
128
155
|
- Streaming speech and transcription (the gateway models' `doStream`) open a WebSocket to `speech-model` or `transcription-model` under `baseURL`. The proxy handles HTTP requests only, so it cannot carry those connections. Generating speech or a transcription without streaming does go through the proxy.
|
|
@@ -132,19 +159,28 @@ Some gateway client calls do not go through the proxy:
|
|
|
132
159
|
|
|
133
160
|
## Authentication
|
|
134
161
|
|
|
135
|
-
|
|
162
|
+
Without the `apiKey` option, the proxy gets a credential on every request with `getGatewayAuthToken()`, in this order:
|
|
136
163
|
|
|
137
164
|
1. **API key:** the `AI_GATEWAY_API_KEY` environment variable
|
|
138
165
|
2. **OIDC:** a Vercel OIDC token, available when deployed on Vercel
|
|
139
166
|
|
|
140
167
|
`getGatewayAuthToken()` is exported. It resolves to `{ token, authMethod: "api-key" | "oidc" }`, or `null` when neither is available, in which case the proxy answers `500` with a `proxy_error` (see below). The proxy sends the token upstream as `authorization: Bearer <token>` with `ai-gateway-auth-method`, so the credential never reaches the client.
|
|
141
168
|
|
|
169
|
+
To take the key from somewhere else, such as a secret store or a key per tenant, pass `apiKey`: a string, or a function of the incoming `Request` that returns one or a promise of one. It runs on every request, before the body is read, so read the request's headers or URL, not its body. When `apiKey` is set it replaces the environment variable and OIDC: an empty or missing result, or a throw, answers `500` with a `proxy_error` without calling the gateway, and never falls back to the operator's own credential.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const proxy = createGatewayProxy({
|
|
173
|
+
apiKey: async (request) => keyForTenant(request.headers.get("x-tenant-id")),
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
142
177
|
## Options
|
|
143
178
|
|
|
144
179
|
`createGatewayProxy(options?)` returns a handler `(request: Request, context?) => Promise<Response>`. The same function is also exposed as its `GET`, `POST`, `PUT`, `DELETE` and `PATCH` properties.
|
|
145
180
|
|
|
146
181
|
| Option | Default | Description |
|
|
147
182
|
| --- | --- | --- |
|
|
183
|
+
| `apiKey` | `AI_GATEWAY_API_KEY`, then Vercel OIDC | `string \| (request) => string \| undefined \| Promise<...>`. The gateway API key, resolved on every request; see Authentication. |
|
|
148
184
|
| `baseUrl` | `https://ai-gateway.vercel.sh/v4/ai` | Gateway URL that the extracted path and the incoming query string are appended to. |
|
|
149
185
|
| `headers` | none | Extra headers for every upstream request. The proxy's own `authorization`, `ai-gateway-auth-method` and `ai-gateway-protocol-version` override entries with the same name in any casing, as does `content-type` on requests with a body. On requests with a body, the incoming request's `ai-language-model-*`, `ai-model-id` and `ai-*-model-specification-version` headers replace any entries describing the model call, and its `ai-o11y-*`, `idempotency-key` and `accept` headers override entries with the same name. No other incoming header is forwarded, so the client's `authorization` and cookies never reach the gateway. |
|
|
150
186
|
| `segmentsParam` | `"segments"` | Name of the catch-all route param read from `context.params` when `extractPath` is not set, joined with `/`. Use `"path"` for `app/api/ai/[...path]/route.ts`. |
|
|
@@ -250,6 +286,7 @@ Apart from forwarding the gateway's own responses, the handler can answer with a
|
|
|
250
286
|
| Status | Type | Message | When |
|
|
251
287
|
| --- | --- | --- | --- |
|
|
252
288
|
| 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. |
|
|
289
|
+
| 500 | `proxy_error` | `AI Gateway authentication not configured: the apiKey option gave no key` | The `apiKey` option resolved to an empty or missing key, or threw; the reason is logged without the key. The gateway is not called. |
|
|
253
290
|
| upstream, such as 401 | `proxy_error` | `AI Gateway rejected the proxy's credential: <message>` | The gateway answered with an error typed `authentication_error`, for a call with a body or a bodyless one such as `GET /config`; see `onError` above. The other fields of the error are kept. |
|
|
254
291
|
| 400 | `invalid_request_error` | `Invalid path` | The path is not a safe relative path under `baseUrl`. |
|
|
255
292
|
| 400 | `invalid_request_error` | `Invalid JSON body` | A request other than `GET` or `HEAD` has a non-empty body that is not JSON. |
|
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,26 @@ var providerUtils = require('@ai-sdk/provider-utils');
|
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolves the `apiKey` option for one request. Returns `null` when it gives no key or throws,
|
|
32
|
+
* after logging why, and never falls back to the environment or OIDC.
|
|
33
|
+
*/ async function resolveApiKeyOption(apiKey, request) {
|
|
34
|
+
try {
|
|
35
|
+
const key = typeof apiKey === "function" ? await apiKey(request) : apiKey;
|
|
36
|
+
if (typeof key === "string" && key !== "") {
|
|
37
|
+
return {
|
|
38
|
+
token: key,
|
|
39
|
+
authMethod: "api-key"
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
console.error("AI Gateway auth: the apiKey option gave no key");
|
|
43
|
+
return null;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
// Only the message: the error object could carry token material.
|
|
46
|
+
console.error("AI Gateway auth: the apiKey option failed:", error instanceof Error ? error.message : String(error));
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
30
50
|
|
|
31
51
|
function isRecord(value) {
|
|
32
52
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -953,13 +973,12 @@ async function runAfterRawResponse(afterRawResponse, context) {
|
|
|
953
973
|
*
|
|
954
974
|
* Uses AI SDK V4 Language Model.
|
|
955
975
|
* @see https://github.com/vercel/ai/blob/main/packages/gateway/src/gateway-language-model.ts
|
|
956
|
-
*/ const createGatewayProxy = ({ baseUrl = DEFAULT_BASE_URL, headers, segmentsParam = "segments", extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse } = {})=>{
|
|
976
|
+
*/ const createGatewayProxy = ({ apiKey, baseUrl = DEFAULT_BASE_URL, headers, segmentsParam = "segments", extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse } = {})=>{
|
|
957
977
|
const handler = async (request, context)=>{
|
|
958
|
-
|
|
959
|
-
const auth = await getGatewayAuthToken();
|
|
978
|
+
const auth = apiKey === undefined ? await getGatewayAuthToken() : await resolveApiKeyOption(apiKey, request);
|
|
960
979
|
if (!auth) {
|
|
961
980
|
// Not authentication_error: @ai-sdk/gateway replaces that message with advice about the client's own key.
|
|
962
|
-
return proxyErrorResponse(500, "proxy_error", "AI Gateway authentication not configured: set AI_GATEWAY_API_KEY or deploy on Vercel with OIDC enabled");
|
|
981
|
+
return proxyErrorResponse(500, "proxy_error", apiKey === undefined ? "AI Gateway authentication not configured: set AI_GATEWAY_API_KEY or deploy on Vercel with OIDC enabled" : "AI Gateway authentication not configured: the apiKey option gave no key");
|
|
963
982
|
}
|
|
964
983
|
try {
|
|
965
984
|
// Build upstream URL
|
package/dist/index.d.cts
CHANGED
|
@@ -10,6 +10,10 @@ type GatewayAuthToken = {
|
|
|
10
10
|
* Returns `null` when neither is available, after logging why OIDC failed, and the proxy then answers 500.
|
|
11
11
|
*/
|
|
12
12
|
declare function getGatewayAuthToken(): Promise<GatewayAuthToken | null>;
|
|
13
|
+
/**
|
|
14
|
+
* A gateway API key, or a function of the incoming request that returns one.
|
|
15
|
+
*/
|
|
16
|
+
type GatewayApiKey = string | ((request: Request) => string | null | undefined | Promise<string | null | undefined>);
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
19
|
* The response body from the AI Gateway for non-streaming requests.
|
|
@@ -92,6 +96,13 @@ type RawResponseContext = RawRequestContext & {
|
|
|
92
96
|
* Options for creating a gateway proxy.
|
|
93
97
|
*/
|
|
94
98
|
type CreateGatewayProxyOptions = {
|
|
99
|
+
/**
|
|
100
|
+
* The gateway API key, or a function of the incoming request that returns it, resolved on every request.
|
|
101
|
+
* When set, it replaces `AI_GATEWAY_API_KEY` and Vercel OIDC: an empty result or a throw answers
|
|
102
|
+
* 500 `proxy_error` without calling the gateway. Read the request's headers or URL, not its body.
|
|
103
|
+
* @default the `AI_GATEWAY_API_KEY` environment variable, then Vercel OIDC
|
|
104
|
+
*/
|
|
105
|
+
apiKey?: GatewayApiKey;
|
|
95
106
|
/**
|
|
96
107
|
* Base URL for the AI Gateway. A trailing slash is accepted.
|
|
97
108
|
* Request paths are resolved under it and cannot leave it; one that would returns 400 Invalid path.
|
|
@@ -275,7 +286,7 @@ type CreateGatewayProxyResult = CreateGatewayProxyFn & {
|
|
|
275
286
|
* Uses AI SDK V4 Language Model.
|
|
276
287
|
* @see https://github.com/vercel/ai/blob/main/packages/gateway/src/gateway-language-model.ts
|
|
277
288
|
*/
|
|
278
|
-
declare const createGatewayProxy: ({ baseUrl, headers, segmentsParam, extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse, }?: CreateGatewayProxyOptions) => CreateGatewayProxyResult;
|
|
289
|
+
declare const createGatewayProxy: ({ apiKey, baseUrl, headers, segmentsParam, extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse, }?: CreateGatewayProxyOptions) => CreateGatewayProxyResult;
|
|
279
290
|
|
|
280
291
|
/**
|
|
281
292
|
* Aggregator for collecting stream content.
|
|
@@ -328,5 +339,5 @@ declare class StreamContentAggregator {
|
|
|
328
339
|
declare function createStreamTransformer(body: ReadableStream<Uint8Array>, requestBody: LanguageModelV4CallOptions, afterResponse: NonNullable<CreateGatewayProxyOptions["afterResponse"]>, signal?: AbortSignal): ReadableStream<Uint8Array>;
|
|
329
340
|
|
|
330
341
|
export { StreamContentAggregator, createGatewayProxy, createStreamTransformer, getGatewayAuthToken };
|
|
331
|
-
export type { CreateGatewayProxyFn, CreateGatewayProxyOptions, CreateGatewayProxyResult, GatewayAuthToken, GatewayError, GatewayResponse, GatewayResponseMetadata, IncompleteStream, ModelCallKind, RawRequestContext, RawResponseContext };
|
|
342
|
+
export type { CreateGatewayProxyFn, CreateGatewayProxyOptions, CreateGatewayProxyResult, GatewayApiKey, GatewayAuthToken, GatewayError, GatewayResponse, GatewayResponseMetadata, IncompleteStream, ModelCallKind, RawRequestContext, RawResponseContext };
|
|
332
343
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -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;;
|
|
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;AAChE;AACA;AACA;AACA,KAAK,aAAa,uBAAuB,OAAO,iCAAiC,OAAO;;ACXxF;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,aAAa,aAAa;AAC1B;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;;ACxQA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,kBAAkB,0IAA0I,yBAAyB,KAAK,wBAAwB;;ACLhO;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
|
@@ -10,6 +10,10 @@ type GatewayAuthToken = {
|
|
|
10
10
|
* Returns `null` when neither is available, after logging why OIDC failed, and the proxy then answers 500.
|
|
11
11
|
*/
|
|
12
12
|
declare function getGatewayAuthToken(): Promise<GatewayAuthToken | null>;
|
|
13
|
+
/**
|
|
14
|
+
* A gateway API key, or a function of the incoming request that returns one.
|
|
15
|
+
*/
|
|
16
|
+
type GatewayApiKey = string | ((request: Request) => string | null | undefined | Promise<string | null | undefined>);
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
19
|
* The response body from the AI Gateway for non-streaming requests.
|
|
@@ -92,6 +96,13 @@ type RawResponseContext = RawRequestContext & {
|
|
|
92
96
|
* Options for creating a gateway proxy.
|
|
93
97
|
*/
|
|
94
98
|
type CreateGatewayProxyOptions = {
|
|
99
|
+
/**
|
|
100
|
+
* The gateway API key, or a function of the incoming request that returns it, resolved on every request.
|
|
101
|
+
* When set, it replaces `AI_GATEWAY_API_KEY` and Vercel OIDC: an empty result or a throw answers
|
|
102
|
+
* 500 `proxy_error` without calling the gateway. Read the request's headers or URL, not its body.
|
|
103
|
+
* @default the `AI_GATEWAY_API_KEY` environment variable, then Vercel OIDC
|
|
104
|
+
*/
|
|
105
|
+
apiKey?: GatewayApiKey;
|
|
95
106
|
/**
|
|
96
107
|
* Base URL for the AI Gateway. A trailing slash is accepted.
|
|
97
108
|
* Request paths are resolved under it and cannot leave it; one that would returns 400 Invalid path.
|
|
@@ -275,7 +286,7 @@ type CreateGatewayProxyResult = CreateGatewayProxyFn & {
|
|
|
275
286
|
* Uses AI SDK V4 Language Model.
|
|
276
287
|
* @see https://github.com/vercel/ai/blob/main/packages/gateway/src/gateway-language-model.ts
|
|
277
288
|
*/
|
|
278
|
-
declare const createGatewayProxy: ({ baseUrl, headers, segmentsParam, extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse, }?: CreateGatewayProxyOptions) => CreateGatewayProxyResult;
|
|
289
|
+
declare const createGatewayProxy: ({ apiKey, baseUrl, headers, segmentsParam, extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse, }?: CreateGatewayProxyOptions) => CreateGatewayProxyResult;
|
|
279
290
|
|
|
280
291
|
/**
|
|
281
292
|
* Aggregator for collecting stream content.
|
|
@@ -328,5 +339,5 @@ declare class StreamContentAggregator {
|
|
|
328
339
|
declare function createStreamTransformer(body: ReadableStream<Uint8Array>, requestBody: LanguageModelV4CallOptions, afterResponse: NonNullable<CreateGatewayProxyOptions["afterResponse"]>, signal?: AbortSignal): ReadableStream<Uint8Array>;
|
|
329
340
|
|
|
330
341
|
export { StreamContentAggregator, createGatewayProxy, createStreamTransformer, getGatewayAuthToken };
|
|
331
|
-
export type { CreateGatewayProxyFn, CreateGatewayProxyOptions, CreateGatewayProxyResult, GatewayAuthToken, GatewayError, GatewayResponse, GatewayResponseMetadata, IncompleteStream, ModelCallKind, RawRequestContext, RawResponseContext };
|
|
342
|
+
export type { CreateGatewayProxyFn, CreateGatewayProxyOptions, CreateGatewayProxyResult, GatewayApiKey, GatewayAuthToken, GatewayError, GatewayResponse, GatewayResponseMetadata, IncompleteStream, ModelCallKind, RawRequestContext, RawResponseContext };
|
|
332
343
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;;
|
|
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;AAChE;AACA;AACA;AACA,KAAK,aAAa,uBAAuB,OAAO,iCAAiC,OAAO;;ACXxF;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,aAAa,aAAa;AAC1B;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;;ACxQA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,kBAAkB,0IAA0I,yBAAyB,KAAK,wBAAwB;;ACLhO;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
|
@@ -25,6 +25,26 @@ import { EventSourceParserStream } from '@ai-sdk/provider-utils';
|
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolves the `apiKey` option for one request. Returns `null` when it gives no key or throws,
|
|
30
|
+
* after logging why, and never falls back to the environment or OIDC.
|
|
31
|
+
*/ async function resolveApiKeyOption(apiKey, request) {
|
|
32
|
+
try {
|
|
33
|
+
const key = typeof apiKey === "function" ? await apiKey(request) : apiKey;
|
|
34
|
+
if (typeof key === "string" && key !== "") {
|
|
35
|
+
return {
|
|
36
|
+
token: key,
|
|
37
|
+
authMethod: "api-key"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
console.error("AI Gateway auth: the apiKey option gave no key");
|
|
41
|
+
return null;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// Only the message: the error object could carry token material.
|
|
44
|
+
console.error("AI Gateway auth: the apiKey option failed:", error instanceof Error ? error.message : String(error));
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
28
48
|
|
|
29
49
|
function isRecord(value) {
|
|
30
50
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -951,13 +971,12 @@ async function runAfterRawResponse(afterRawResponse, context) {
|
|
|
951
971
|
*
|
|
952
972
|
* Uses AI SDK V4 Language Model.
|
|
953
973
|
* @see https://github.com/vercel/ai/blob/main/packages/gateway/src/gateway-language-model.ts
|
|
954
|
-
*/ const createGatewayProxy = ({ baseUrl = DEFAULT_BASE_URL, headers, segmentsParam = "segments", extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse } = {})=>{
|
|
974
|
+
*/ const createGatewayProxy = ({ apiKey, baseUrl = DEFAULT_BASE_URL, headers, segmentsParam = "segments", extractPath, beforeRequest, afterResponse, onError, beforeRawRequest, afterRawResponse } = {})=>{
|
|
955
975
|
const handler = async (request, context)=>{
|
|
956
|
-
|
|
957
|
-
const auth = await getGatewayAuthToken();
|
|
976
|
+
const auth = apiKey === undefined ? await getGatewayAuthToken() : await resolveApiKeyOption(apiKey, request);
|
|
958
977
|
if (!auth) {
|
|
959
978
|
// Not authentication_error: @ai-sdk/gateway replaces that message with advice about the client's own key.
|
|
960
|
-
return proxyErrorResponse(500, "proxy_error", "AI Gateway authentication not configured: set AI_GATEWAY_API_KEY or deploy on Vercel with OIDC enabled");
|
|
979
|
+
return proxyErrorResponse(500, "proxy_error", apiKey === undefined ? "AI Gateway authentication not configured: set AI_GATEWAY_API_KEY or deploy on Vercel with OIDC enabled" : "AI Gateway authentication not configured: the apiKey option gave no key");
|
|
961
980
|
}
|
|
962
981
|
try {
|
|
963
982
|
// Build upstream URL
|