@ai-sdk/gateway 0.0.0-1c33ba03-20260114162300 → 0.0.0-4115c213-20260122152721
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 +50 -3
- 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 +16 -5
- package/src/errors/as-gateway-error.ts +33 -0
- package/src/errors/create-gateway-error.ts +132 -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.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.ts +23 -0
- package/src/gateway-config.ts +7 -0
- package/src/gateway-embedding-model-settings.ts +22 -0
- package/src/gateway-embedding-model.ts +109 -0
- package/src/gateway-fetch-metadata.ts +127 -0
- package/src/gateway-image-model-settings.ts +12 -0
- package/src/gateway-image-model.ts +145 -0
- package/src/gateway-language-model-settings.ts +159 -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.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.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,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,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
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const marker = 'vercel.ai.gateway.error';
|
|
2
|
+
const symbol = Symbol.for(marker);
|
|
3
|
+
|
|
4
|
+
export abstract class GatewayError extends Error {
|
|
5
|
+
private readonly [symbol] = true; // used in isInstance
|
|
6
|
+
|
|
7
|
+
abstract readonly name: string;
|
|
8
|
+
abstract readonly type: string;
|
|
9
|
+
readonly statusCode: number;
|
|
10
|
+
readonly cause?: unknown;
|
|
11
|
+
readonly generationId?: string;
|
|
12
|
+
|
|
13
|
+
constructor({
|
|
14
|
+
message,
|
|
15
|
+
statusCode = 500,
|
|
16
|
+
cause,
|
|
17
|
+
generationId,
|
|
18
|
+
}: {
|
|
19
|
+
message: string;
|
|
20
|
+
statusCode?: number;
|
|
21
|
+
cause?: unknown;
|
|
22
|
+
generationId?: string;
|
|
23
|
+
}) {
|
|
24
|
+
super(generationId ? `${message} [${generationId}]` : message);
|
|
25
|
+
this.statusCode = statusCode;
|
|
26
|
+
this.cause = cause;
|
|
27
|
+
this.generationId = generationId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Checks if the given error is a Gateway Error.
|
|
32
|
+
* @param {unknown} error - The error to check.
|
|
33
|
+
* @returns {boolean} True if the error is a Gateway Error, false otherwise.
|
|
34
|
+
*/
|
|
35
|
+
static isInstance(error: unknown): error is GatewayError {
|
|
36
|
+
return GatewayError.hasMarker(error);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static hasMarker(error: unknown): error is GatewayError {
|
|
40
|
+
return (
|
|
41
|
+
typeof error === 'object' &&
|
|
42
|
+
error !== null &&
|
|
43
|
+
symbol in error &&
|
|
44
|
+
(error as any)[symbol] === true
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GatewayError } from './gateway-error';
|
|
2
|
+
|
|
3
|
+
const name = 'GatewayInternalServerError';
|
|
4
|
+
const marker = `vercel.ai.gateway.error.${name}`;
|
|
5
|
+
const symbol = Symbol.for(marker);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal server error from the Gateway
|
|
9
|
+
*/
|
|
10
|
+
export class GatewayInternalServerError extends GatewayError {
|
|
11
|
+
private readonly [symbol] = true; // used in isInstance
|
|
12
|
+
|
|
13
|
+
readonly name = name;
|
|
14
|
+
readonly type = 'internal_server_error';
|
|
15
|
+
|
|
16
|
+
constructor({
|
|
17
|
+
message = 'Internal server error',
|
|
18
|
+
statusCode = 500,
|
|
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 GatewayInternalServerError {
|
|
31
|
+
return GatewayError.hasMarker(error) && symbol in error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GatewayError } from './gateway-error';
|
|
2
|
+
|
|
3
|
+
const name = 'GatewayInvalidRequestError';
|
|
4
|
+
const marker = `vercel.ai.gateway.error.${name}`;
|
|
5
|
+
const symbol = Symbol.for(marker);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Invalid request - missing headers, malformed data, etc.
|
|
9
|
+
*/
|
|
10
|
+
export class GatewayInvalidRequestError extends GatewayError {
|
|
11
|
+
private readonly [symbol] = true; // used in isInstance
|
|
12
|
+
|
|
13
|
+
readonly name = name;
|
|
14
|
+
readonly type = 'invalid_request_error';
|
|
15
|
+
|
|
16
|
+
constructor({
|
|
17
|
+
message = 'Invalid request',
|
|
18
|
+
statusCode = 400,
|
|
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 GatewayInvalidRequestError {
|
|
31
|
+
return GatewayError.hasMarker(error) && symbol in error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { GatewayError } from './gateway-error';
|
|
3
|
+
import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
|
|
4
|
+
|
|
5
|
+
const name = 'GatewayModelNotFoundError';
|
|
6
|
+
const marker = `vercel.ai.gateway.error.${name}`;
|
|
7
|
+
const symbol = Symbol.for(marker);
|
|
8
|
+
|
|
9
|
+
export const modelNotFoundParamSchema = lazySchema(() =>
|
|
10
|
+
zodSchema(
|
|
11
|
+
z.object({
|
|
12
|
+
modelId: z.string(),
|
|
13
|
+
}),
|
|
14
|
+
),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Model not found or not available
|
|
19
|
+
*/
|
|
20
|
+
export class GatewayModelNotFoundError extends GatewayError {
|
|
21
|
+
private readonly [symbol] = true; // used in isInstance
|
|
22
|
+
|
|
23
|
+
readonly name = name;
|
|
24
|
+
readonly type = 'model_not_found';
|
|
25
|
+
readonly modelId?: string;
|
|
26
|
+
|
|
27
|
+
constructor({
|
|
28
|
+
message = 'Model not found',
|
|
29
|
+
statusCode = 404,
|
|
30
|
+
modelId,
|
|
31
|
+
cause,
|
|
32
|
+
generationId,
|
|
33
|
+
}: {
|
|
34
|
+
message?: string;
|
|
35
|
+
statusCode?: number;
|
|
36
|
+
modelId?: string;
|
|
37
|
+
cause?: unknown;
|
|
38
|
+
generationId?: string;
|
|
39
|
+
} = {}) {
|
|
40
|
+
super({ message, statusCode, cause, generationId });
|
|
41
|
+
this.modelId = modelId;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static isInstance(error: unknown): error is GatewayModelNotFoundError {
|
|
45
|
+
return GatewayError.hasMarker(error) && symbol in error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GatewayError } from './gateway-error';
|
|
2
|
+
|
|
3
|
+
const name = 'GatewayRateLimitError';
|
|
4
|
+
const marker = `vercel.ai.gateway.error.${name}`;
|
|
5
|
+
const symbol = Symbol.for(marker);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Rate limit exceeded.
|
|
9
|
+
*/
|
|
10
|
+
export class GatewayRateLimitError extends GatewayError {
|
|
11
|
+
private readonly [symbol] = true; // used in isInstance
|
|
12
|
+
|
|
13
|
+
readonly name = name;
|
|
14
|
+
readonly type = 'rate_limit_exceeded';
|
|
15
|
+
|
|
16
|
+
constructor({
|
|
17
|
+
message = 'Rate limit exceeded',
|
|
18
|
+
statusCode = 429,
|
|
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 GatewayRateLimitError {
|
|
31
|
+
return GatewayError.hasMarker(error) && symbol in error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { TypeValidationError } from '@ai-sdk/provider';
|
|
2
|
+
import { GatewayError } from './gateway-error';
|
|
3
|
+
|
|
4
|
+
const name = 'GatewayResponseError';
|
|
5
|
+
const marker = `vercel.ai.gateway.error.${name}`;
|
|
6
|
+
const symbol = Symbol.for(marker);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Gateway response parsing error
|
|
10
|
+
*/
|
|
11
|
+
export class GatewayResponseError extends GatewayError {
|
|
12
|
+
private readonly [symbol] = true; // used in isInstance
|
|
13
|
+
|
|
14
|
+
readonly name = name;
|
|
15
|
+
readonly type = 'response_error';
|
|
16
|
+
readonly response?: unknown;
|
|
17
|
+
readonly validationError?: TypeValidationError;
|
|
18
|
+
|
|
19
|
+
constructor({
|
|
20
|
+
message = 'Invalid response from Gateway',
|
|
21
|
+
statusCode = 502,
|
|
22
|
+
response,
|
|
23
|
+
validationError,
|
|
24
|
+
cause,
|
|
25
|
+
generationId,
|
|
26
|
+
}: {
|
|
27
|
+
message?: string;
|
|
28
|
+
statusCode?: number;
|
|
29
|
+
response?: unknown;
|
|
30
|
+
validationError?: TypeValidationError;
|
|
31
|
+
cause?: unknown;
|
|
32
|
+
generationId?: string;
|
|
33
|
+
} = {}) {
|
|
34
|
+
super({ message, statusCode, cause, generationId });
|
|
35
|
+
this.response = response;
|
|
36
|
+
this.validationError = validationError;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static isInstance(error: unknown): error is GatewayResponseError {
|
|
40
|
+
return GatewayError.hasMarker(error) && symbol in error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { asGatewayError } from './as-gateway-error';
|
|
2
|
+
export {
|
|
3
|
+
createGatewayErrorFromResponse,
|
|
4
|
+
type GatewayErrorResponse,
|
|
5
|
+
} from './create-gateway-error';
|
|
6
|
+
export { extractApiCallResponse } from './extract-api-call-response';
|
|
7
|
+
export { GatewayError } from './gateway-error';
|
|
8
|
+
export { GatewayAuthenticationError } from './gateway-authentication-error';
|
|
9
|
+
export { GatewayInternalServerError } from './gateway-internal-server-error';
|
|
10
|
+
export { GatewayInvalidRequestError } from './gateway-invalid-request-error';
|
|
11
|
+
export {
|
|
12
|
+
GatewayModelNotFoundError,
|
|
13
|
+
modelNotFoundParamSchema,
|
|
14
|
+
} from './gateway-model-not-found-error';
|
|
15
|
+
export { GatewayRateLimitError } from './gateway-rate-limit-error';
|
|
16
|
+
export { GatewayResponseError } from './gateway-response-error';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import {
|
|
3
|
+
lazySchema,
|
|
4
|
+
safeValidateTypes,
|
|
5
|
+
zodSchema,
|
|
6
|
+
} from '@ai-sdk/provider-utils';
|
|
7
|
+
|
|
8
|
+
export const GATEWAY_AUTH_METHOD_HEADER = 'ai-gateway-auth-method' as const;
|
|
9
|
+
|
|
10
|
+
export async function parseAuthMethod(
|
|
11
|
+
headers: Record<string, string | undefined>,
|
|
12
|
+
) {
|
|
13
|
+
const result = await safeValidateTypes({
|
|
14
|
+
value: headers[GATEWAY_AUTH_METHOD_HEADER],
|
|
15
|
+
schema: gatewayAuthMethodSchema,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return result.success ? result.value : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const gatewayAuthMethodSchema = lazySchema(() =>
|
|
22
|
+
zodSchema(z.union([z.literal('api-key'), z.literal('oidc')])),
|
|
23
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type GatewayEmbeddingModelId =
|
|
2
|
+
| 'alibaba/qwen3-embedding-0.6b'
|
|
3
|
+
| 'alibaba/qwen3-embedding-4b'
|
|
4
|
+
| 'alibaba/qwen3-embedding-8b'
|
|
5
|
+
| 'amazon/titan-embed-text-v2'
|
|
6
|
+
| 'cohere/embed-v4.0'
|
|
7
|
+
| 'google/gemini-embedding-001'
|
|
8
|
+
| 'google/text-embedding-005'
|
|
9
|
+
| 'google/text-multilingual-embedding-002'
|
|
10
|
+
| 'mistral/codestral-embed'
|
|
11
|
+
| 'mistral/mistral-embed'
|
|
12
|
+
| 'openai/text-embedding-3-large'
|
|
13
|
+
| 'openai/text-embedding-3-small'
|
|
14
|
+
| 'openai/text-embedding-ada-002'
|
|
15
|
+
| 'voyage/voyage-3-large'
|
|
16
|
+
| 'voyage/voyage-3.5'
|
|
17
|
+
| 'voyage/voyage-3.5-lite'
|
|
18
|
+
| 'voyage/voyage-code-2'
|
|
19
|
+
| 'voyage/voyage-code-3'
|
|
20
|
+
| 'voyage/voyage-finance-2'
|
|
21
|
+
| 'voyage/voyage-law-2'
|
|
22
|
+
| (string & {});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EmbeddingModelV3,
|
|
3
|
+
SharedV3ProviderMetadata,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
import {
|
|
6
|
+
combineHeaders,
|
|
7
|
+
createJsonErrorResponseHandler,
|
|
8
|
+
createJsonResponseHandler,
|
|
9
|
+
lazySchema,
|
|
10
|
+
postJsonToApi,
|
|
11
|
+
resolve,
|
|
12
|
+
zodSchema,
|
|
13
|
+
type Resolvable,
|
|
14
|
+
} from '@ai-sdk/provider-utils';
|
|
15
|
+
import { z } from 'zod/v4';
|
|
16
|
+
import { asGatewayError } from './errors';
|
|
17
|
+
import { parseAuthMethod } from './errors/parse-auth-method';
|
|
18
|
+
import type { GatewayConfig } from './gateway-config';
|
|
19
|
+
|
|
20
|
+
export class GatewayEmbeddingModel implements EmbeddingModelV3 {
|
|
21
|
+
readonly specificationVersion = 'v3';
|
|
22
|
+
readonly maxEmbeddingsPerCall = 2048;
|
|
23
|
+
readonly supportsParallelCalls = true;
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
readonly modelId: string,
|
|
27
|
+
private readonly config: GatewayConfig & {
|
|
28
|
+
provider: string;
|
|
29
|
+
o11yHeaders: Resolvable<Record<string, string>>;
|
|
30
|
+
},
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
get provider(): string {
|
|
34
|
+
return this.config.provider;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async doEmbed({
|
|
38
|
+
values,
|
|
39
|
+
headers,
|
|
40
|
+
abortSignal,
|
|
41
|
+
providerOptions,
|
|
42
|
+
}: Parameters<EmbeddingModelV3['doEmbed']>[0]): Promise<
|
|
43
|
+
Awaited<ReturnType<EmbeddingModelV3['doEmbed']>>
|
|
44
|
+
> {
|
|
45
|
+
const resolvedHeaders = await resolve(this.config.headers());
|
|
46
|
+
try {
|
|
47
|
+
const {
|
|
48
|
+
responseHeaders,
|
|
49
|
+
value: responseBody,
|
|
50
|
+
rawValue,
|
|
51
|
+
} = await postJsonToApi({
|
|
52
|
+
url: this.getUrl(),
|
|
53
|
+
headers: combineHeaders(
|
|
54
|
+
resolvedHeaders,
|
|
55
|
+
headers ?? {},
|
|
56
|
+
this.getModelConfigHeaders(),
|
|
57
|
+
await resolve(this.config.o11yHeaders),
|
|
58
|
+
),
|
|
59
|
+
body: {
|
|
60
|
+
values,
|
|
61
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
62
|
+
},
|
|
63
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
64
|
+
gatewayEmbeddingResponseSchema,
|
|
65
|
+
),
|
|
66
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
67
|
+
errorSchema: z.any(),
|
|
68
|
+
errorToMessage: data => data,
|
|
69
|
+
}),
|
|
70
|
+
...(abortSignal && { abortSignal }),
|
|
71
|
+
fetch: this.config.fetch,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
embeddings: responseBody.embeddings,
|
|
76
|
+
usage: responseBody.usage ?? undefined,
|
|
77
|
+
providerMetadata:
|
|
78
|
+
responseBody.providerMetadata as unknown as SharedV3ProviderMetadata,
|
|
79
|
+
response: { headers: responseHeaders, body: rawValue },
|
|
80
|
+
warnings: [],
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw await asGatewayError(error, await parseAuthMethod(resolvedHeaders));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private getUrl() {
|
|
88
|
+
return `${this.config.baseURL}/embedding-model`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private getModelConfigHeaders() {
|
|
92
|
+
return {
|
|
93
|
+
'ai-embedding-model-specification-version': '3',
|
|
94
|
+
'ai-model-id': this.modelId,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const gatewayEmbeddingResponseSchema = lazySchema(() =>
|
|
100
|
+
zodSchema(
|
|
101
|
+
z.object({
|
|
102
|
+
embeddings: z.array(z.array(z.number())),
|
|
103
|
+
usage: z.object({ tokens: z.number() }).nullish(),
|
|
104
|
+
providerMetadata: z
|
|
105
|
+
.record(z.string(), z.record(z.string(), z.unknown()))
|
|
106
|
+
.optional(),
|
|
107
|
+
}),
|
|
108
|
+
),
|
|
109
|
+
);
|