@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,33 @@
|
|
|
1
|
+
import { APICallError } from '@ai-sdk/provider';
|
|
2
|
+
import { extractApiCallResponse, GatewayError } from '.';
|
|
3
|
+
import { createGatewayErrorFromResponse } from './create-gateway-error';
|
|
4
|
+
|
|
5
|
+
export function asGatewayError(
|
|
6
|
+
error: unknown,
|
|
7
|
+
authMethod?: 'api-key' | 'oidc',
|
|
8
|
+
) {
|
|
9
|
+
if (GatewayError.isInstance(error)) {
|
|
10
|
+
return error;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (APICallError.isInstance(error)) {
|
|
14
|
+
return createGatewayErrorFromResponse({
|
|
15
|
+
response: extractApiCallResponse(error),
|
|
16
|
+
statusCode: error.statusCode ?? 500,
|
|
17
|
+
defaultMessage: 'Gateway request failed',
|
|
18
|
+
cause: error,
|
|
19
|
+
authMethod,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return createGatewayErrorFromResponse({
|
|
24
|
+
response: {},
|
|
25
|
+
statusCode: 500,
|
|
26
|
+
defaultMessage:
|
|
27
|
+
error instanceof Error
|
|
28
|
+
? `Gateway request failed: ${error.message}`
|
|
29
|
+
: 'Unknown Gateway error',
|
|
30
|
+
cause: error,
|
|
31
|
+
authMethod,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
createGatewayErrorFromResponse,
|
|
4
|
+
GatewayAuthenticationError,
|
|
5
|
+
GatewayInvalidRequestError,
|
|
6
|
+
GatewayRateLimitError,
|
|
7
|
+
GatewayModelNotFoundError,
|
|
8
|
+
GatewayInternalServerError,
|
|
9
|
+
GatewayResponseError,
|
|
10
|
+
type GatewayErrorResponse,
|
|
11
|
+
} from './index';
|
|
12
|
+
|
|
13
|
+
describe('Valid error responses', () => {
|
|
14
|
+
it('should create GatewayAuthenticationError for authentication_error type', async () => {
|
|
15
|
+
const response: GatewayErrorResponse = {
|
|
16
|
+
error: {
|
|
17
|
+
message: 'Invalid API key',
|
|
18
|
+
type: 'authentication_error',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const error = await createGatewayErrorFromResponse({
|
|
23
|
+
response,
|
|
24
|
+
statusCode: 401,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(error).toBeInstanceOf(GatewayAuthenticationError);
|
|
28
|
+
expect(error.message).toContain('No authentication provided');
|
|
29
|
+
expect(error.statusCode).toBe(401);
|
|
30
|
+
expect(error.type).toBe('authentication_error');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should create GatewayInvalidRequestError for invalid_request_error type', async () => {
|
|
34
|
+
const response: GatewayErrorResponse = {
|
|
35
|
+
error: {
|
|
36
|
+
message: 'Missing required parameter',
|
|
37
|
+
type: 'invalid_request_error',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const error = await createGatewayErrorFromResponse({
|
|
42
|
+
response,
|
|
43
|
+
statusCode: 400,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(error).toBeInstanceOf(GatewayInvalidRequestError);
|
|
47
|
+
expect(error.message).toBe('Missing required parameter');
|
|
48
|
+
expect(error.statusCode).toBe(400);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should create GatewayRateLimitError for rate_limit_exceeded type', async () => {
|
|
52
|
+
const response: GatewayErrorResponse = {
|
|
53
|
+
error: {
|
|
54
|
+
message: 'Rate limit exceeded. Try again later.',
|
|
55
|
+
type: 'rate_limit_exceeded',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const error = await createGatewayErrorFromResponse({
|
|
60
|
+
response,
|
|
61
|
+
statusCode: 429,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(error).toBeInstanceOf(GatewayRateLimitError);
|
|
65
|
+
expect(error.message).toBe('Rate limit exceeded. Try again later.');
|
|
66
|
+
expect(error.statusCode).toBe(429);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should create GatewayModelNotFoundError for model_not_found type', async () => {
|
|
70
|
+
const response: GatewayErrorResponse = {
|
|
71
|
+
error: {
|
|
72
|
+
message: 'Model not available',
|
|
73
|
+
type: 'model_not_found',
|
|
74
|
+
param: { modelId: 'gpt-4-turbo' },
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const error = await createGatewayErrorFromResponse({
|
|
79
|
+
response,
|
|
80
|
+
statusCode: 404,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(error).toBeInstanceOf(GatewayModelNotFoundError);
|
|
84
|
+
expect(error.message).toBe('Model not available');
|
|
85
|
+
expect(error.statusCode).toBe(404);
|
|
86
|
+
expect((error as GatewayModelNotFoundError).modelId).toBe('gpt-4-turbo');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should create GatewayModelNotFoundError without modelId for invalid param', async () => {
|
|
90
|
+
const response: GatewayErrorResponse = {
|
|
91
|
+
error: {
|
|
92
|
+
message: 'Model not available',
|
|
93
|
+
type: 'model_not_found',
|
|
94
|
+
param: { invalidField: 'value' },
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const error = await createGatewayErrorFromResponse({
|
|
99
|
+
response,
|
|
100
|
+
statusCode: 404,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(error).toBeInstanceOf(GatewayModelNotFoundError);
|
|
104
|
+
expect((error as GatewayModelNotFoundError).modelId).toBeUndefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should create GatewayInternalServerError for internal_server_error type', async () => {
|
|
108
|
+
const response: GatewayErrorResponse = {
|
|
109
|
+
error: {
|
|
110
|
+
message: 'Internal server error occurred',
|
|
111
|
+
type: 'internal_server_error',
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const error = await createGatewayErrorFromResponse({
|
|
116
|
+
response,
|
|
117
|
+
statusCode: 500,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(error).toBeInstanceOf(GatewayInternalServerError);
|
|
121
|
+
expect(error.message).toBe('Internal server error occurred');
|
|
122
|
+
expect(error.statusCode).toBe(500);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should create GatewayInternalServerError for unknown error type', async () => {
|
|
126
|
+
const response: GatewayErrorResponse = {
|
|
127
|
+
error: {
|
|
128
|
+
message: 'Unknown error occurred',
|
|
129
|
+
type: 'unknown_error_type',
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const error = await createGatewayErrorFromResponse({
|
|
134
|
+
response,
|
|
135
|
+
statusCode: 500,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
expect(error).toBeInstanceOf(GatewayInternalServerError);
|
|
139
|
+
expect(error.message).toBe('Unknown error occurred');
|
|
140
|
+
expect(error.statusCode).toBe(500);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('Error response edge cases', () => {
|
|
145
|
+
it('should preserve empty string messages from Gateway', async () => {
|
|
146
|
+
const response: GatewayErrorResponse = {
|
|
147
|
+
error: {
|
|
148
|
+
message: '',
|
|
149
|
+
type: 'authentication_error',
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const error = await createGatewayErrorFromResponse({
|
|
154
|
+
response,
|
|
155
|
+
statusCode: 401,
|
|
156
|
+
defaultMessage: 'Custom default message',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
expect(error.message).toContain('No authentication provided'); // Uses contextual message
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should use defaultMessage when response message is null', async () => {
|
|
163
|
+
const response = {
|
|
164
|
+
error: {
|
|
165
|
+
message: null,
|
|
166
|
+
type: 'authentication_error',
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const error = await createGatewayErrorFromResponse({
|
|
171
|
+
response,
|
|
172
|
+
statusCode: 401,
|
|
173
|
+
defaultMessage: 'Custom default message',
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// When the response doesn't pass schema validation, it creates a response error
|
|
177
|
+
expect(error).toBeInstanceOf(GatewayResponseError);
|
|
178
|
+
expect(error.message).toBe(
|
|
179
|
+
'Invalid error response format: Custom default message',
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Verify debugging information is included
|
|
183
|
+
const responseError = error as GatewayResponseError;
|
|
184
|
+
expect(responseError.response).toBe(response);
|
|
185
|
+
expect(responseError.validationError).toBeDefined();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should handle error type as null', async () => {
|
|
189
|
+
const response: GatewayErrorResponse = {
|
|
190
|
+
error: {
|
|
191
|
+
message: 'Some error',
|
|
192
|
+
type: null,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const error = await createGatewayErrorFromResponse({
|
|
197
|
+
response,
|
|
198
|
+
statusCode: 500,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(error).toBeInstanceOf(GatewayInternalServerError);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should include cause in the created error', async () => {
|
|
205
|
+
const originalCause = new Error('Original network error');
|
|
206
|
+
const response: GatewayErrorResponse = {
|
|
207
|
+
error: {
|
|
208
|
+
message: 'Gateway timeout',
|
|
209
|
+
type: 'internal_server_error',
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const error = await createGatewayErrorFromResponse({
|
|
214
|
+
response,
|
|
215
|
+
statusCode: 504,
|
|
216
|
+
cause: originalCause,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
expect(error.cause).toBe(originalCause);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('Malformed responses', () => {
|
|
224
|
+
it('should create GatewayResponseError for completely invalid response', async () => {
|
|
225
|
+
const response = {
|
|
226
|
+
invalidField: 'value',
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const error = await createGatewayErrorFromResponse({
|
|
230
|
+
response,
|
|
231
|
+
statusCode: 500,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
expect(error).toBeInstanceOf(GatewayResponseError);
|
|
235
|
+
expect(error.message).toBe(
|
|
236
|
+
'Invalid error response format: Gateway request failed',
|
|
237
|
+
);
|
|
238
|
+
expect(error.statusCode).toBe(500);
|
|
239
|
+
|
|
240
|
+
// Verify debugging information is included
|
|
241
|
+
const responseError = error as GatewayResponseError;
|
|
242
|
+
expect(responseError.response).toBe(response);
|
|
243
|
+
expect(responseError.validationError).toBeDefined();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should create GatewayResponseError for missing error field', async () => {
|
|
247
|
+
const response = {
|
|
248
|
+
data: 'some data',
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const error = await createGatewayErrorFromResponse({
|
|
252
|
+
response,
|
|
253
|
+
statusCode: 500,
|
|
254
|
+
defaultMessage: 'Custom error message',
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
expect(error).toBeInstanceOf(GatewayResponseError);
|
|
258
|
+
expect(error.message).toBe(
|
|
259
|
+
'Invalid error response format: Custom error message',
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// Verify debugging information is included
|
|
263
|
+
const responseError = error as GatewayResponseError;
|
|
264
|
+
expect(responseError.response).toBe(response);
|
|
265
|
+
expect(responseError.validationError).toBeDefined();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should create GatewayResponseError for null response', async () => {
|
|
269
|
+
const response = null;
|
|
270
|
+
|
|
271
|
+
const error = await createGatewayErrorFromResponse({
|
|
272
|
+
response,
|
|
273
|
+
statusCode: 500,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect(error).toBeInstanceOf(GatewayResponseError);
|
|
277
|
+
expect(error.message).toBe(
|
|
278
|
+
'Invalid error response format: Gateway request failed',
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Verify debugging information is included
|
|
282
|
+
const responseError = error as GatewayResponseError;
|
|
283
|
+
expect(responseError.response).toBe(response);
|
|
284
|
+
expect(responseError.validationError).toBeDefined();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should create GatewayResponseError for string response', async () => {
|
|
288
|
+
const response = 'Error string';
|
|
289
|
+
|
|
290
|
+
const error = await createGatewayErrorFromResponse({
|
|
291
|
+
response,
|
|
292
|
+
statusCode: 500,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(error).toBeInstanceOf(GatewayResponseError);
|
|
296
|
+
expect(error.message).toBe(
|
|
297
|
+
'Invalid error response format: Gateway request failed',
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// Verify debugging information is included
|
|
301
|
+
const responseError = error as GatewayResponseError;
|
|
302
|
+
expect(responseError.response).toBe(response);
|
|
303
|
+
expect(responseError.validationError).toBeDefined();
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should create GatewayResponseError for array response', async () => {
|
|
307
|
+
const response = ['error', 'array'];
|
|
308
|
+
|
|
309
|
+
const error = await createGatewayErrorFromResponse({
|
|
310
|
+
response,
|
|
311
|
+
statusCode: 500,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
expect(error).toBeInstanceOf(GatewayResponseError);
|
|
315
|
+
expect(error.message).toBe(
|
|
316
|
+
'Invalid error response format: Gateway request failed',
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
// Verify debugging information is included
|
|
320
|
+
const responseError = error as GatewayResponseError;
|
|
321
|
+
expect(responseError.response).toBe(response);
|
|
322
|
+
expect(responseError.validationError).toBeDefined();
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe('Object parameter validation', () => {
|
|
327
|
+
it('should use default defaultMessage when not provided', async () => {
|
|
328
|
+
const response = {
|
|
329
|
+
invalidField: 'value',
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const error = await createGatewayErrorFromResponse({
|
|
333
|
+
response,
|
|
334
|
+
statusCode: 500,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
expect(error).toBeInstanceOf(GatewayResponseError);
|
|
338
|
+
expect(error.message).toBe(
|
|
339
|
+
'Invalid error response format: Gateway request failed',
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// Verify debugging information is included
|
|
343
|
+
const responseError = error as GatewayResponseError;
|
|
344
|
+
expect(responseError.response).toBe(response);
|
|
345
|
+
expect(responseError.validationError).toBeDefined();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should handle undefined cause', async () => {
|
|
349
|
+
const response: GatewayErrorResponse = {
|
|
350
|
+
error: {
|
|
351
|
+
message: 'Test error',
|
|
352
|
+
type: 'authentication_error',
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const error = await createGatewayErrorFromResponse({
|
|
357
|
+
response,
|
|
358
|
+
statusCode: 401,
|
|
359
|
+
cause: undefined,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
expect(error.cause).toBeUndefined();
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe('Complex scenarios', () => {
|
|
367
|
+
it('should handle model_not_found with missing param field', async () => {
|
|
368
|
+
const response: GatewayErrorResponse = {
|
|
369
|
+
error: {
|
|
370
|
+
message: 'Model not found',
|
|
371
|
+
type: 'model_not_found',
|
|
372
|
+
// param field missing
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const error = await createGatewayErrorFromResponse({
|
|
377
|
+
response,
|
|
378
|
+
statusCode: 404,
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
expect(error).toBeInstanceOf(GatewayModelNotFoundError);
|
|
382
|
+
expect((error as GatewayModelNotFoundError).modelId).toBeUndefined();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should handle response with extra fields', async () => {
|
|
386
|
+
const response = {
|
|
387
|
+
error: {
|
|
388
|
+
message: 'Test error',
|
|
389
|
+
type: 'authentication_error',
|
|
390
|
+
code: 'AUTH_FAILED',
|
|
391
|
+
param: null,
|
|
392
|
+
extraField: 'should be ignored',
|
|
393
|
+
},
|
|
394
|
+
metadata: 'should be ignored',
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const error = await createGatewayErrorFromResponse({
|
|
398
|
+
response,
|
|
399
|
+
statusCode: 401,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
expect(error).toBeInstanceOf(GatewayAuthenticationError);
|
|
403
|
+
expect(error.message).toContain('No authentication provided');
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('should preserve error properties correctly', async () => {
|
|
407
|
+
const originalCause = new TypeError('Type error');
|
|
408
|
+
const response: GatewayErrorResponse = {
|
|
409
|
+
error: {
|
|
410
|
+
message: 'Rate limit hit',
|
|
411
|
+
type: 'rate_limit_exceeded',
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const error = await createGatewayErrorFromResponse({
|
|
416
|
+
response,
|
|
417
|
+
statusCode: 429,
|
|
418
|
+
defaultMessage: 'Fallback message',
|
|
419
|
+
cause: originalCause,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
expect(error).toBeInstanceOf(GatewayRateLimitError);
|
|
423
|
+
expect(error.message).toBe('Rate limit hit'); // Uses response message, not default
|
|
424
|
+
expect(error.statusCode).toBe(429);
|
|
425
|
+
expect(error.cause).toBe(originalCause);
|
|
426
|
+
expect(error.name).toBe('GatewayRateLimitError');
|
|
427
|
+
expect(error.type).toBe('rate_limit_exceeded');
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
describe('generationId support', () => {
|
|
432
|
+
it('should include generationId in error when present in response', async () => {
|
|
433
|
+
const response = {
|
|
434
|
+
error: {
|
|
435
|
+
message: 'Internal server error',
|
|
436
|
+
type: 'internal_server_error',
|
|
437
|
+
},
|
|
438
|
+
generationId: 'gen_01ABC123XYZ',
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const error = await createGatewayErrorFromResponse({
|
|
442
|
+
response,
|
|
443
|
+
statusCode: 500,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
expect(error).toBeInstanceOf(GatewayInternalServerError);
|
|
447
|
+
expect(error.generationId).toBe('gen_01ABC123XYZ');
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('should include generationId in authentication error', async () => {
|
|
451
|
+
const response = {
|
|
452
|
+
error: {
|
|
453
|
+
message: 'Invalid API key',
|
|
454
|
+
type: 'authentication_error',
|
|
455
|
+
},
|
|
456
|
+
generationId: 'gen_01AUTH456',
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const error = await createGatewayErrorFromResponse({
|
|
460
|
+
response,
|
|
461
|
+
statusCode: 401,
|
|
462
|
+
authMethod: 'api-key',
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
expect(error).toBeInstanceOf(GatewayAuthenticationError);
|
|
466
|
+
expect(error.generationId).toBe('gen_01AUTH456');
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('should include generationId in rate limit error', async () => {
|
|
470
|
+
const response = {
|
|
471
|
+
error: {
|
|
472
|
+
message: 'Rate limit exceeded',
|
|
473
|
+
type: 'rate_limit_exceeded',
|
|
474
|
+
},
|
|
475
|
+
generationId: 'gen_01RATE789',
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const error = await createGatewayErrorFromResponse({
|
|
479
|
+
response,
|
|
480
|
+
statusCode: 429,
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
expect(error).toBeInstanceOf(GatewayRateLimitError);
|
|
484
|
+
expect(error.generationId).toBe('gen_01RATE789');
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('should include generationId in model not found error', async () => {
|
|
488
|
+
const response = {
|
|
489
|
+
error: {
|
|
490
|
+
message: 'Model not found',
|
|
491
|
+
type: 'model_not_found',
|
|
492
|
+
param: { modelId: 'gpt-5' },
|
|
493
|
+
},
|
|
494
|
+
generationId: 'gen_01MODEL000',
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const error = await createGatewayErrorFromResponse({
|
|
498
|
+
response,
|
|
499
|
+
statusCode: 404,
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
expect(error).toBeInstanceOf(GatewayModelNotFoundError);
|
|
503
|
+
expect(error.generationId).toBe('gen_01MODEL000');
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it('should have undefined generationId when not present in response', async () => {
|
|
507
|
+
const response = {
|
|
508
|
+
error: {
|
|
509
|
+
message: 'Some error',
|
|
510
|
+
type: 'internal_server_error',
|
|
511
|
+
},
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const error = await createGatewayErrorFromResponse({
|
|
515
|
+
response,
|
|
516
|
+
statusCode: 500,
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
expect(error.generationId).toBeUndefined();
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it('should extract generationId from malformed response when possible', async () => {
|
|
523
|
+
const response = {
|
|
524
|
+
invalidField: 'value',
|
|
525
|
+
generationId: 'gen_01MALFORMED',
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const error = await createGatewayErrorFromResponse({
|
|
529
|
+
response,
|
|
530
|
+
statusCode: 500,
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
expect(error).toBeInstanceOf(GatewayResponseError);
|
|
534
|
+
expect(error.generationId).toBe('gen_01MALFORMED');
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
describe('authentication_error with authMethod context', () => {
|
|
539
|
+
it('should create contextual error for API key authentication failure', async () => {
|
|
540
|
+
const error = await createGatewayErrorFromResponse({
|
|
541
|
+
response: {
|
|
542
|
+
error: {
|
|
543
|
+
type: 'authentication_error',
|
|
544
|
+
message: 'Invalid API key',
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
statusCode: 401,
|
|
548
|
+
authMethod: 'api-key',
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
expect(error).toBeInstanceOf(GatewayAuthenticationError);
|
|
552
|
+
expect(error.message).toContain('Invalid API key');
|
|
553
|
+
expect(error.message).toContain('vercel.com/d?to=');
|
|
554
|
+
expect(error.statusCode).toBe(401);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it('should create contextual error for OIDC authentication failure', async () => {
|
|
558
|
+
const error = await createGatewayErrorFromResponse({
|
|
559
|
+
response: {
|
|
560
|
+
error: {
|
|
561
|
+
type: 'authentication_error',
|
|
562
|
+
message: 'Invalid OIDC token',
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
statusCode: 401,
|
|
566
|
+
authMethod: 'oidc',
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
expect(error).toBeInstanceOf(GatewayAuthenticationError);
|
|
570
|
+
expect(error.message).toContain('Invalid OIDC token');
|
|
571
|
+
expect(error.message).toContain('npx vercel link');
|
|
572
|
+
expect(error.statusCode).toBe(401);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('should create contextual error without authMethod context', async () => {
|
|
576
|
+
const error = await createGatewayErrorFromResponse({
|
|
577
|
+
response: {
|
|
578
|
+
error: {
|
|
579
|
+
type: 'authentication_error',
|
|
580
|
+
message: 'Authentication failed',
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
statusCode: 401,
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
expect(error).toBeInstanceOf(GatewayAuthenticationError);
|
|
587
|
+
expect(error.message).toContain('No authentication provided');
|
|
588
|
+
expect(error.statusCode).toBe(401);
|
|
589
|
+
});
|
|
590
|
+
});
|