@ai-sdk/google 4.0.47 → 4.0.49
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 +12 -0
- package/dist/index.d.ts +18 -18
- package/dist/index.js +528 -556
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +101 -12
- package/dist/internal/index.js.map +1 -1
- package/docs/15-google.mdx +7 -60
- package/package.json +1 -1
- package/src/convert-json-schema-to-openapi-schema.ts +171 -14
- package/src/google-image-model-options.ts +12 -16
- package/src/google-image-model.ts +25 -166
- package/src/google-image-settings.ts +1 -5
package/docs/15-google.mdx
CHANGED
|
@@ -1467,7 +1467,7 @@ Function tools work the same way as on the standard provider:
|
|
|
1467
1467
|
|
|
1468
1468
|
```ts
|
|
1469
1469
|
import { google } from '@ai-sdk/google';
|
|
1470
|
-
import { generateText,
|
|
1470
|
+
import { generateText, isStepCount, tool } from 'ai';
|
|
1471
1471
|
import { z } from 'zod';
|
|
1472
1472
|
|
|
1473
1473
|
const weatherTool = tool({
|
|
@@ -1479,7 +1479,7 @@ const weatherTool = tool({
|
|
|
1479
1479
|
const { text, toolCalls } = await generateText({
|
|
1480
1480
|
model: google.interactions('gemini-3.7-flash'),
|
|
1481
1481
|
tools: { getWeather: weatherTool },
|
|
1482
|
-
stopWhen:
|
|
1482
|
+
stopWhen: isStepCount(5),
|
|
1483
1483
|
prompt: 'What is the weather in San Francisco right now?',
|
|
1484
1484
|
});
|
|
1485
1485
|
```
|
|
@@ -1804,7 +1804,7 @@ import { generateText } from 'ai';
|
|
|
1804
1804
|
|
|
1805
1805
|
const { text } = await generateText({
|
|
1806
1806
|
model: google('gemma-3-27b-it'),
|
|
1807
|
-
|
|
1807
|
+
instructions: 'You are a helpful assistant that responds concisely.',
|
|
1808
1808
|
prompt: 'What is machine learning?',
|
|
1809
1809
|
});
|
|
1810
1810
|
```
|
|
@@ -1902,61 +1902,8 @@ The following optional provider options are available for Google embedding model
|
|
|
1902
1902
|
You can create image models that call the Google Generative AI API using the `.image()` factory method.
|
|
1903
1903
|
For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
|
|
1904
1904
|
|
|
1905
|
-
The
|
|
1906
|
-
|
|
1907
|
-
- **Imagen models**: Dedicated image generation models using the `:predict` API
|
|
1908
|
-
- **Gemini image models**: Multimodal language models with image output capabilities using the `:generateContent` API
|
|
1909
|
-
|
|
1910
|
-
### Imagen Models
|
|
1911
|
-
|
|
1912
|
-
[Imagen](https://ai.google.dev/gemini-api/docs/imagen) models are dedicated image generation models.
|
|
1913
|
-
|
|
1914
|
-
```ts
|
|
1915
|
-
import { google } from '@ai-sdk/google';
|
|
1916
|
-
import { generateImage } from 'ai';
|
|
1917
|
-
|
|
1918
|
-
const { image } = await generateImage({
|
|
1919
|
-
model: google.image('imagen-4.0-generate-001'),
|
|
1920
|
-
prompt: 'A futuristic cityscape at sunset',
|
|
1921
|
-
aspectRatio: '16:9',
|
|
1922
|
-
});
|
|
1923
|
-
```
|
|
1924
|
-
|
|
1925
|
-
Further configuration can be done using Google provider options. You can validate the provider options using the `GoogleImageModelOptions` type.
|
|
1926
|
-
|
|
1927
|
-
```ts
|
|
1928
|
-
import { google } from '@ai-sdk/google';
|
|
1929
|
-
import { GoogleImageModelOptions } from '@ai-sdk/google';
|
|
1930
|
-
import { generateImage } from 'ai';
|
|
1931
|
-
|
|
1932
|
-
const { image } = await generateImage({
|
|
1933
|
-
model: google.image('imagen-4.0-generate-001'),
|
|
1934
|
-
providerOptions: {
|
|
1935
|
-
google: {
|
|
1936
|
-
personGeneration: 'dont_allow',
|
|
1937
|
-
} satisfies GoogleImageModelOptions,
|
|
1938
|
-
},
|
|
1939
|
-
// ...
|
|
1940
|
-
});
|
|
1941
|
-
```
|
|
1942
|
-
|
|
1943
|
-
The following provider options are available for Imagen models:
|
|
1944
|
-
|
|
1945
|
-
- **personGeneration** `allow_adult` | `allow_all` | `dont_allow`
|
|
1946
|
-
Whether to allow person generation. Defaults to `allow_adult`.
|
|
1947
|
-
|
|
1948
|
-
<Note>
|
|
1949
|
-
Imagen models do not support the `size` parameter. Use the `aspectRatio`
|
|
1950
|
-
parameter instead.
|
|
1951
|
-
</Note>
|
|
1952
|
-
|
|
1953
|
-
#### Imagen Model Capabilities
|
|
1954
|
-
|
|
1955
|
-
| Model | Aspect Ratios |
|
|
1956
|
-
| ------------------------------- | ------------------------- |
|
|
1957
|
-
| `imagen-4.0-generate-001` | 1:1, 3:4, 4:3, 9:16, 16:9 |
|
|
1958
|
-
| `imagen-4.0-ultra-generate-001` | 1:1, 3:4, 4:3, 9:16, 16:9 |
|
|
1959
|
-
| `imagen-4.0-fast-generate-001` | 1:1, 3:4, 4:3, 9:16, 16:9 |
|
|
1905
|
+
The `.image()` factory supports Gemini multimodal language models with image
|
|
1906
|
+
output capabilities through the `:generateContent` API.
|
|
1960
1907
|
|
|
1961
1908
|
### Gemini Image Models
|
|
1962
1909
|
|
|
@@ -2023,7 +1970,7 @@ const { image } = await generateImage({
|
|
|
2023
1970
|
Gemini image models support [Google Search grounding](#google-search) through `providerOptions.google.googleSearch`. The value matches the args of `google.tools.googleSearch(...)`; pass `{}` to enable with defaults, or `{ searchTypes: { imageSearch: {} } }` to ground on reference photos.
|
|
2024
1971
|
|
|
2025
1972
|
```ts
|
|
2026
|
-
import { google } from '@ai-sdk/google';
|
|
1973
|
+
import { google, type GoogleImageModelOptions } from '@ai-sdk/google';
|
|
2027
1974
|
import { generateImage } from 'ai';
|
|
2028
1975
|
|
|
2029
1976
|
const result = await generateImage({
|
|
@@ -2033,7 +1980,7 @@ const result = await generateImage({
|
|
|
2033
1980
|
providerOptions: {
|
|
2034
1981
|
google: {
|
|
2035
1982
|
googleSearch: { searchTypes: { imageSearch: {} } },
|
|
2036
|
-
},
|
|
1983
|
+
} satisfies GoogleImageModelOptions,
|
|
2037
1984
|
},
|
|
2038
1985
|
});
|
|
2039
1986
|
|
package/package.json
CHANGED
|
@@ -4,6 +4,16 @@ import {
|
|
|
4
4
|
type JSONSchema7Definition,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
6
|
|
|
7
|
+
type JSONSchema7WithDefinitions = JSONSchema7 & {
|
|
8
|
+
$defs?: Record<string, JSONSchema7Definition>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type ReferenceContext = {
|
|
12
|
+
definitions: Record<string, JSONSchema7Definition> | undefined;
|
|
13
|
+
dollarDefinitions: Record<string, JSONSchema7Definition> | undefined;
|
|
14
|
+
resolvingReferences: ReadonlySet<string>;
|
|
15
|
+
};
|
|
16
|
+
|
|
7
17
|
/**
|
|
8
18
|
* Converts JSON Schema 7 to OpenAPI Schema 3.0
|
|
9
19
|
*/
|
|
@@ -11,26 +21,52 @@ export function convertJSONSchemaToOpenAPISchema(
|
|
|
11
21
|
jsonSchema: JSONSchema7Definition | undefined,
|
|
12
22
|
isRoot = true,
|
|
13
23
|
): unknown {
|
|
14
|
-
|
|
24
|
+
const rootSchema =
|
|
25
|
+
typeof jsonSchema === 'object'
|
|
26
|
+
? (jsonSchema as JSONSchema7WithDefinitions)
|
|
27
|
+
: undefined;
|
|
28
|
+
|
|
29
|
+
return convertJSONSchemaDefinition(jsonSchema, isRoot, {
|
|
30
|
+
definitions: rootSchema?.definitions,
|
|
31
|
+
dollarDefinitions: rootSchema?.$defs,
|
|
32
|
+
resolvingReferences: new Set(),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function convertJSONSchemaDefinition(
|
|
37
|
+
jsonSchema: JSONSchema7Definition | undefined,
|
|
38
|
+
isRoot: boolean,
|
|
39
|
+
referenceContext: ReferenceContext,
|
|
40
|
+
): unknown {
|
|
15
41
|
if (jsonSchema == null) {
|
|
16
42
|
return undefined;
|
|
17
43
|
}
|
|
18
44
|
|
|
45
|
+
if (typeof jsonSchema === 'boolean') {
|
|
46
|
+
return { type: 'boolean', properties: {} };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (jsonSchema.$ref != null) {
|
|
50
|
+
return convertJSONSchemaReference({
|
|
51
|
+
jsonSchema,
|
|
52
|
+
reference: jsonSchema.$ref,
|
|
53
|
+
isRoot,
|
|
54
|
+
referenceContext,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Handle empty object schemas: undefined at root, preserved when nested
|
|
19
59
|
if (isEmptyObjectSchema(jsonSchema)) {
|
|
20
60
|
if (isRoot) {
|
|
21
61
|
return undefined;
|
|
22
62
|
}
|
|
23
63
|
|
|
24
|
-
if (
|
|
64
|
+
if (jsonSchema.description) {
|
|
25
65
|
return { type: 'object', description: jsonSchema.description };
|
|
26
66
|
}
|
|
27
67
|
return { type: 'object' };
|
|
28
68
|
}
|
|
29
69
|
|
|
30
|
-
if (typeof jsonSchema === 'boolean') {
|
|
31
|
-
return { type: 'boolean', properties: {} };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
70
|
const {
|
|
35
71
|
type,
|
|
36
72
|
description,
|
|
@@ -83,7 +119,7 @@ export function convertJSONSchemaToOpenAPISchema(
|
|
|
83
119
|
if (properties != null) {
|
|
84
120
|
result.properties = Object.entries(properties).reduce(
|
|
85
121
|
(acc, [key, value]) => {
|
|
86
|
-
acc[key] =
|
|
122
|
+
acc[key] = convertJSONSchemaDefinition(value, false, referenceContext);
|
|
87
123
|
return acc;
|
|
88
124
|
},
|
|
89
125
|
{} as Record<string, unknown>,
|
|
@@ -92,13 +128,15 @@ export function convertJSONSchemaToOpenAPISchema(
|
|
|
92
128
|
|
|
93
129
|
if (items) {
|
|
94
130
|
result.items = Array.isArray(items)
|
|
95
|
-
? items.map(item =>
|
|
96
|
-
|
|
131
|
+
? items.map(item =>
|
|
132
|
+
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
133
|
+
)
|
|
134
|
+
: convertJSONSchemaDefinition(items, false, referenceContext);
|
|
97
135
|
}
|
|
98
136
|
|
|
99
137
|
if (allOf) {
|
|
100
138
|
result.allOf = allOf.map(item =>
|
|
101
|
-
|
|
139
|
+
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
102
140
|
);
|
|
103
141
|
}
|
|
104
142
|
if (anyOf) {
|
|
@@ -114,9 +152,10 @@ export function convertJSONSchemaToOpenAPISchema(
|
|
|
114
152
|
|
|
115
153
|
if (nonNullSchemas.length === 1) {
|
|
116
154
|
// If there's only one non-null schema, convert it and make it nullable
|
|
117
|
-
const converted =
|
|
155
|
+
const converted = convertJSONSchemaDefinition(
|
|
118
156
|
nonNullSchemas[0],
|
|
119
157
|
false,
|
|
158
|
+
referenceContext,
|
|
120
159
|
);
|
|
121
160
|
if (typeof converted === 'object') {
|
|
122
161
|
result.nullable = true;
|
|
@@ -125,19 +164,19 @@ export function convertJSONSchemaToOpenAPISchema(
|
|
|
125
164
|
} else {
|
|
126
165
|
// If there are multiple non-null schemas, keep them in anyOf
|
|
127
166
|
result.anyOf = nonNullSchemas.map(item =>
|
|
128
|
-
|
|
167
|
+
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
129
168
|
);
|
|
130
169
|
result.nullable = true;
|
|
131
170
|
}
|
|
132
171
|
} else {
|
|
133
172
|
result.anyOf = anyOf.map(item =>
|
|
134
|
-
|
|
173
|
+
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
135
174
|
);
|
|
136
175
|
}
|
|
137
176
|
}
|
|
138
177
|
if (oneOf) {
|
|
139
178
|
result.oneOf = oneOf.map(item =>
|
|
140
|
-
|
|
179
|
+
convertJSONSchemaDefinition(item, false, referenceContext),
|
|
141
180
|
);
|
|
142
181
|
}
|
|
143
182
|
|
|
@@ -148,6 +187,124 @@ export function convertJSONSchemaToOpenAPISchema(
|
|
|
148
187
|
return result;
|
|
149
188
|
}
|
|
150
189
|
|
|
190
|
+
function convertJSONSchemaReference({
|
|
191
|
+
jsonSchema,
|
|
192
|
+
reference,
|
|
193
|
+
isRoot,
|
|
194
|
+
referenceContext,
|
|
195
|
+
}: {
|
|
196
|
+
jsonSchema: JSONSchema7;
|
|
197
|
+
reference: string;
|
|
198
|
+
isRoot: boolean;
|
|
199
|
+
referenceContext: ReferenceContext;
|
|
200
|
+
}): unknown {
|
|
201
|
+
const { definition, referenceKey } = getReferencedDefinition(
|
|
202
|
+
reference,
|
|
203
|
+
referenceContext,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (referenceContext.resolvingReferences.has(referenceKey)) {
|
|
207
|
+
throw new UnsupportedFunctionalityError({
|
|
208
|
+
functionality: `recursive JSON Schema reference: ${reference}`,
|
|
209
|
+
message:
|
|
210
|
+
'Google schema conversion does not support recursive JSON Schema references.',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const resolvingReferences = new Set(referenceContext.resolvingReferences);
|
|
215
|
+
resolvingReferences.add(referenceKey);
|
|
216
|
+
|
|
217
|
+
// Inline references instead of emitting Google's `ref` / `defs` fields.
|
|
218
|
+
// Those fields are supported by Vertex AI's Schema representation but are
|
|
219
|
+
// rejected by the Gemini Developer API representation used by this shared
|
|
220
|
+
// converter.
|
|
221
|
+
const { $ref: _reference, ...siblingSchema } = jsonSchema;
|
|
222
|
+
const resolvedSchema =
|
|
223
|
+
typeof definition === 'boolean'
|
|
224
|
+
? definition
|
|
225
|
+
? siblingSchema
|
|
226
|
+
: false
|
|
227
|
+
: { ...definition, ...siblingSchema };
|
|
228
|
+
|
|
229
|
+
return convertJSONSchemaDefinition(resolvedSchema, isRoot, {
|
|
230
|
+
...referenceContext,
|
|
231
|
+
resolvingReferences,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function getReferencedDefinition(
|
|
236
|
+
reference: string,
|
|
237
|
+
referenceContext: ReferenceContext,
|
|
238
|
+
): {
|
|
239
|
+
definition: JSONSchema7Definition;
|
|
240
|
+
referenceKey: string;
|
|
241
|
+
} {
|
|
242
|
+
const definitionSources = [
|
|
243
|
+
{
|
|
244
|
+
prefix: '#/$defs/',
|
|
245
|
+
definitions: referenceContext.dollarDefinitions,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
prefix: '#/definitions/',
|
|
249
|
+
definitions: referenceContext.definitions,
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
const source = definitionSources.find(({ prefix }) =>
|
|
254
|
+
reference.startsWith(prefix),
|
|
255
|
+
);
|
|
256
|
+
const encodedDefinitionName = source
|
|
257
|
+
? reference.slice(source.prefix.length)
|
|
258
|
+
: undefined;
|
|
259
|
+
|
|
260
|
+
if (
|
|
261
|
+
source == null ||
|
|
262
|
+
encodedDefinitionName == null ||
|
|
263
|
+
encodedDefinitionName.length === 0 ||
|
|
264
|
+
encodedDefinitionName.includes('/')
|
|
265
|
+
) {
|
|
266
|
+
throwUnsupportedReference(reference);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let decodedDefinitionName: string;
|
|
270
|
+
try {
|
|
271
|
+
decodedDefinitionName = decodeURIComponent(encodedDefinitionName);
|
|
272
|
+
} catch {
|
|
273
|
+
throwUnsupportedReference(reference);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (
|
|
277
|
+
decodedDefinitionName.includes('/') ||
|
|
278
|
+
/~(?![01])/u.test(decodedDefinitionName) ||
|
|
279
|
+
source.definitions == null
|
|
280
|
+
) {
|
|
281
|
+
throwUnsupportedReference(reference);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const definitionName = decodedDefinitionName.replace(/~[01]/g, match =>
|
|
285
|
+
match === '~1' ? '/' : '~',
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
if (
|
|
289
|
+
!Object.prototype.hasOwnProperty.call(source.definitions, definitionName)
|
|
290
|
+
) {
|
|
291
|
+
throwUnsupportedReference(reference);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
definition: source.definitions[definitionName],
|
|
296
|
+
referenceKey: `${source.prefix}${definitionName}`,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function throwUnsupportedReference(reference: string): never {
|
|
301
|
+
throw new UnsupportedFunctionalityError({
|
|
302
|
+
functionality: `JSON Schema reference: ${reference}`,
|
|
303
|
+
message:
|
|
304
|
+
'Google schema conversion only supports references to direct children of root-level $defs or definitions.',
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
151
308
|
type EnumValues = NonNullable<JSONSchema7['enum']>;
|
|
152
309
|
type EnumType = 'string' | 'number' | 'integer' | 'boolean';
|
|
153
310
|
type GoogleEnumSchema = {
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
lazySchema,
|
|
3
|
-
zodSchema,
|
|
4
|
-
type InferSchema,
|
|
5
|
-
} from '@ai-sdk/provider-utils';
|
|
1
|
+
import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
|
|
6
2
|
import { z } from 'zod/v4';
|
|
7
|
-
import {
|
|
3
|
+
import type { GoogleLanguageModelOptions } from './google-language-model-options';
|
|
4
|
+
import {
|
|
5
|
+
googleSearchToolArgsBaseSchema,
|
|
6
|
+
type GoogleSearchToolArgs,
|
|
7
|
+
} from './tool/google-search';
|
|
8
8
|
|
|
9
|
-
// Note: For the initial GA launch of Imagen 3, safety filters are not configurable.
|
|
10
|
-
// https://ai.google.dev/gemini-api/docs/imagen#imagen-model
|
|
11
9
|
export const googleImageModelOptionsSchema = lazySchema(() =>
|
|
12
10
|
zodSchema(
|
|
13
11
|
z.object({
|
|
14
|
-
personGeneration: z
|
|
15
|
-
.enum(['dont_allow', 'allow_adult', 'allow_all'])
|
|
16
|
-
.nullish(),
|
|
17
|
-
aspectRatio: z.enum(['1:1', '3:4', '4:3', '9:16', '16:9']).nullish(),
|
|
18
|
-
|
|
19
12
|
/**
|
|
20
13
|
* Enable Google Search grounding for Gemini image models. The value is
|
|
21
14
|
* forwarded as the args of the `google.tools.googleSearch` provider
|
|
@@ -30,6 +23,9 @@ export const googleImageModelOptionsSchema = lazySchema(() =>
|
|
|
30
23
|
),
|
|
31
24
|
);
|
|
32
25
|
|
|
33
|
-
export type GoogleImageModelOptions =
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
export type GoogleImageModelOptions = Omit<
|
|
27
|
+
GoogleLanguageModelOptions,
|
|
28
|
+
'responseModalities'
|
|
29
|
+
> & {
|
|
30
|
+
googleSearch?: GoogleSearchToolArgs;
|
|
31
|
+
};
|
|
@@ -4,23 +4,15 @@ import type {
|
|
|
4
4
|
SharedV4Warning,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
6
|
import {
|
|
7
|
-
combineHeaders,
|
|
8
7
|
convertToBase64,
|
|
9
|
-
createJsonResponseHandler,
|
|
10
8
|
generateId as defaultGenerateId,
|
|
11
|
-
lazySchema,
|
|
12
9
|
parseProviderOptions,
|
|
13
|
-
postJsonToApi,
|
|
14
|
-
resolve,
|
|
15
10
|
serializeModelOptions,
|
|
16
11
|
WORKFLOW_SERIALIZE,
|
|
17
12
|
WORKFLOW_DESERIALIZE,
|
|
18
|
-
zodSchema,
|
|
19
13
|
type FetchFunction,
|
|
20
14
|
type Resolvable,
|
|
21
15
|
} from '@ai-sdk/provider-utils';
|
|
22
|
-
import { z } from 'zod/v4';
|
|
23
|
-
import { googleFailedResponseHandler } from './google-error';
|
|
24
16
|
import { googleImageModelOptionsSchema } from './google-image-model-options';
|
|
25
17
|
import type {
|
|
26
18
|
GoogleImageModelId,
|
|
@@ -61,12 +53,7 @@ export class GoogleImageModel implements ImageModelV4 {
|
|
|
61
53
|
if (this.settings.maxImagesPerCall != null) {
|
|
62
54
|
return this.settings.maxImagesPerCall;
|
|
63
55
|
}
|
|
64
|
-
|
|
65
|
-
if (isGeminiModel(this.modelId)) {
|
|
66
|
-
return 10;
|
|
67
|
-
}
|
|
68
|
-
// https://ai.google.dev/gemini-api/docs/imagen#imagen-model
|
|
69
|
-
return 4;
|
|
56
|
+
return 10;
|
|
70
57
|
}
|
|
71
58
|
|
|
72
59
|
get provider(): string {
|
|
@@ -82,137 +69,12 @@ export class GoogleImageModel implements ImageModelV4 {
|
|
|
82
69
|
async doGenerate(
|
|
83
70
|
options: Parameters<ImageModelV4['doGenerate']>[0],
|
|
84
71
|
): Promise<Awaited<ReturnType<ImageModelV4['doGenerate']>>> {
|
|
85
|
-
|
|
86
|
-
if (isGeminiModel(this.modelId)) {
|
|
87
|
-
return this.doGenerateGemini(options);
|
|
88
|
-
}
|
|
89
|
-
return this.doGenerateImagen(options);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
private async doGenerateImagen(
|
|
93
|
-
options: Parameters<ImageModelV4['doGenerate']>[0],
|
|
94
|
-
): Promise<Awaited<ReturnType<ImageModelV4['doGenerate']>>> {
|
|
95
|
-
const {
|
|
96
|
-
prompt,
|
|
97
|
-
n = 1,
|
|
98
|
-
size,
|
|
99
|
-
aspectRatio = '1:1',
|
|
100
|
-
seed,
|
|
101
|
-
providerOptions,
|
|
102
|
-
headers,
|
|
103
|
-
abortSignal,
|
|
104
|
-
files,
|
|
105
|
-
mask,
|
|
106
|
-
} = options;
|
|
107
|
-
const warnings: Array<SharedV4Warning> = [];
|
|
108
|
-
|
|
109
|
-
// Imagen API endpoints do not support image editing
|
|
110
|
-
if (files != null && files.length > 0) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
'Google Gemini API does not support image editing with Imagen models. ' +
|
|
113
|
-
'Use Google Vertex AI (@ai-sdk/google-vertex) for image editing capabilities.',
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (mask != null) {
|
|
72
|
+
if (!this.modelId.startsWith('gemini-')) {
|
|
118
73
|
throw new Error(
|
|
119
|
-
'Google Gemini
|
|
120
|
-
'Use Google Vertex AI (@ai-sdk/google-vertex) for image editing capabilities.',
|
|
74
|
+
'Google image models other than Gemini are no longer supported. Use a model ID that starts with `gemini-`.',
|
|
121
75
|
);
|
|
122
76
|
}
|
|
123
77
|
|
|
124
|
-
if (size != null) {
|
|
125
|
-
warnings.push({
|
|
126
|
-
type: 'unsupported',
|
|
127
|
-
feature: 'size',
|
|
128
|
-
details:
|
|
129
|
-
'This model does not support the `size` option. Use `aspectRatio` instead.',
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (seed != null) {
|
|
134
|
-
warnings.push({
|
|
135
|
-
type: 'unsupported',
|
|
136
|
-
feature: 'seed',
|
|
137
|
-
details:
|
|
138
|
-
'This model does not support the `seed` option through this provider.',
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const googleOptions = await parseProviderOptions({
|
|
143
|
-
provider: 'google',
|
|
144
|
-
providerOptions,
|
|
145
|
-
schema: googleImageModelOptionsSchema,
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
149
|
-
|
|
150
|
-
const parameters: Record<string, unknown> = {
|
|
151
|
-
sampleCount: n,
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
if (aspectRatio != null) {
|
|
155
|
-
parameters.aspectRatio = aspectRatio;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (googleOptions) {
|
|
159
|
-
const { googleSearch: imagenGoogleSearch, ...imagenOptions } =
|
|
160
|
-
googleOptions;
|
|
161
|
-
if (imagenGoogleSearch != null) {
|
|
162
|
-
warnings.push({
|
|
163
|
-
type: 'unsupported',
|
|
164
|
-
feature: 'googleSearch',
|
|
165
|
-
details:
|
|
166
|
-
'Google Search grounding is only supported on Gemini image models.',
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
Object.assign(parameters, imagenOptions);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const body = {
|
|
173
|
-
instances: [{ prompt }],
|
|
174
|
-
parameters,
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const { responseHeaders, value: response } = await postJsonToApi<{
|
|
178
|
-
predictions: Array<{ bytesBase64Encoded: string }>;
|
|
179
|
-
}>({
|
|
180
|
-
url: `${this.config.baseURL}/models/${this.modelId}:predict`,
|
|
181
|
-
headers: combineHeaders(
|
|
182
|
-
this.config.headers ? await resolve(this.config.headers) : undefined,
|
|
183
|
-
headers,
|
|
184
|
-
),
|
|
185
|
-
body,
|
|
186
|
-
failedResponseHandler: googleFailedResponseHandler,
|
|
187
|
-
successfulResponseHandler: createJsonResponseHandler(
|
|
188
|
-
googleImageResponseSchema,
|
|
189
|
-
),
|
|
190
|
-
abortSignal,
|
|
191
|
-
fetch: this.config.fetch,
|
|
192
|
-
});
|
|
193
|
-
return {
|
|
194
|
-
images: response.predictions.map(
|
|
195
|
-
(p: { bytesBase64Encoded: string }) => p.bytesBase64Encoded,
|
|
196
|
-
),
|
|
197
|
-
warnings,
|
|
198
|
-
providerMetadata: {
|
|
199
|
-
google: {
|
|
200
|
-
images: response.predictions.map(() => ({
|
|
201
|
-
// Add any prediction-specific metadata here
|
|
202
|
-
})),
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
response: {
|
|
206
|
-
timestamp: currentDate,
|
|
207
|
-
modelId: this.modelId,
|
|
208
|
-
headers: responseHeaders,
|
|
209
|
-
},
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private async doGenerateGemini(
|
|
214
|
-
options: Parameters<ImageModelV4['doGenerate']>[0],
|
|
215
|
-
): Promise<Awaited<ReturnType<ImageModelV4['doGenerate']>>> {
|
|
216
78
|
const {
|
|
217
79
|
prompt,
|
|
218
80
|
n,
|
|
@@ -302,8 +164,12 @@ export class GoogleImageModel implements ImageModelV4 {
|
|
|
302
164
|
schema: googleImageModelOptionsSchema,
|
|
303
165
|
});
|
|
304
166
|
|
|
305
|
-
const {
|
|
306
|
-
|
|
167
|
+
const {
|
|
168
|
+
googleSearch: _strippedGoogleSearch,
|
|
169
|
+
responseModalities: _strippedResponseModalities,
|
|
170
|
+
imageConfig: userImageConfig,
|
|
171
|
+
...passthroughGoogleOptions
|
|
172
|
+
} = providerOptions?.google ?? {};
|
|
307
173
|
|
|
308
174
|
// Instantiate language model
|
|
309
175
|
const languageModel = new GoogleLanguageModel(this.modelId, {
|
|
@@ -320,18 +186,26 @@ export class GoogleImageModel implements ImageModelV4 {
|
|
|
320
186
|
seed,
|
|
321
187
|
providerOptions: {
|
|
322
188
|
google: {
|
|
323
|
-
responseModalities: ['IMAGE'],
|
|
324
|
-
imageConfig: aspectRatio
|
|
325
|
-
? {
|
|
326
|
-
aspectRatio: aspectRatio as NonNullable<
|
|
327
|
-
GoogleLanguageModelOptions['imageConfig']
|
|
328
|
-
>['aspectRatio'],
|
|
329
|
-
}
|
|
330
|
-
: undefined,
|
|
331
189
|
...(passthroughGoogleOptions as Omit<
|
|
332
190
|
GoogleLanguageModelOptions,
|
|
333
191
|
'responseModalities' | 'imageConfig'
|
|
334
192
|
>),
|
|
193
|
+
responseModalities: ['IMAGE'],
|
|
194
|
+
imageConfig:
|
|
195
|
+
aspectRatio != null || userImageConfig != null
|
|
196
|
+
? {
|
|
197
|
+
...(userImageConfig as NonNullable<
|
|
198
|
+
GoogleLanguageModelOptions['imageConfig']
|
|
199
|
+
>),
|
|
200
|
+
...(aspectRatio != null
|
|
201
|
+
? {
|
|
202
|
+
aspectRatio: aspectRatio as NonNullable<
|
|
203
|
+
GoogleLanguageModelOptions['imageConfig']
|
|
204
|
+
>['aspectRatio'],
|
|
205
|
+
}
|
|
206
|
+
: {}),
|
|
207
|
+
}
|
|
208
|
+
: undefined,
|
|
335
209
|
} satisfies GoogleLanguageModelOptions,
|
|
336
210
|
},
|
|
337
211
|
tools:
|
|
@@ -393,18 +267,3 @@ export class GoogleImageModel implements ImageModelV4 {
|
|
|
393
267
|
};
|
|
394
268
|
}
|
|
395
269
|
}
|
|
396
|
-
|
|
397
|
-
function isGeminiModel(modelId: string): boolean {
|
|
398
|
-
return modelId.startsWith('gemini-');
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// minimal version of the schema
|
|
402
|
-
const googleImageResponseSchema = lazySchema(() =>
|
|
403
|
-
zodSchema(
|
|
404
|
-
z.object({
|
|
405
|
-
predictions: z
|
|
406
|
-
.array(z.object({ bytesBase64Encoded: z.string() }))
|
|
407
|
-
.default([]),
|
|
408
|
-
}),
|
|
409
|
-
),
|
|
410
|
-
);
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
export type GoogleImageModelId =
|
|
2
|
-
// Imagen models (use :predict API)
|
|
3
|
-
| 'imagen-4.0-generate-001'
|
|
4
|
-
| 'imagen-4.0-ultra-generate-001'
|
|
5
|
-
| 'imagen-4.0-fast-generate-001'
|
|
6
2
|
// Gemini image models (technically multimodal output language models, use :generateContent API)
|
|
7
3
|
| 'gemini-2.5-flash-image'
|
|
8
4
|
| 'gemini-3-pro-image-preview'
|
|
@@ -11,7 +7,7 @@ export type GoogleImageModelId =
|
|
|
11
7
|
|
|
12
8
|
export interface GoogleImageSettings {
|
|
13
9
|
/**
|
|
14
|
-
* Override the maximum number of images per call (default
|
|
10
|
+
* Override the maximum number of images per call (default 10)
|
|
15
11
|
*/
|
|
16
12
|
maxImagesPerCall?: number;
|
|
17
13
|
}
|