@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,212 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV3,
|
|
3
|
+
LanguageModelV3CallOptions,
|
|
4
|
+
SharedV3Warning,
|
|
5
|
+
LanguageModelV3FilePart,
|
|
6
|
+
LanguageModelV3StreamPart,
|
|
7
|
+
LanguageModelV3GenerateResult,
|
|
8
|
+
LanguageModelV3StreamResult,
|
|
9
|
+
} from '@ai-sdk/provider';
|
|
10
|
+
import {
|
|
11
|
+
combineHeaders,
|
|
12
|
+
createEventSourceResponseHandler,
|
|
13
|
+
createJsonErrorResponseHandler,
|
|
14
|
+
createJsonResponseHandler,
|
|
15
|
+
postJsonToApi,
|
|
16
|
+
resolve,
|
|
17
|
+
type ParseResult,
|
|
18
|
+
type Resolvable,
|
|
19
|
+
} from '@ai-sdk/provider-utils';
|
|
20
|
+
import { z } from 'zod/v4';
|
|
21
|
+
import type { GatewayConfig } from './gateway-config';
|
|
22
|
+
import type { GatewayModelId } from './gateway-language-model-settings';
|
|
23
|
+
import { asGatewayError } from './errors';
|
|
24
|
+
import { parseAuthMethod } from './errors/parse-auth-method';
|
|
25
|
+
|
|
26
|
+
type GatewayChatConfig = GatewayConfig & {
|
|
27
|
+
provider: string;
|
|
28
|
+
o11yHeaders: Resolvable<Record<string, string>>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export class GatewayLanguageModel implements LanguageModelV3 {
|
|
32
|
+
readonly specificationVersion = 'v3';
|
|
33
|
+
readonly supportedUrls = { '*/*': [/.*/] };
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
readonly modelId: GatewayModelId,
|
|
37
|
+
private readonly config: GatewayChatConfig,
|
|
38
|
+
) {}
|
|
39
|
+
|
|
40
|
+
get provider(): string {
|
|
41
|
+
return this.config.provider;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private async getArgs(options: LanguageModelV3CallOptions) {
|
|
45
|
+
const { abortSignal: _abortSignal, ...optionsWithoutSignal } = options;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
args: this.maybeEncodeFileParts(optionsWithoutSignal),
|
|
49
|
+
warnings: [],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async doGenerate(
|
|
54
|
+
options: LanguageModelV3CallOptions,
|
|
55
|
+
): Promise<LanguageModelV3GenerateResult> {
|
|
56
|
+
const { args, warnings } = await this.getArgs(options);
|
|
57
|
+
const { abortSignal } = options;
|
|
58
|
+
|
|
59
|
+
const resolvedHeaders = await resolve(this.config.headers());
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const {
|
|
63
|
+
responseHeaders,
|
|
64
|
+
value: responseBody,
|
|
65
|
+
rawValue: rawResponse,
|
|
66
|
+
} = await postJsonToApi({
|
|
67
|
+
url: this.getUrl(),
|
|
68
|
+
headers: combineHeaders(
|
|
69
|
+
resolvedHeaders,
|
|
70
|
+
options.headers,
|
|
71
|
+
this.getModelConfigHeaders(this.modelId, false),
|
|
72
|
+
await resolve(this.config.o11yHeaders),
|
|
73
|
+
),
|
|
74
|
+
body: args,
|
|
75
|
+
successfulResponseHandler: createJsonResponseHandler(z.any()),
|
|
76
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
77
|
+
errorSchema: z.any(),
|
|
78
|
+
errorToMessage: data => data,
|
|
79
|
+
}),
|
|
80
|
+
...(abortSignal && { abortSignal }),
|
|
81
|
+
fetch: this.config.fetch,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
...responseBody,
|
|
86
|
+
request: { body: args },
|
|
87
|
+
response: { headers: responseHeaders, body: rawResponse },
|
|
88
|
+
warnings,
|
|
89
|
+
};
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw await asGatewayError(error, await parseAuthMethod(resolvedHeaders));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async doStream(
|
|
96
|
+
options: LanguageModelV3CallOptions,
|
|
97
|
+
): Promise<LanguageModelV3StreamResult> {
|
|
98
|
+
const { args, warnings } = await this.getArgs(options);
|
|
99
|
+
const { abortSignal } = options;
|
|
100
|
+
|
|
101
|
+
const resolvedHeaders = await resolve(this.config.headers());
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const { value: response, responseHeaders } = await postJsonToApi({
|
|
105
|
+
url: this.getUrl(),
|
|
106
|
+
headers: combineHeaders(
|
|
107
|
+
resolvedHeaders,
|
|
108
|
+
options.headers,
|
|
109
|
+
this.getModelConfigHeaders(this.modelId, true),
|
|
110
|
+
await resolve(this.config.o11yHeaders),
|
|
111
|
+
),
|
|
112
|
+
body: args,
|
|
113
|
+
successfulResponseHandler: createEventSourceResponseHandler(z.any()),
|
|
114
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
115
|
+
errorSchema: z.any(),
|
|
116
|
+
errorToMessage: data => data,
|
|
117
|
+
}),
|
|
118
|
+
...(abortSignal && { abortSignal }),
|
|
119
|
+
fetch: this.config.fetch,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
stream: response.pipeThrough(
|
|
124
|
+
new TransformStream<
|
|
125
|
+
ParseResult<LanguageModelV3StreamPart>,
|
|
126
|
+
LanguageModelV3StreamPart
|
|
127
|
+
>({
|
|
128
|
+
start(controller) {
|
|
129
|
+
if (warnings.length > 0) {
|
|
130
|
+
controller.enqueue({ type: 'stream-start', warnings });
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
transform(chunk, controller) {
|
|
134
|
+
if (chunk.success) {
|
|
135
|
+
const streamPart = chunk.value;
|
|
136
|
+
|
|
137
|
+
// Handle raw chunks: if this is a raw chunk from the gateway API,
|
|
138
|
+
// only emit it if includeRawChunks is true
|
|
139
|
+
if (streamPart.type === 'raw' && !options.includeRawChunks) {
|
|
140
|
+
return; // Skip raw chunks if not requested
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
streamPart.type === 'response-metadata' &&
|
|
145
|
+
streamPart.timestamp &&
|
|
146
|
+
typeof streamPart.timestamp === 'string'
|
|
147
|
+
) {
|
|
148
|
+
streamPart.timestamp = new Date(streamPart.timestamp);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
controller.enqueue(streamPart);
|
|
152
|
+
} else {
|
|
153
|
+
controller.error(
|
|
154
|
+
(chunk as { success: false; error: unknown }).error,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
}),
|
|
159
|
+
),
|
|
160
|
+
request: { body: args },
|
|
161
|
+
response: { headers: responseHeaders },
|
|
162
|
+
};
|
|
163
|
+
} catch (error) {
|
|
164
|
+
throw await asGatewayError(error, await parseAuthMethod(resolvedHeaders));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private isFilePart(part: unknown) {
|
|
169
|
+
return (
|
|
170
|
+
part && typeof part === 'object' && 'type' in part && part.type === 'file'
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Encodes file parts in the prompt to base64. Mutates the passed options
|
|
176
|
+
* instance directly to avoid copying the file data.
|
|
177
|
+
* @param options - The options to encode.
|
|
178
|
+
* @returns The options with the file parts encoded.
|
|
179
|
+
*/
|
|
180
|
+
private maybeEncodeFileParts(options: LanguageModelV3CallOptions) {
|
|
181
|
+
for (const message of options.prompt) {
|
|
182
|
+
for (const part of message.content) {
|
|
183
|
+
if (this.isFilePart(part)) {
|
|
184
|
+
const filePart = part as LanguageModelV3FilePart;
|
|
185
|
+
// If the file part is a URL it will get cleanly converted to a string.
|
|
186
|
+
// If it's a binary file attachment we convert it to a data url.
|
|
187
|
+
// In either case, server-side we should only ever see URLs as strings.
|
|
188
|
+
if (filePart.data instanceof Uint8Array) {
|
|
189
|
+
const buffer = Uint8Array.from(filePart.data);
|
|
190
|
+
const base64Data = Buffer.from(buffer).toString('base64');
|
|
191
|
+
filePart.data = new URL(
|
|
192
|
+
`data:${filePart.mediaType || 'application/octet-stream'};base64,${base64Data}`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return options;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private getUrl() {
|
|
202
|
+
return `${this.config.baseURL}/language-model`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private getModelConfigHeaders(modelId: string, streaming: boolean) {
|
|
206
|
+
return {
|
|
207
|
+
'ai-language-model-specification-version': '3',
|
|
208
|
+
'ai-language-model-id': modelId,
|
|
209
|
+
'ai-language-model-streaming': String(streaming),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { LanguageModelV3 } from '@ai-sdk/provider';
|
|
2
|
+
|
|
3
|
+
export interface GatewayLanguageModelEntry {
|
|
4
|
+
/**
|
|
5
|
+
* The model id used by the remote provider in model settings and for specifying the
|
|
6
|
+
* intended model for text generation.
|
|
7
|
+
*/
|
|
8
|
+
id: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The display name of the model for presentation in user-facing contexts.
|
|
12
|
+
*/
|
|
13
|
+
name: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Optional description of the model.
|
|
17
|
+
*/
|
|
18
|
+
description?: string | null;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Optional pricing information for the model.
|
|
22
|
+
*/
|
|
23
|
+
pricing?: {
|
|
24
|
+
/**
|
|
25
|
+
* Cost per input token in USD.
|
|
26
|
+
*/
|
|
27
|
+
input: string;
|
|
28
|
+
/**
|
|
29
|
+
* Cost per output token in USD.
|
|
30
|
+
*/
|
|
31
|
+
output: string;
|
|
32
|
+
/**
|
|
33
|
+
* Cost per cached input token in USD.
|
|
34
|
+
* Only present for providers/models that support prompt caching.
|
|
35
|
+
*/
|
|
36
|
+
cachedInputTokens?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Cost per input token to create/write cache entries in USD.
|
|
39
|
+
* Only present for providers/models that support prompt caching.
|
|
40
|
+
*/
|
|
41
|
+
cacheCreationInputTokens?: string;
|
|
42
|
+
} | null;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Additional AI SDK language model specifications for the model.
|
|
46
|
+
*/
|
|
47
|
+
specification: GatewayLanguageModelSpecification;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Optional field to differentiate between model types.
|
|
51
|
+
*/
|
|
52
|
+
modelType?: 'language' | 'embedding' | 'image' | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type GatewayLanguageModelSpecification = Pick<
|
|
56
|
+
LanguageModelV3,
|
|
57
|
+
'specificationVersion' | 'provider' | 'modelId'
|
|
58
|
+
>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
|
|
4
|
+
// https://vercel.com/docs/ai-gateway/provider-options
|
|
5
|
+
const gatewayProviderOptions = lazySchema(() =>
|
|
6
|
+
zodSchema(
|
|
7
|
+
z.object({
|
|
8
|
+
/**
|
|
9
|
+
* Array of provider slugs that are the only ones allowed to be used.
|
|
10
|
+
*
|
|
11
|
+
* Example: `['azure', 'openai']` will only allow Azure and OpenAI to be used.
|
|
12
|
+
*/
|
|
13
|
+
only: z.array(z.string()).optional(),
|
|
14
|
+
/**
|
|
15
|
+
* Array of provider slugs that specifies the sequence in which providers should be attempted.
|
|
16
|
+
*
|
|
17
|
+
* Example: `['bedrock', 'anthropic']` will try Amazon Bedrock first, then Anthropic as fallback.
|
|
18
|
+
*/
|
|
19
|
+
order: z.array(z.string()).optional(),
|
|
20
|
+
/**
|
|
21
|
+
* The unique identifier for the end user on behalf of whom the request was made.
|
|
22
|
+
*
|
|
23
|
+
* Used for spend tracking and attribution purposes.
|
|
24
|
+
*/
|
|
25
|
+
user: z.string().optional(),
|
|
26
|
+
/**
|
|
27
|
+
* User-specified tags for use in reporting and filtering usage.
|
|
28
|
+
*
|
|
29
|
+
* For example, spend tracking reporting by feature or prompt version.
|
|
30
|
+
*
|
|
31
|
+
* Example: `['chat', 'v2']`
|
|
32
|
+
*/
|
|
33
|
+
tags: z.array(z.string()).optional(),
|
|
34
|
+
/**
|
|
35
|
+
* Array of model slugs specifying fallback models to use in order.
|
|
36
|
+
*
|
|
37
|
+
* Example: `['openai/gpt-5-nano', 'zai/glm-4.6']` will try `openai/gpt-5-nano` first, then `zai/glm-4.6` as fallback.
|
|
38
|
+
*/
|
|
39
|
+
models: z.array(z.string()).optional(),
|
|
40
|
+
/**
|
|
41
|
+
* Request-scoped BYOK credentials to use instead of cached credentials.
|
|
42
|
+
*
|
|
43
|
+
* When provided, cached BYOK credentials are ignored entirely.
|
|
44
|
+
*
|
|
45
|
+
* Each provider can have multiple credentials (tried in order).
|
|
46
|
+
*
|
|
47
|
+
* Examples:
|
|
48
|
+
* - Simple: `{ 'anthropic': [{ apiKey: 'sk-ant-...' }] }`
|
|
49
|
+
* - Multiple: `{ 'vertex': [{ projectId: 'proj-1', privateKey: '...' }, { projectId: 'proj-2', privateKey: '...' }] }`
|
|
50
|
+
* - Multi-provider: `{ 'anthropic': [{ apiKey: '...' }], 'bedrock': [{ accessKeyId: '...', secretAccessKey: '...' }] }`
|
|
51
|
+
*/
|
|
52
|
+
byok: z
|
|
53
|
+
.record(z.string(), z.array(z.record(z.string(), z.unknown())))
|
|
54
|
+
.optional(),
|
|
55
|
+
/**
|
|
56
|
+
* Whether to filter by only providers that state they have zero data
|
|
57
|
+
* retention with Vercel AI Gateway. When enabled, only providers that
|
|
58
|
+
* have agreements with Vercel AI Gateway for zero data retention will be
|
|
59
|
+
* used.
|
|
60
|
+
*/
|
|
61
|
+
zeroDataRetention: z.boolean().optional(),
|
|
62
|
+
}),
|
|
63
|
+
),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
export type GatewayProviderOptions = InferSchema<typeof gatewayProviderOptions>;
|