@ai-sdk/google 4.0.0-beta.21 → 4.0.0-beta.23
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 +15 -0
- package/README.md +2 -0
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +250 -55
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +261 -52
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +40 -5
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +45 -6
- package/dist/internal/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/convert-to-google-generative-ai-messages.ts +58 -17
- package/src/google-generative-ai-files.ts +230 -0
- package/src/google-generative-ai-prompt.ts +10 -2
- package/src/google-provider.ts +13 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/google",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.23",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@ai-sdk/provider": "4.0.0-beta.
|
|
40
|
-
"@ai-sdk/provider-utils": "5.0.0-beta.
|
|
39
|
+
"@ai-sdk/provider": "4.0.0-beta.6",
|
|
40
|
+
"@ai-sdk/provider-utils": "5.0.0-beta.10"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "20.17.24",
|
|
@@ -2,7 +2,11 @@ import {
|
|
|
2
2
|
LanguageModelV4Prompt,
|
|
3
3
|
UnsupportedFunctionalityError,
|
|
4
4
|
} from '@ai-sdk/provider';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
convertToBase64,
|
|
7
|
+
isProviderReference,
|
|
8
|
+
resolveProviderReference,
|
|
9
|
+
} from '@ai-sdk/provider-utils';
|
|
6
10
|
import {
|
|
7
11
|
GoogleGenerativeAIContent,
|
|
8
12
|
GoogleGenerativeAIContentPart,
|
|
@@ -203,25 +207,40 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
203
207
|
}
|
|
204
208
|
|
|
205
209
|
case 'file': {
|
|
206
|
-
// default to image/jpeg for unknown image/* types
|
|
207
210
|
const mediaType =
|
|
208
211
|
part.mediaType === 'image/*' ? 'image/jpeg' : part.mediaType;
|
|
209
212
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
213
|
+
if (part.data instanceof URL) {
|
|
214
|
+
parts.push({
|
|
215
|
+
fileData: {
|
|
216
|
+
mimeType: mediaType,
|
|
217
|
+
fileUri: part.data.toString(),
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
} else if (isProviderReference(part.data)) {
|
|
221
|
+
if (providerOptionsName === 'vertex') {
|
|
222
|
+
throw new UnsupportedFunctionalityError({
|
|
223
|
+
functionality: 'file parts with provider references',
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
parts.push({
|
|
228
|
+
fileData: {
|
|
229
|
+
mimeType: mediaType,
|
|
230
|
+
fileUri: resolveProviderReference({
|
|
231
|
+
reference: part.data,
|
|
232
|
+
provider: 'google',
|
|
233
|
+
}),
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
} else {
|
|
237
|
+
parts.push({
|
|
238
|
+
inlineData: {
|
|
239
|
+
mimeType: mediaType,
|
|
240
|
+
data: convertToBase64(part.data),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
225
244
|
|
|
226
245
|
break;
|
|
227
246
|
}
|
|
@@ -295,6 +314,28 @@ export function convertToGoogleGenerativeAIMessages(
|
|
|
295
314
|
});
|
|
296
315
|
}
|
|
297
316
|
|
|
317
|
+
if (isProviderReference(part.data)) {
|
|
318
|
+
if (providerOptionsName === 'vertex') {
|
|
319
|
+
throw new UnsupportedFunctionalityError({
|
|
320
|
+
functionality: 'file parts with provider references',
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
fileData: {
|
|
326
|
+
mimeType: part.mediaType,
|
|
327
|
+
fileUri: resolveProviderReference({
|
|
328
|
+
reference: part.data,
|
|
329
|
+
provider: 'google',
|
|
330
|
+
}),
|
|
331
|
+
},
|
|
332
|
+
...(providerOpts?.thought === true
|
|
333
|
+
? { thought: true }
|
|
334
|
+
: {}),
|
|
335
|
+
thoughtSignature,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
298
339
|
return {
|
|
299
340
|
inlineData: {
|
|
300
341
|
mimeType: part.mediaType,
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AISDKError,
|
|
3
|
+
type FilesV4,
|
|
4
|
+
type FilesV4UploadFileCallOptions,
|
|
5
|
+
type FilesV4UploadFileResult,
|
|
6
|
+
type SharedV4Warning,
|
|
7
|
+
} from '@ai-sdk/provider';
|
|
8
|
+
import {
|
|
9
|
+
combineHeaders,
|
|
10
|
+
convertUint8ArrayToBase64,
|
|
11
|
+
createJsonResponseHandler,
|
|
12
|
+
delay,
|
|
13
|
+
type FetchFunction,
|
|
14
|
+
lazySchema,
|
|
15
|
+
parseProviderOptions,
|
|
16
|
+
postJsonToApi,
|
|
17
|
+
zodSchema,
|
|
18
|
+
getFromApi,
|
|
19
|
+
} from '@ai-sdk/provider-utils';
|
|
20
|
+
import { z } from 'zod/v4';
|
|
21
|
+
import { googleFailedResponseHandler } from './google-error';
|
|
22
|
+
|
|
23
|
+
export type GoogleFilesUploadOptions = {
|
|
24
|
+
displayName?: string | null;
|
|
25
|
+
pollIntervalMs?: number | null;
|
|
26
|
+
pollTimeoutMs?: number | null;
|
|
27
|
+
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
interface GoogleGenerativeAIFilesConfig {
|
|
32
|
+
provider: string;
|
|
33
|
+
baseURL: string;
|
|
34
|
+
headers: () => Record<string, string | undefined>;
|
|
35
|
+
fetch?: FetchFunction;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class GoogleGenerativeAIFiles implements FilesV4 {
|
|
39
|
+
readonly specificationVersion = 'v4';
|
|
40
|
+
|
|
41
|
+
get provider(): string {
|
|
42
|
+
return this.config.provider;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
constructor(private readonly config: GoogleGenerativeAIFilesConfig) {}
|
|
46
|
+
|
|
47
|
+
async uploadFile(
|
|
48
|
+
options: FilesV4UploadFileCallOptions,
|
|
49
|
+
): Promise<FilesV4UploadFileResult> {
|
|
50
|
+
const googleOptions = (await parseProviderOptions({
|
|
51
|
+
provider: 'google',
|
|
52
|
+
providerOptions: options.providerOptions,
|
|
53
|
+
schema: googleFilesUploadOptionsSchema,
|
|
54
|
+
})) as GoogleFilesUploadOptions | undefined;
|
|
55
|
+
|
|
56
|
+
const resolvedHeaders = this.config.headers();
|
|
57
|
+
const fetchFn = this.config.fetch ?? globalThis.fetch;
|
|
58
|
+
|
|
59
|
+
const warnings: Array<SharedV4Warning> = [];
|
|
60
|
+
if (options.filename != null) {
|
|
61
|
+
warnings.push({ type: 'unsupported', feature: 'filename' });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const data = options.data;
|
|
65
|
+
const fileBytes =
|
|
66
|
+
data instanceof Uint8Array
|
|
67
|
+
? data
|
|
68
|
+
: Uint8Array.from(atob(data), c => c.charCodeAt(0));
|
|
69
|
+
|
|
70
|
+
const mediaType = options.mediaType;
|
|
71
|
+
const displayName = googleOptions?.displayName;
|
|
72
|
+
|
|
73
|
+
const baseOrigin = this.config.baseURL.replace(/\/v1beta$/, '');
|
|
74
|
+
|
|
75
|
+
const initResponse = await fetchFn(`${baseOrigin}/upload/v1beta/files`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: {
|
|
78
|
+
...resolvedHeaders,
|
|
79
|
+
'X-Goog-Upload-Protocol': 'resumable',
|
|
80
|
+
'X-Goog-Upload-Command': 'start',
|
|
81
|
+
'X-Goog-Upload-Header-Content-Length': String(fileBytes.length),
|
|
82
|
+
'X-Goog-Upload-Header-Content-Type': mediaType,
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
},
|
|
85
|
+
body: JSON.stringify({
|
|
86
|
+
file: {
|
|
87
|
+
...(displayName != null ? { display_name: displayName } : {}),
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (!initResponse.ok) {
|
|
93
|
+
const errorBody = await initResponse.text();
|
|
94
|
+
throw new AISDKError({
|
|
95
|
+
name: 'GOOGLE_FILES_UPLOAD_ERROR',
|
|
96
|
+
message: `Failed to initiate resumable upload: ${initResponse.status} ${errorBody}`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const uploadUrl = initResponse.headers.get('x-goog-upload-url');
|
|
101
|
+
if (!uploadUrl) {
|
|
102
|
+
throw new AISDKError({
|
|
103
|
+
name: 'GOOGLE_FILES_UPLOAD_ERROR',
|
|
104
|
+
message: 'No upload URL returned from initiation request',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const uploadResponse = await fetchFn(uploadUrl, {
|
|
109
|
+
method: 'POST',
|
|
110
|
+
headers: {
|
|
111
|
+
'Content-Length': String(fileBytes.length),
|
|
112
|
+
'X-Goog-Upload-Offset': '0',
|
|
113
|
+
'X-Goog-Upload-Command': 'upload, finalize',
|
|
114
|
+
},
|
|
115
|
+
body: fileBytes,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (!uploadResponse.ok) {
|
|
119
|
+
const errorBody = await uploadResponse.text();
|
|
120
|
+
throw new AISDKError({
|
|
121
|
+
name: 'GOOGLE_FILES_UPLOAD_ERROR',
|
|
122
|
+
message: `Failed to upload file data: ${uploadResponse.status} ${errorBody}`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const uploadResult = (await uploadResponse.json()) as {
|
|
127
|
+
file: GoogleFileResource;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
let file = uploadResult.file;
|
|
131
|
+
|
|
132
|
+
const pollIntervalMs = googleOptions?.pollIntervalMs ?? 2000;
|
|
133
|
+
const pollTimeoutMs = googleOptions?.pollTimeoutMs ?? 300000;
|
|
134
|
+
const startTime = Date.now();
|
|
135
|
+
|
|
136
|
+
while (file.state === 'PROCESSING') {
|
|
137
|
+
if (Date.now() - startTime > pollTimeoutMs) {
|
|
138
|
+
throw new AISDKError({
|
|
139
|
+
name: 'GOOGLE_FILES_UPLOAD_TIMEOUT',
|
|
140
|
+
message: `File processing timed out after ${pollTimeoutMs}ms`,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
await delay(pollIntervalMs);
|
|
145
|
+
|
|
146
|
+
const { value: fileStatus } = await getFromApi({
|
|
147
|
+
url: `${this.config.baseURL}/${file.name}`,
|
|
148
|
+
headers: combineHeaders(resolvedHeaders),
|
|
149
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
150
|
+
googleFileResponseSchema,
|
|
151
|
+
),
|
|
152
|
+
failedResponseHandler: googleFailedResponseHandler,
|
|
153
|
+
fetch: this.config.fetch,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
file = fileStatus;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (file.state === 'FAILED') {
|
|
160
|
+
throw new AISDKError({
|
|
161
|
+
name: 'GOOGLE_FILES_UPLOAD_FAILED',
|
|
162
|
+
message: `File processing failed for ${file.name}`,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
warnings,
|
|
168
|
+
providerReference: { google: file.uri },
|
|
169
|
+
mediaType: file.mimeType ?? options.mediaType,
|
|
170
|
+
providerMetadata: {
|
|
171
|
+
google: {
|
|
172
|
+
name: file.name,
|
|
173
|
+
displayName: file.displayName,
|
|
174
|
+
mimeType: file.mimeType,
|
|
175
|
+
sizeBytes: file.sizeBytes,
|
|
176
|
+
state: file.state,
|
|
177
|
+
uri: file.uri,
|
|
178
|
+
...(file.createTime != null ? { createTime: file.createTime } : {}),
|
|
179
|
+
...(file.updateTime != null ? { updateTime: file.updateTime } : {}),
|
|
180
|
+
...(file.expirationTime != null
|
|
181
|
+
? { expirationTime: file.expirationTime }
|
|
182
|
+
: {}),
|
|
183
|
+
...(file.sha256Hash != null ? { sha256Hash: file.sha256Hash } : {}),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
type GoogleFileResource = {
|
|
191
|
+
name: string;
|
|
192
|
+
displayName?: string | null;
|
|
193
|
+
mimeType: string;
|
|
194
|
+
sizeBytes?: string | null;
|
|
195
|
+
createTime?: string | null;
|
|
196
|
+
updateTime?: string | null;
|
|
197
|
+
expirationTime?: string | null;
|
|
198
|
+
sha256Hash?: string | null;
|
|
199
|
+
uri: string;
|
|
200
|
+
state: string;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const googleFileResponseSchema = lazySchema(() =>
|
|
204
|
+
zodSchema(
|
|
205
|
+
z.object({
|
|
206
|
+
name: z.string(),
|
|
207
|
+
displayName: z.string().nullish(),
|
|
208
|
+
mimeType: z.string(),
|
|
209
|
+
sizeBytes: z.string().nullish(),
|
|
210
|
+
createTime: z.string().nullish(),
|
|
211
|
+
updateTime: z.string().nullish(),
|
|
212
|
+
expirationTime: z.string().nullish(),
|
|
213
|
+
sha256Hash: z.string().nullish(),
|
|
214
|
+
uri: z.string(),
|
|
215
|
+
state: z.string(),
|
|
216
|
+
}),
|
|
217
|
+
),
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const googleFilesUploadOptionsSchema = lazySchema(() =>
|
|
221
|
+
zodSchema(
|
|
222
|
+
z
|
|
223
|
+
.object({
|
|
224
|
+
displayName: z.string().nullish(),
|
|
225
|
+
pollIntervalMs: z.number().positive().nullish(),
|
|
226
|
+
pollTimeoutMs: z.number().positive().nullish(),
|
|
227
|
+
})
|
|
228
|
+
.passthrough(),
|
|
229
|
+
),
|
|
230
|
+
);
|
|
@@ -22,7 +22,11 @@ export type GoogleGenerativeAIContent = {
|
|
|
22
22
|
|
|
23
23
|
export type GoogleGenerativeAIContentPart =
|
|
24
24
|
| { text: string; thought?: boolean; thoughtSignature?: string }
|
|
25
|
-
| {
|
|
25
|
+
| {
|
|
26
|
+
inlineData: { mimeType: string; data: string };
|
|
27
|
+
thought?: boolean;
|
|
28
|
+
thoughtSignature?: string;
|
|
29
|
+
}
|
|
26
30
|
| { functionCall: { name: string; args: unknown }; thoughtSignature?: string }
|
|
27
31
|
| {
|
|
28
32
|
functionResponse: {
|
|
@@ -31,7 +35,11 @@ export type GoogleGenerativeAIContentPart =
|
|
|
31
35
|
parts?: Array<GoogleGenerativeAIFunctionResponsePart>;
|
|
32
36
|
};
|
|
33
37
|
}
|
|
34
|
-
| {
|
|
38
|
+
| {
|
|
39
|
+
fileData: { mimeType: string; fileUri: string };
|
|
40
|
+
thought?: boolean;
|
|
41
|
+
thoughtSignature?: string;
|
|
42
|
+
}
|
|
35
43
|
| {
|
|
36
44
|
toolCall: {
|
|
37
45
|
toolType: string;
|
package/src/google-provider.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EmbeddingModelV4,
|
|
3
3
|
Experimental_VideoModelV4,
|
|
4
|
+
FilesV4,
|
|
4
5
|
ImageModelV4,
|
|
5
6
|
LanguageModelV4,
|
|
6
7
|
ProviderV4,
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
GoogleGenerativeAIImageModelId,
|
|
25
26
|
} from './google-generative-ai-image-settings';
|
|
26
27
|
import { GoogleGenerativeAIImageModel } from './google-generative-ai-image-model';
|
|
28
|
+
import { GoogleGenerativeAIFiles } from './google-generative-ai-files';
|
|
27
29
|
import { GoogleGenerativeAIVideoModel } from './google-generative-ai-video-model';
|
|
28
30
|
import { GoogleGenerativeAIVideoModelId } from './google-generative-ai-video-settings';
|
|
29
31
|
|
|
@@ -81,6 +83,8 @@ export interface GoogleGenerativeAIProvider extends ProviderV4 {
|
|
|
81
83
|
modelId: GoogleGenerativeAIVideoModelId,
|
|
82
84
|
): Experimental_VideoModelV4;
|
|
83
85
|
|
|
86
|
+
files(): FilesV4;
|
|
87
|
+
|
|
84
88
|
tools: typeof googleTools;
|
|
85
89
|
}
|
|
86
90
|
|
|
@@ -185,6 +189,14 @@ export function createGoogleGenerativeAI(
|
|
|
185
189
|
fetch: options.fetch,
|
|
186
190
|
});
|
|
187
191
|
|
|
192
|
+
const createFiles = () =>
|
|
193
|
+
new GoogleGenerativeAIFiles({
|
|
194
|
+
provider: providerName,
|
|
195
|
+
baseURL,
|
|
196
|
+
headers: getHeaders,
|
|
197
|
+
fetch: options.fetch,
|
|
198
|
+
});
|
|
199
|
+
|
|
188
200
|
const createVideoModel = (modelId: GoogleGenerativeAIVideoModelId) =>
|
|
189
201
|
new GoogleGenerativeAIVideoModel(modelId, {
|
|
190
202
|
provider: providerName,
|
|
@@ -216,6 +228,7 @@ export function createGoogleGenerativeAI(
|
|
|
216
228
|
provider.imageModel = createImageModel;
|
|
217
229
|
provider.video = createVideoModel;
|
|
218
230
|
provider.videoModel = createVideoModel;
|
|
231
|
+
provider.files = createFiles;
|
|
219
232
|
provider.tools = googleTools;
|
|
220
233
|
|
|
221
234
|
return provider as GoogleGenerativeAIProvider;
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export type {
|
|
|
21
21
|
GoogleVideoModelOptions as GoogleGenerativeAIVideoProviderOptions,
|
|
22
22
|
} from './google-generative-ai-video-model';
|
|
23
23
|
export type { GoogleGenerativeAIVideoModelId } from './google-generative-ai-video-settings';
|
|
24
|
+
export type { GoogleFilesUploadOptions } from './google-generative-ai-files';
|
|
24
25
|
export { createGoogleGenerativeAI, google } from './google-provider';
|
|
25
26
|
export type {
|
|
26
27
|
GoogleGenerativeAIProvider,
|