@ai-sdk/google 3.0.12 → 3.0.13

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.
@@ -1,411 +0,0 @@
1
- import { createTestServer } from '@ai-sdk/test-server/with-vitest';
2
- import { GoogleGenerativeAIImageModel } from './google-generative-ai-image-model';
3
- import { describe, it, expect } from 'vitest';
4
-
5
- const prompt = 'A cute baby sea otter';
6
-
7
- const model = new GoogleGenerativeAIImageModel(
8
- 'imagen-3.0-generate-002',
9
- {},
10
- {
11
- provider: 'google.generative-ai',
12
- baseURL: 'https://api.example.com/v1beta',
13
- headers: () => ({ 'api-key': 'test-api-key' }),
14
- },
15
- );
16
-
17
- const server = createTestServer({
18
- 'https://api.example.com/v1beta/models/imagen-3.0-generate-002:predict': {
19
- response: {
20
- type: 'json-value',
21
- body: {
22
- predictions: [
23
- { bytesBase64Encoded: 'base64-image-1' },
24
- { bytesBase64Encoded: 'base64-image-2' },
25
- ],
26
- },
27
- },
28
- },
29
- });
30
- describe('GoogleGenerativeAIImageModel', () => {
31
- describe('doGenerate', () => {
32
- function prepareJsonResponse({
33
- headers,
34
- }: {
35
- headers?: Record<string, string>;
36
- } = {}) {
37
- const url =
38
- 'https://api.example.com/v1beta/models/imagen-3.0-generate-002:predict';
39
- server.urls[url].response = {
40
- type: 'json-value',
41
- headers,
42
- body: {
43
- predictions: [
44
- { bytesBase64Encoded: 'base64-image-1' },
45
- { bytesBase64Encoded: 'base64-image-2' },
46
- ],
47
- },
48
- };
49
- }
50
-
51
- it('should pass headers', async () => {
52
- prepareJsonResponse();
53
-
54
- const modelWithHeaders = new GoogleGenerativeAIImageModel(
55
- 'imagen-3.0-generate-002',
56
- {},
57
- {
58
- provider: 'google.generative-ai',
59
- baseURL: 'https://api.example.com/v1beta',
60
- headers: () => ({
61
- 'Custom-Provider-Header': 'provider-header-value',
62
- }),
63
- },
64
- );
65
-
66
- await modelWithHeaders.doGenerate({
67
- prompt,
68
- files: undefined,
69
- mask: undefined,
70
- n: 2,
71
- size: undefined,
72
- aspectRatio: undefined,
73
- seed: undefined,
74
- providerOptions: {},
75
- headers: {
76
- 'Custom-Request-Header': 'request-header-value',
77
- },
78
- });
79
-
80
- expect(server.calls[0].requestHeaders).toStrictEqual({
81
- 'content-type': 'application/json',
82
- 'custom-provider-header': 'provider-header-value',
83
- 'custom-request-header': 'request-header-value',
84
- });
85
- });
86
-
87
- it('should respect maxImagesPerCall setting', () => {
88
- const customModel = new GoogleGenerativeAIImageModel(
89
- 'imagen-3.0-generate-002',
90
- { maxImagesPerCall: 2 },
91
- {
92
- provider: 'google.generative-ai',
93
- baseURL: 'https://api.example.com/v1beta',
94
- headers: () => ({ 'api-key': 'test-api-key' }),
95
- },
96
- );
97
-
98
- expect(customModel.maxImagesPerCall).toBe(2);
99
- });
100
-
101
- it('should use default maxImagesPerCall when not specified', () => {
102
- const defaultModel = new GoogleGenerativeAIImageModel(
103
- 'imagen-3.0-generate-002',
104
- {},
105
- {
106
- provider: 'google.generative-ai',
107
- baseURL: 'https://api.example.com/v1beta',
108
- headers: () => ({ 'api-key': 'test-api-key' }),
109
- },
110
- );
111
-
112
- expect(defaultModel.maxImagesPerCall).toBe(4);
113
- });
114
-
115
- it('should extract the generated images', async () => {
116
- prepareJsonResponse();
117
-
118
- const result = await model.doGenerate({
119
- prompt,
120
- files: undefined,
121
- mask: undefined,
122
- n: 2,
123
- size: undefined,
124
- aspectRatio: undefined,
125
- seed: undefined,
126
- providerOptions: {},
127
- });
128
-
129
- expect(result.images).toStrictEqual(['base64-image-1', 'base64-image-2']);
130
- });
131
-
132
- it('sends aspect ratio in the request', async () => {
133
- prepareJsonResponse();
134
-
135
- await model.doGenerate({
136
- prompt: 'test prompt',
137
- files: undefined,
138
- mask: undefined,
139
- n: 1,
140
- size: undefined,
141
- aspectRatio: '16:9',
142
- seed: undefined,
143
- providerOptions: {},
144
- });
145
-
146
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
147
- {
148
- "instances": [
149
- {
150
- "prompt": "test prompt",
151
- },
152
- ],
153
- "parameters": {
154
- "aspectRatio": "16:9",
155
- "sampleCount": 1,
156
- },
157
- }
158
- `);
159
- });
160
-
161
- it('should pass aspect ratio directly when specified', async () => {
162
- prepareJsonResponse();
163
-
164
- await model.doGenerate({
165
- prompt: 'test prompt',
166
- files: undefined,
167
- mask: undefined,
168
- n: 1,
169
- size: undefined,
170
- aspectRatio: '16:9',
171
- seed: undefined,
172
- providerOptions: {},
173
- });
174
-
175
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
176
- {
177
- "instances": [
178
- {
179
- "prompt": "test prompt",
180
- },
181
- ],
182
- "parameters": {
183
- "aspectRatio": "16:9",
184
- "sampleCount": 1,
185
- },
186
- }
187
- `);
188
- });
189
-
190
- it('should combine aspectRatio and provider options', async () => {
191
- prepareJsonResponse();
192
-
193
- await model.doGenerate({
194
- prompt: 'test prompt',
195
- files: undefined,
196
- mask: undefined,
197
- n: 1,
198
- size: undefined,
199
- aspectRatio: '1:1',
200
- seed: undefined,
201
- providerOptions: {
202
- google: {
203
- personGeneration: 'dont_allow',
204
- },
205
- },
206
- });
207
-
208
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
209
- {
210
- "instances": [
211
- {
212
- "prompt": "test prompt",
213
- },
214
- ],
215
- "parameters": {
216
- "aspectRatio": "1:1",
217
- "personGeneration": "dont_allow",
218
- "sampleCount": 1,
219
- },
220
- }
221
- `);
222
- });
223
-
224
- it('should return warnings for unsupported settings', async () => {
225
- prepareJsonResponse();
226
-
227
- const result = await model.doGenerate({
228
- prompt,
229
- files: undefined,
230
- mask: undefined,
231
- n: 1,
232
- size: '1024x1024',
233
- aspectRatio: '1:1',
234
- seed: 123,
235
- providerOptions: {},
236
- });
237
-
238
- expect(result.warnings).toMatchInlineSnapshot(`
239
- [
240
- {
241
- "details": "This model does not support the \`size\` option. Use \`aspectRatio\` instead.",
242
- "feature": "size",
243
- "type": "unsupported",
244
- },
245
- {
246
- "details": "This model does not support the \`seed\` option through this provider.",
247
- "feature": "seed",
248
- "type": "unsupported",
249
- },
250
- ]
251
- `);
252
- });
253
-
254
- it('should include response data with timestamp, modelId and headers', async () => {
255
- prepareJsonResponse({
256
- headers: {
257
- 'request-id': 'test-request-id',
258
- 'x-goog-quota-remaining': '123',
259
- },
260
- });
261
-
262
- const testDate = new Date('2024-03-15T12:00:00Z');
263
-
264
- const customModel = new GoogleGenerativeAIImageModel(
265
- 'imagen-3.0-generate-002',
266
- {},
267
- {
268
- provider: 'google.generative-ai',
269
- baseURL: 'https://api.example.com/v1beta',
270
- headers: () => ({ 'api-key': 'test-api-key' }),
271
- _internal: {
272
- currentDate: () => testDate,
273
- },
274
- },
275
- );
276
-
277
- const result = await customModel.doGenerate({
278
- prompt,
279
- files: undefined,
280
- mask: undefined,
281
- n: 1,
282
- size: undefined,
283
- aspectRatio: undefined,
284
- seed: undefined,
285
- providerOptions: {},
286
- });
287
-
288
- expect(result.response).toStrictEqual({
289
- timestamp: testDate,
290
- modelId: 'imagen-3.0-generate-002',
291
- headers: {
292
- 'content-length': '97',
293
- 'content-type': 'application/json',
294
- 'request-id': 'test-request-id',
295
- 'x-goog-quota-remaining': '123',
296
- },
297
- });
298
- });
299
-
300
- it('should use real date when no custom date provider is specified', async () => {
301
- prepareJsonResponse();
302
- const beforeDate = new Date();
303
-
304
- const result = await model.doGenerate({
305
- prompt,
306
- files: undefined,
307
- mask: undefined,
308
- n: 2,
309
- size: undefined,
310
- aspectRatio: undefined,
311
- seed: undefined,
312
- providerOptions: {},
313
- });
314
-
315
- const afterDate = new Date();
316
-
317
- expect(result.response.timestamp.getTime()).toBeGreaterThanOrEqual(
318
- beforeDate.getTime(),
319
- );
320
- expect(result.response.timestamp.getTime()).toBeLessThanOrEqual(
321
- afterDate.getTime(),
322
- );
323
- expect(result.response.modelId).toBe('imagen-3.0-generate-002');
324
- });
325
-
326
- it('should only pass valid provider options', async () => {
327
- prepareJsonResponse();
328
-
329
- await model.doGenerate({
330
- prompt,
331
- files: undefined,
332
- mask: undefined,
333
- n: 2,
334
- size: undefined,
335
- aspectRatio: '16:9',
336
- seed: undefined,
337
- providerOptions: {
338
- google: {
339
- addWatermark: false,
340
- personGeneration: 'allow_all',
341
- foo: 'bar',
342
- negativePrompt: 'negative prompt',
343
- },
344
- },
345
- });
346
-
347
- expect(await server.calls[0].requestBodyJson).toMatchInlineSnapshot(`
348
- {
349
- "instances": [
350
- {
351
- "prompt": "A cute baby sea otter",
352
- },
353
- ],
354
- "parameters": {
355
- "aspectRatio": "16:9",
356
- "personGeneration": "allow_all",
357
- "sampleCount": 2,
358
- },
359
- }
360
- `);
361
- });
362
- });
363
-
364
- describe('Image Editing (Not Supported)', () => {
365
- it('should throw error when files are provided', async () => {
366
- await expect(
367
- model.doGenerate({
368
- prompt: 'Edit this image',
369
- files: [
370
- {
371
- type: 'file',
372
- data: 'base64-source-image',
373
- mediaType: 'image/png',
374
- },
375
- ],
376
- mask: undefined,
377
- n: 1,
378
- size: undefined,
379
- aspectRatio: undefined,
380
- seed: undefined,
381
- providerOptions: {},
382
- }),
383
- ).rejects.toThrow(
384
- 'Google Generative AI does not support image editing. ' +
385
- 'Use Google Vertex AI (@ai-sdk/google-vertex) for image editing capabilities.',
386
- );
387
- });
388
-
389
- it('should throw error when mask is provided', async () => {
390
- await expect(
391
- model.doGenerate({
392
- prompt: 'Edit this image',
393
- files: undefined,
394
- mask: {
395
- type: 'file',
396
- data: 'base64-mask-image',
397
- mediaType: 'image/png',
398
- },
399
- n: 1,
400
- size: undefined,
401
- aspectRatio: undefined,
402
- seed: undefined,
403
- providerOptions: {},
404
- }),
405
- ).rejects.toThrow(
406
- 'Google Generative AI does not support image editing with masks. ' +
407
- 'Use Google Vertex AI (@ai-sdk/google-vertex) for image editing capabilities.',
408
- );
409
- });
410
- });
411
- });