Skip to content

Commit 1789884

Browse files
feat (provider/{google, google-vertex}): expose type for validating provider options (#5491)
1 parent 2abf7a6 commit 1789884

11 files changed

Lines changed: 210 additions & 41 deletions

File tree

.changeset/pink-deers-switch.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@ai-sdk/google-vertex': patch
3+
'@ai-sdk/google': patch
4+
---
5+
6+
feat: add provider option schemas for vertex imagegen and google genai

content/providers/01-ai-sdk-providers/15-google-generative-ai.mdx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const model = google('gemini-1.5-pro-latest');
8080
e.g. `tunedModels/my-model`.
8181
</Note>
8282

83-
Google Generative AI models support also some model specific settings that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
83+
Google Generative AI also supports some model specific settings that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
8484
You can pass them as an options argument:
8585

8686
```ts
@@ -132,6 +132,29 @@ The following optional settings are available for Google Generative AI models:
132132
- `BLOCK_ONLY_HIGH`
133133
- `BLOCK_NONE`
134134

135+
Further configuration can be done using Google Generative AI provider options. You can validate the provider options using the `GoogleGenerativeAIProviderOptions` type.
136+
137+
```ts
138+
import { google } from '@ai-sdk/google';
139+
import { GoogleGenerativeAIProviderOptions } from '@ai-sdk/google';
140+
import { generateText } from 'ai';
141+
142+
const { text } = await generateText({
143+
model: google('gemini-1.5-pro-latest'),
144+
providerOptions: {
145+
google: {
146+
responseModalities: ['TEXT', 'IMAGE'],
147+
} satisfies GoogleGenerativeAIProviderOptions,
148+
},
149+
// ...
150+
});
151+
```
152+
153+
The following provider options are available:
154+
155+
- **responseModalities** _string[]_
156+
The modalities to use for the response. The following modalities are supported: `TEXT`, `IMAGE`. When not defined or empty, the model defaults to returning only text.
157+
135158
You can use Google Generative AI language models to generate text with the `generateText` function:
136159

137160
```ts

content/providers/01-ai-sdk-providers/16-google-vertex.mdx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,41 @@ const { image } = await generateImage({
599599
});
600600
```
601601

602+
Further configuration can be done using Google Vertex provider options. You can validate the provider options using the `GoogleVertexImageProviderOptions` type.
603+
604+
```ts
605+
import { vertex } from '@ai-sdk/google-vertex';
606+
import { GoogleVertexImageProviderOptions } from '@ai-sdk/google-vertex';
607+
import { generateImage } from 'ai';
608+
609+
const { image } = await generateImage({
610+
model: vertex.image('imagen-3.0-generate-001'),
611+
providerOptions: {
612+
vertex: {
613+
negativePrompt: 'pixelated, blurry, low-quality',
614+
} satisfies GoogleVertexImageProviderOptions,
615+
},
616+
// ...
617+
});
618+
```
619+
620+
The following provider options are available:
621+
622+
- **negativePrompt** _string_
623+
A description of what to discourage in the generated images.
624+
625+
- **personGeneration** `allow_adult` | `allow_all` | `dont_allow`
626+
Whether to allow person generation. Defaults to `allow_adult`.
627+
628+
- **safetySetting** `block_low_and_above` | `block_medium_and_above` | `block_only_high` | `block_none`
629+
Whether to block unsafe content. Defaults to `block_medium_and_above`.
630+
631+
- **addWatermark** _boolean_
632+
Whether to add an invisible watermark to the generated images. Defaults to `true`.
633+
634+
- **storageUri** _string_
635+
Cloud Storage URI to store the generated images.
636+
602637
<Note>
603638
Imagen models do not support the `size` parameter. Use the `aspectRatio`
604639
parameter instead.

examples/ai-core/src/generate-image/google-vertex.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { vertex } from '@ai-sdk/google-vertex';
1+
import {
2+
GoogleVertexImageProviderOptions,
3+
vertex,
4+
} from '@ai-sdk/google-vertex';
25
import { experimental_generateImage as generateImage } from 'ai';
3-
import { presentImages } from '../lib/present-image';
46
import 'dotenv/config';
7+
import { presentImages } from '../lib/present-image';
58

69
async function main() {
710
const { image } = await generateImage({
@@ -10,9 +13,8 @@ async function main() {
1013
aspectRatio: '1:1',
1114
providerOptions: {
1215
vertex: {
13-
// https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/imagen-api#parameter_list
1416
addWatermark: false,
15-
},
17+
} satisfies GoogleVertexImageProviderOptions,
1618
},
1719
});
1820

examples/ai-core/src/generate-text/google-image.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { google } from '@ai-sdk/google';
1+
import { google, GoogleGenerativeAIProviderOptions } from '@ai-sdk/google';
22
import { generateText } from 'ai';
33
import 'dotenv/config';
44
import fs from 'node:fs';
@@ -15,6 +15,11 @@ async function main() {
1515
],
1616
},
1717
],
18+
providerOptions: {
19+
google: {
20+
responseModalities: ['TEXT', 'IMAGE'],
21+
} satisfies GoogleGenerativeAIProviderOptions,
22+
},
1823
});
1924

2025
console.log(result.text);

packages/google-vertex/src/google-vertex-image-model.test.ts

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,6 @@ describe('GoogleVertexImageModel', () => {
3838
};
3939
}
4040

41-
it('should pass the correct parameters', async () => {
42-
prepareJsonResponse();
43-
44-
await model.doGenerate({
45-
prompt,
46-
n: 2,
47-
size: undefined,
48-
aspectRatio: undefined,
49-
seed: undefined,
50-
providerOptions: { vertex: { aspectRatio: '1:1' } },
51-
});
52-
53-
expect(await server.calls[0].requestBody).toStrictEqual({
54-
instances: [{ prompt }],
55-
parameters: {
56-
sampleCount: 2,
57-
aspectRatio: '1:1',
58-
},
59-
});
60-
});
61-
6241
it('should pass headers', async () => {
6342
prepareJsonResponse();
6443

@@ -143,13 +122,9 @@ describe('GoogleVertexImageModel', () => {
143122
prompt: 'test prompt',
144123
n: 1,
145124
size: undefined,
146-
aspectRatio: undefined,
125+
aspectRatio: '16:9',
147126
seed: undefined,
148-
providerOptions: {
149-
vertex: {
150-
aspectRatio: '16:9',
151-
},
152-
},
127+
providerOptions: {},
153128
});
154129

155130
expect(await server.calls[0].requestBody).toStrictEqual({
@@ -214,7 +189,7 @@ describe('GoogleVertexImageModel', () => {
214189
seed: 42,
215190
providerOptions: {
216191
vertex: {
217-
temperature: 0.8,
192+
addWatermark: false,
218193
},
219194
},
220195
});
@@ -225,7 +200,7 @@ describe('GoogleVertexImageModel', () => {
225200
sampleCount: 1,
226201
aspectRatio: '1:1',
227202
seed: 42,
228-
temperature: 0.8,
203+
addWatermark: false,
229204
},
230205
});
231206
});
@@ -302,7 +277,7 @@ describe('GoogleVertexImageModel', () => {
302277

303278
const result = await model.doGenerate({
304279
prompt,
305-
n: 1,
280+
n: 2,
306281
size: undefined,
307282
aspectRatio: undefined,
308283
seed: undefined,
@@ -319,5 +294,36 @@ describe('GoogleVertexImageModel', () => {
319294
);
320295
expect(result.response.modelId).toBe('imagen-3.0-generate-001');
321296
});
297+
298+
it('should only pass valid provider options', async () => {
299+
prepareJsonResponse();
300+
301+
await model.doGenerate({
302+
prompt,
303+
n: 2,
304+
size: undefined,
305+
aspectRatio: '16:9',
306+
seed: undefined,
307+
providerOptions: {
308+
vertex: {
309+
addWatermark: false,
310+
negativePrompt: 'negative prompt',
311+
personGeneration: 'allow_all',
312+
foo: 'bar',
313+
},
314+
},
315+
});
316+
317+
expect(await server.calls[0].requestBody).toStrictEqual({
318+
instances: [{ prompt }],
319+
parameters: {
320+
sampleCount: 2,
321+
addWatermark: false,
322+
negativePrompt: 'negative prompt',
323+
personGeneration: 'allow_all',
324+
aspectRatio: '16:9',
325+
},
326+
});
327+
});
322328
});
323329
});

packages/google-vertex/src/google-vertex-image-model.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Resolvable,
44
combineHeaders,
55
createJsonResponseHandler,
6+
parseProviderOptions,
67
postJsonToApi,
78
resolve,
89
} from '@ai-sdk/provider-utils';
@@ -65,13 +66,19 @@ export class GoogleVertexImageModel implements ImageModelV1 {
6566
});
6667
}
6768

69+
const vertexImageOptions = parseProviderOptions({
70+
provider: 'vertex',
71+
providerOptions,
72+
schema: vertexImageProviderOptionsSchema,
73+
});
74+
6875
const body = {
6976
instances: [{ prompt }],
7077
parameters: {
7178
sampleCount: n,
7279
...(aspectRatio != null ? { aspectRatio } : {}),
7380
...(seed != null ? { seed } : {}),
74-
...(providerOptions.vertex ?? {}),
81+
...(vertexImageOptions ?? {}),
7582
},
7683
};
7784

@@ -108,3 +115,23 @@ export class GoogleVertexImageModel implements ImageModelV1 {
108115
const vertexImageResponseSchema = z.object({
109116
predictions: z.array(z.object({ bytesBase64Encoded: z.string() })).nullish(),
110117
});
118+
119+
const vertexImageProviderOptionsSchema = z.object({
120+
negativePrompt: z.string().nullish(),
121+
personGeneration: z
122+
.enum(['dont_allow', 'allow_adult', 'allow_all'])
123+
.nullish(),
124+
safetySetting: z
125+
.enum([
126+
'block_low_and_above',
127+
'block_medium_and_above',
128+
'block_only_high',
129+
'block_none',
130+
])
131+
.nullish(),
132+
addWatermark: z.boolean().nullish(),
133+
storageUri: z.string().nullish(),
134+
});
135+
export type GoogleVertexImageProviderOptions = z.infer<
136+
typeof vertexImageProviderOptionsSchema
137+
>;

packages/google-vertex/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export type { GoogleVertexImageProviderOptions } from './google-vertex-image-model';
12
export { createVertex, vertex } from './google-vertex-provider-node';
23
export type {
34
GoogleVertexProvider,

packages/google/src/google-generative-ai-language-model.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,39 @@ describe('doGenerate', () => {
414414
});
415415
});
416416

417+
it('should only pass valid provider options', async () => {
418+
prepareJsonResponse({});
419+
420+
await model.doGenerate({
421+
inputFormat: 'prompt',
422+
mode: { type: 'regular' },
423+
prompt: [
424+
{ role: 'system', content: 'test system instruction' },
425+
{ role: 'user', content: [{ type: 'text', text: 'Hello' }] },
426+
],
427+
seed: 123,
428+
temperature: 0.5,
429+
providerMetadata: {
430+
google: { foo: 'bar', responseModalities: ['TEXT', 'IMAGE'] },
431+
},
432+
});
433+
434+
expect(await server.calls[0].requestBody).toStrictEqual({
435+
contents: [
436+
{
437+
role: 'user',
438+
parts: [{ text: 'Hello' }],
439+
},
440+
],
441+
systemInstruction: { parts: [{ text: 'test system instruction' }] },
442+
generationConfig: {
443+
seed: 123,
444+
temperature: 0.5,
445+
responseModalities: ['TEXT', 'IMAGE'],
446+
},
447+
});
448+
});
449+
417450
it('should pass tools and toolChoice', async () => {
418451
prepareJsonResponse({});
419452

@@ -1869,4 +1902,29 @@ describe('doStream', () => {
18691902
'tool-calls',
18701903
);
18711904
});
1905+
1906+
it('should only pass valid provider options', async () => {
1907+
prepareStreamResponse({ content: [''] });
1908+
1909+
await model.doStream({
1910+
inputFormat: 'prompt',
1911+
mode: { type: 'regular' },
1912+
prompt: TEST_PROMPT,
1913+
providerMetadata: {
1914+
google: { foo: 'bar', responseModalities: ['TEXT', 'IMAGE'] },
1915+
},
1916+
});
1917+
1918+
expect(await server.calls[0].requestBody).toMatchObject({
1919+
contents: [
1920+
{
1921+
role: 'user',
1922+
parts: [{ text: 'Hello' }],
1923+
},
1924+
],
1925+
generationConfig: {
1926+
responseModalities: ['TEXT', 'IMAGE'],
1927+
},
1928+
});
1929+
});
18721930
});

packages/google/src/google-generative-ai-language-model.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV1 {
8888
const googleOptions = parseProviderOptions({
8989
provider: 'google',
9090
providerOptions: providerMetadata,
91-
schema: z.object({
92-
responseModalities: z.array(z.enum(['TEXT', 'IMAGE'])).nullish(),
93-
}),
91+
schema: googleGenerativeAIProviderOptionsSchema,
9492
});
9593

9694
const generationConfig = {
@@ -623,3 +621,10 @@ const chunkSchema = z.object({
623621
})
624622
.nullish(),
625623
});
624+
625+
const googleGenerativeAIProviderOptionsSchema = z.object({
626+
responseModalities: z.array(z.enum(['TEXT', 'IMAGE'])).nullish(),
627+
});
628+
export type GoogleGenerativeAIProviderOptions = z.infer<
629+
typeof googleGenerativeAIProviderOptionsSchema
630+
>;

0 commit comments

Comments
 (0)