@ai-sdk/gateway 0.0.0-64aae7dd-20260114144918 → 0.0.0-98261322-20260122142521
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/CHANGELOG.md +49 -4
- package/dist/index.d.mts +20 -10
- package/dist/index.d.ts +20 -10
- package/dist/index.js +62 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -25
- package/dist/index.mjs.map +1 -1
- package/docs/00-ai-gateway.mdx +625 -0
- package/package.json +12 -5
- package/src/errors/as-gateway-error.ts +33 -0
- package/src/errors/create-gateway-error.test.ts +590 -0
- package/src/errors/create-gateway-error.ts +132 -0
- package/src/errors/extract-api-call-response.test.ts +270 -0
- package/src/errors/extract-api-call-response.ts +15 -0
- package/src/errors/gateway-authentication-error.ts +84 -0
- package/src/errors/gateway-error-types.test.ts +278 -0
- package/src/errors/gateway-error.ts +47 -0
- package/src/errors/gateway-internal-server-error.ts +33 -0
- package/src/errors/gateway-invalid-request-error.ts +33 -0
- package/src/errors/gateway-model-not-found-error.ts +47 -0
- package/src/errors/gateway-rate-limit-error.ts +33 -0
- package/src/errors/gateway-response-error.ts +42 -0
- package/src/errors/index.ts +16 -0
- package/src/errors/parse-auth-method.test.ts +136 -0
- package/src/errors/parse-auth-method.ts +23 -0
- package/src/gateway-config.ts +7 -0
- package/src/gateway-embedding-model-settings.ts +22 -0
- package/src/gateway-embedding-model.test.ts +213 -0
- package/src/gateway-embedding-model.ts +109 -0
- package/src/gateway-fetch-metadata.test.ts +774 -0
- package/src/gateway-fetch-metadata.ts +127 -0
- package/src/gateway-image-model-settings.ts +12 -0
- package/src/gateway-image-model.test.ts +823 -0
- package/src/gateway-image-model.ts +145 -0
- package/src/gateway-language-model-settings.ts +159 -0
- package/src/gateway-language-model.test.ts +1485 -0
- package/src/gateway-language-model.ts +212 -0
- package/src/gateway-model-entry.ts +58 -0
- package/src/gateway-provider-options.ts +66 -0
- package/src/gateway-provider.test.ts +1210 -0
- package/src/gateway-provider.ts +284 -0
- package/src/gateway-tools.ts +15 -0
- package/src/index.ts +27 -0
- package/src/tool/perplexity-search.ts +294 -0
- package/src/vercel-environment.test.ts +65 -0
- package/src/vercel-environment.ts +6 -0
- package/src/version.ts +6 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import type { GatewayError } from './gateway-error';
|
|
3
|
+
import { GatewayAuthenticationError } from './gateway-authentication-error';
|
|
4
|
+
import { GatewayInvalidRequestError } from './gateway-invalid-request-error';
|
|
5
|
+
import { GatewayRateLimitError } from './gateway-rate-limit-error';
|
|
6
|
+
import {
|
|
7
|
+
GatewayModelNotFoundError,
|
|
8
|
+
modelNotFoundParamSchema,
|
|
9
|
+
} from './gateway-model-not-found-error';
|
|
10
|
+
import { GatewayInternalServerError } from './gateway-internal-server-error';
|
|
11
|
+
import { GatewayResponseError } from './gateway-response-error';
|
|
12
|
+
import {
|
|
13
|
+
InferSchema,
|
|
14
|
+
lazySchema,
|
|
15
|
+
safeValidateTypes,
|
|
16
|
+
validateTypes,
|
|
17
|
+
zodSchema,
|
|
18
|
+
} from '@ai-sdk/provider-utils';
|
|
19
|
+
|
|
20
|
+
export async function createGatewayErrorFromResponse({
|
|
21
|
+
response,
|
|
22
|
+
statusCode,
|
|
23
|
+
defaultMessage = 'Gateway request failed',
|
|
24
|
+
cause,
|
|
25
|
+
authMethod,
|
|
26
|
+
}: {
|
|
27
|
+
response: unknown;
|
|
28
|
+
statusCode: number;
|
|
29
|
+
defaultMessage?: string;
|
|
30
|
+
cause?: unknown;
|
|
31
|
+
authMethod?: 'api-key' | 'oidc';
|
|
32
|
+
}): Promise<GatewayError> {
|
|
33
|
+
const parseResult = await safeValidateTypes({
|
|
34
|
+
value: response,
|
|
35
|
+
schema: gatewayErrorResponseSchema,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!parseResult.success) {
|
|
39
|
+
// Try to extract generationId even if validation failed
|
|
40
|
+
const rawGenerationId =
|
|
41
|
+
typeof response === 'object' &&
|
|
42
|
+
response !== null &&
|
|
43
|
+
'generationId' in response
|
|
44
|
+
? (response as { generationId?: string }).generationId
|
|
45
|
+
: undefined;
|
|
46
|
+
|
|
47
|
+
return new GatewayResponseError({
|
|
48
|
+
message: `Invalid error response format: ${defaultMessage}`,
|
|
49
|
+
statusCode,
|
|
50
|
+
response,
|
|
51
|
+
validationError: parseResult.error,
|
|
52
|
+
cause,
|
|
53
|
+
generationId: rawGenerationId,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const validatedResponse: GatewayErrorResponse = parseResult.value;
|
|
58
|
+
const errorType = validatedResponse.error.type;
|
|
59
|
+
const message = validatedResponse.error.message;
|
|
60
|
+
const generationId = validatedResponse.generationId ?? undefined;
|
|
61
|
+
|
|
62
|
+
switch (errorType) {
|
|
63
|
+
case 'authentication_error':
|
|
64
|
+
return GatewayAuthenticationError.createContextualError({
|
|
65
|
+
apiKeyProvided: authMethod === 'api-key',
|
|
66
|
+
oidcTokenProvided: authMethod === 'oidc',
|
|
67
|
+
statusCode,
|
|
68
|
+
cause,
|
|
69
|
+
generationId,
|
|
70
|
+
});
|
|
71
|
+
case 'invalid_request_error':
|
|
72
|
+
return new GatewayInvalidRequestError({
|
|
73
|
+
message,
|
|
74
|
+
statusCode,
|
|
75
|
+
cause,
|
|
76
|
+
generationId,
|
|
77
|
+
});
|
|
78
|
+
case 'rate_limit_exceeded':
|
|
79
|
+
return new GatewayRateLimitError({
|
|
80
|
+
message,
|
|
81
|
+
statusCode,
|
|
82
|
+
cause,
|
|
83
|
+
generationId,
|
|
84
|
+
});
|
|
85
|
+
case 'model_not_found': {
|
|
86
|
+
const modelResult = await safeValidateTypes({
|
|
87
|
+
value: validatedResponse.error.param,
|
|
88
|
+
schema: modelNotFoundParamSchema,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return new GatewayModelNotFoundError({
|
|
92
|
+
message,
|
|
93
|
+
statusCode,
|
|
94
|
+
modelId: modelResult.success ? modelResult.value.modelId : undefined,
|
|
95
|
+
cause,
|
|
96
|
+
generationId,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
case 'internal_server_error':
|
|
100
|
+
return new GatewayInternalServerError({
|
|
101
|
+
message,
|
|
102
|
+
statusCode,
|
|
103
|
+
cause,
|
|
104
|
+
generationId,
|
|
105
|
+
});
|
|
106
|
+
default:
|
|
107
|
+
return new GatewayInternalServerError({
|
|
108
|
+
message,
|
|
109
|
+
statusCode,
|
|
110
|
+
cause,
|
|
111
|
+
generationId,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const gatewayErrorResponseSchema = lazySchema(() =>
|
|
117
|
+
zodSchema(
|
|
118
|
+
z.object({
|
|
119
|
+
error: z.object({
|
|
120
|
+
message: z.string(),
|
|
121
|
+
type: z.string().nullish(),
|
|
122
|
+
param: z.unknown().nullish(),
|
|
123
|
+
code: z.union([z.string(), z.number()]).nullish(),
|
|
124
|
+
}),
|
|
125
|
+
generationId: z.string().nullish(),
|
|
126
|
+
}),
|
|
127
|
+
),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
export type GatewayErrorResponse = InferSchema<
|
|
131
|
+
typeof gatewayErrorResponseSchema
|
|
132
|
+
>;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { APICallError } from '@ai-sdk/provider';
|
|
3
|
+
import { extractApiCallResponse } from './extract-api-call-response';
|
|
4
|
+
|
|
5
|
+
describe('extractResponseFromAPICallError', () => {
|
|
6
|
+
describe('when error.data is available', () => {
|
|
7
|
+
it('should return error.data when successfully parsed by AI SDK', () => {
|
|
8
|
+
const parsedData = {
|
|
9
|
+
error: { message: 'Parsed error', type: 'authentication_error' },
|
|
10
|
+
};
|
|
11
|
+
const apiCallError = new APICallError({
|
|
12
|
+
message: 'Request failed',
|
|
13
|
+
statusCode: 401,
|
|
14
|
+
data: parsedData,
|
|
15
|
+
responseHeaders: {},
|
|
16
|
+
responseBody: JSON.stringify({ different: 'data' }),
|
|
17
|
+
url: 'http://test.url',
|
|
18
|
+
requestBodyValues: {},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const result = extractApiCallResponse(apiCallError);
|
|
22
|
+
|
|
23
|
+
expect(result).toBe(parsedData); // Should prefer parsed data over responseBody
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should return error.data even when it is null', () => {
|
|
27
|
+
const apiCallError = new APICallError({
|
|
28
|
+
message: 'Request failed',
|
|
29
|
+
statusCode: 500,
|
|
30
|
+
data: null,
|
|
31
|
+
responseHeaders: {},
|
|
32
|
+
responseBody: '{"fallback": "data"}',
|
|
33
|
+
url: 'http://test.url',
|
|
34
|
+
requestBodyValues: {},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const result = extractApiCallResponse(apiCallError);
|
|
38
|
+
|
|
39
|
+
expect(result).toBeNull(); // Should return null, not fallback to responseBody
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should return error.data even when it is an empty object', () => {
|
|
43
|
+
const emptyData = {};
|
|
44
|
+
const apiCallError = new APICallError({
|
|
45
|
+
message: 'Request failed',
|
|
46
|
+
statusCode: 400,
|
|
47
|
+
data: emptyData,
|
|
48
|
+
responseHeaders: {},
|
|
49
|
+
responseBody: '{"fallback": "data"}',
|
|
50
|
+
url: 'http://test.url',
|
|
51
|
+
requestBodyValues: {},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const result = extractApiCallResponse(apiCallError);
|
|
55
|
+
|
|
56
|
+
expect(result).toBe(emptyData); // Should return empty object, not fallback
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('when error.data is undefined', () => {
|
|
61
|
+
it('should parse and return responseBody as JSON when valid', () => {
|
|
62
|
+
const responseData = {
|
|
63
|
+
ferror: { message: 'Malformed error', type: 'model_not_found' },
|
|
64
|
+
};
|
|
65
|
+
const apiCallError = new APICallError({
|
|
66
|
+
message: 'Request failed',
|
|
67
|
+
statusCode: 404,
|
|
68
|
+
data: undefined,
|
|
69
|
+
responseHeaders: {},
|
|
70
|
+
responseBody: JSON.stringify(responseData),
|
|
71
|
+
url: 'http://test.url',
|
|
72
|
+
requestBodyValues: {},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = extractApiCallResponse(apiCallError);
|
|
76
|
+
|
|
77
|
+
expect(result).toEqual(responseData);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return raw responseBody when JSON parsing fails', () => {
|
|
81
|
+
const invalidJson = 'This is not valid JSON';
|
|
82
|
+
const apiCallError = new APICallError({
|
|
83
|
+
message: 'Request failed',
|
|
84
|
+
statusCode: 500,
|
|
85
|
+
data: undefined,
|
|
86
|
+
responseHeaders: {},
|
|
87
|
+
responseBody: invalidJson,
|
|
88
|
+
url: 'http://test.url',
|
|
89
|
+
requestBodyValues: {},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const result = extractApiCallResponse(apiCallError);
|
|
93
|
+
|
|
94
|
+
expect(result).toBe(invalidJson);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should handle HTML error responses', () => {
|
|
98
|
+
const htmlResponse =
|
|
99
|
+
'<html><body><h1>500 Internal Server Error</h1></body></html>';
|
|
100
|
+
const apiCallError = new APICallError({
|
|
101
|
+
message: 'Request failed',
|
|
102
|
+
statusCode: 500,
|
|
103
|
+
data: undefined,
|
|
104
|
+
responseHeaders: {},
|
|
105
|
+
responseBody: htmlResponse,
|
|
106
|
+
url: 'http://test.url',
|
|
107
|
+
requestBodyValues: {},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const result = extractApiCallResponse(apiCallError);
|
|
111
|
+
|
|
112
|
+
expect(result).toBe(htmlResponse);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should handle empty string responseBody', () => {
|
|
116
|
+
const apiCallError = new APICallError({
|
|
117
|
+
message: 'Request failed',
|
|
118
|
+
statusCode: 502,
|
|
119
|
+
data: undefined,
|
|
120
|
+
responseHeaders: {},
|
|
121
|
+
responseBody: '',
|
|
122
|
+
url: 'http://test.url',
|
|
123
|
+
requestBodyValues: {},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const result = extractApiCallResponse(apiCallError);
|
|
127
|
+
|
|
128
|
+
expect(result).toBe('');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should handle malformed JSON gracefully', () => {
|
|
132
|
+
const malformedJson = '{"incomplete": json';
|
|
133
|
+
const apiCallError = new APICallError({
|
|
134
|
+
message: 'Request failed',
|
|
135
|
+
statusCode: 500,
|
|
136
|
+
data: undefined,
|
|
137
|
+
responseHeaders: {},
|
|
138
|
+
responseBody: malformedJson,
|
|
139
|
+
url: 'http://test.url',
|
|
140
|
+
requestBodyValues: {},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const result = extractApiCallResponse(apiCallError);
|
|
144
|
+
|
|
145
|
+
expect(result).toBe(malformedJson); // Should return raw string, not throw
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should parse complex nested JSON structures', () => {
|
|
149
|
+
const complexData = {
|
|
150
|
+
error: {
|
|
151
|
+
message: 'Complex error',
|
|
152
|
+
type: 'validation_error',
|
|
153
|
+
details: {
|
|
154
|
+
field: 'prompt',
|
|
155
|
+
issues: [
|
|
156
|
+
{ code: 'too_long', message: 'Prompt exceeds maximum length' },
|
|
157
|
+
{
|
|
158
|
+
code: 'invalid_format',
|
|
159
|
+
message: 'Contains invalid characters',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
metadata: {
|
|
165
|
+
requestId: '12345',
|
|
166
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const apiCallError = new APICallError({
|
|
171
|
+
message: 'Request failed',
|
|
172
|
+
statusCode: 400,
|
|
173
|
+
data: undefined,
|
|
174
|
+
responseHeaders: {},
|
|
175
|
+
responseBody: JSON.stringify(complexData),
|
|
176
|
+
url: 'http://test.url',
|
|
177
|
+
requestBodyValues: {},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const result = extractApiCallResponse(apiCallError);
|
|
181
|
+
|
|
182
|
+
expect(result).toEqual(complexData);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('when responseBody is not available', () => {
|
|
187
|
+
it('should return empty object when both data and responseBody are undefined', () => {
|
|
188
|
+
const apiCallError = new APICallError({
|
|
189
|
+
message: 'Request failed',
|
|
190
|
+
statusCode: 500,
|
|
191
|
+
data: undefined,
|
|
192
|
+
responseHeaders: {},
|
|
193
|
+
responseBody: undefined as any, // Simulating missing responseBody
|
|
194
|
+
url: 'http://test.url',
|
|
195
|
+
requestBodyValues: {},
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const result = extractApiCallResponse(apiCallError);
|
|
199
|
+
|
|
200
|
+
expect(result).toEqual({});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should return empty object when responseBody is null', () => {
|
|
204
|
+
const apiCallError = new APICallError({
|
|
205
|
+
message: 'Request failed',
|
|
206
|
+
statusCode: 500,
|
|
207
|
+
data: undefined,
|
|
208
|
+
responseHeaders: {},
|
|
209
|
+
responseBody: null as any,
|
|
210
|
+
url: 'http://test.url',
|
|
211
|
+
requestBodyValues: {},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const result = extractApiCallResponse(apiCallError);
|
|
215
|
+
|
|
216
|
+
expect(result).toEqual({});
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('edge cases', () => {
|
|
221
|
+
it('should handle numeric responseBody', () => {
|
|
222
|
+
const apiCallError = new APICallError({
|
|
223
|
+
message: 'Request failed',
|
|
224
|
+
statusCode: 500,
|
|
225
|
+
data: undefined,
|
|
226
|
+
responseHeaders: {},
|
|
227
|
+
responseBody: '404',
|
|
228
|
+
url: 'http://test.url',
|
|
229
|
+
requestBodyValues: {},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const result = extractApiCallResponse(apiCallError);
|
|
233
|
+
|
|
234
|
+
expect(result).toBe(404); // Should parse as number
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should handle boolean responseBody', () => {
|
|
238
|
+
const apiCallError = new APICallError({
|
|
239
|
+
message: 'Request failed',
|
|
240
|
+
statusCode: 500,
|
|
241
|
+
data: undefined,
|
|
242
|
+
responseHeaders: {},
|
|
243
|
+
responseBody: 'true',
|
|
244
|
+
url: 'http://test.url',
|
|
245
|
+
requestBodyValues: {},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const result = extractApiCallResponse(apiCallError);
|
|
249
|
+
|
|
250
|
+
expect(result).toBe(true); // Should parse as boolean
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should handle array responseBody', () => {
|
|
254
|
+
const arrayData = ['error1', 'error2', 'error3'];
|
|
255
|
+
const apiCallError = new APICallError({
|
|
256
|
+
message: 'Request failed',
|
|
257
|
+
statusCode: 400,
|
|
258
|
+
data: undefined,
|
|
259
|
+
responseHeaders: {},
|
|
260
|
+
responseBody: JSON.stringify(arrayData),
|
|
261
|
+
url: 'http://test.url',
|
|
262
|
+
requestBodyValues: {},
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const result = extractApiCallResponse(apiCallError);
|
|
266
|
+
|
|
267
|
+
expect(result).toEqual(arrayData);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { APICallError } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
export function extractApiCallResponse(error: APICallError): unknown {
|
|
4
|
+
if (error.data !== undefined) {
|
|
5
|
+
return error.data;
|
|
6
|
+
}
|
|
7
|
+
if (error.responseBody != null) {
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(error.responseBody);
|
|
10
|
+
} catch {
|
|
11
|
+
return error.responseBody;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { GatewayError } from './gateway-error';
|
|
2
|
+
|
|
3
|
+
const name = 'GatewayAuthenticationError';
|
|
4
|
+
const marker = `vercel.ai.gateway.error.${name}`;
|
|
5
|
+
const symbol = Symbol.for(marker);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Authentication failed - invalid API key or OIDC token
|
|
9
|
+
*/
|
|
10
|
+
export class GatewayAuthenticationError extends GatewayError {
|
|
11
|
+
private readonly [symbol] = true; // used in isInstance
|
|
12
|
+
|
|
13
|
+
readonly name = name;
|
|
14
|
+
readonly type = 'authentication_error';
|
|
15
|
+
|
|
16
|
+
constructor({
|
|
17
|
+
message = 'Authentication failed',
|
|
18
|
+
statusCode = 401,
|
|
19
|
+
cause,
|
|
20
|
+
generationId,
|
|
21
|
+
}: {
|
|
22
|
+
message?: string;
|
|
23
|
+
statusCode?: number;
|
|
24
|
+
cause?: unknown;
|
|
25
|
+
generationId?: string;
|
|
26
|
+
} = {}) {
|
|
27
|
+
super({ message, statusCode, cause, generationId });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static isInstance(error: unknown): error is GatewayAuthenticationError {
|
|
31
|
+
return GatewayError.hasMarker(error) && symbol in error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates a contextual error message when authentication fails
|
|
36
|
+
*/
|
|
37
|
+
static createContextualError({
|
|
38
|
+
apiKeyProvided,
|
|
39
|
+
oidcTokenProvided,
|
|
40
|
+
message = 'Authentication failed',
|
|
41
|
+
statusCode = 401,
|
|
42
|
+
cause,
|
|
43
|
+
generationId,
|
|
44
|
+
}: {
|
|
45
|
+
apiKeyProvided: boolean;
|
|
46
|
+
oidcTokenProvided: boolean;
|
|
47
|
+
message?: string;
|
|
48
|
+
statusCode?: number;
|
|
49
|
+
cause?: unknown;
|
|
50
|
+
generationId?: string;
|
|
51
|
+
}): GatewayAuthenticationError {
|
|
52
|
+
let contextualMessage: string;
|
|
53
|
+
|
|
54
|
+
if (apiKeyProvided) {
|
|
55
|
+
contextualMessage = `AI Gateway authentication failed: Invalid API key.
|
|
56
|
+
|
|
57
|
+
Create a new API key: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys
|
|
58
|
+
|
|
59
|
+
Provide via 'apiKey' option or 'AI_GATEWAY_API_KEY' environment variable.`;
|
|
60
|
+
} else if (oidcTokenProvided) {
|
|
61
|
+
contextualMessage = `AI Gateway authentication failed: Invalid OIDC token.
|
|
62
|
+
|
|
63
|
+
Run 'npx vercel link' to link your project, then 'vc env pull' to fetch the token.
|
|
64
|
+
|
|
65
|
+
Alternatively, use an API key: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys`;
|
|
66
|
+
} else {
|
|
67
|
+
contextualMessage = `AI Gateway authentication failed: No authentication provided.
|
|
68
|
+
|
|
69
|
+
Option 1 - API key:
|
|
70
|
+
Create an API key: https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys
|
|
71
|
+
Provide via 'apiKey' option or 'AI_GATEWAY_API_KEY' environment variable.
|
|
72
|
+
|
|
73
|
+
Option 2 - OIDC token:
|
|
74
|
+
Run 'npx vercel link' to link your project, then 'vc env pull' to fetch the token.`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return new GatewayAuthenticationError({
|
|
78
|
+
message: contextualMessage,
|
|
79
|
+
statusCode,
|
|
80
|
+
cause,
|
|
81
|
+
generationId,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|