Skip to content

Commit b2e02a4

Browse files
authored
Add new attributes for schema (#17340)
1 parent 7894526 commit b2e02a4

File tree

2 files changed

+290
-15
lines changed

2 files changed

+290
-15
lines changed

packages/firebase_vertexai/firebase_vertexai/lib/src/schema.dart

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,46 +23,65 @@ final class Schema {
2323
this.type, {
2424
this.format,
2525
this.description,
26+
this.title,
2627
this.nullable,
2728
this.enumValues,
2829
this.items,
30+
this.minItems,
31+
this.maxItems,
32+
this.minimum,
33+
this.maximum,
2934
this.properties,
3035
this.optionalProperties,
36+
this.propertyOrdering,
37+
this.anyOf,
3138
});
3239

3340
/// Construct a schema for an object with one or more properties.
3441
Schema.object({
3542
required Map<String, Schema> properties,
3643
List<String>? optionalProperties,
44+
List<String>? propertyOrdering,
3745
String? description,
46+
String? title,
3847
bool? nullable,
3948
}) : this(
4049
SchemaType.object,
4150
properties: properties,
4251
optionalProperties: optionalProperties,
52+
propertyOrdering: propertyOrdering,
4353
description: description,
54+
title: title,
4455
nullable: nullable,
4556
);
4657

4758
/// Construct a schema for an array of values with a specified type.
4859
Schema.array({
4960
required Schema items,
5061
String? description,
62+
String? title,
5163
bool? nullable,
64+
int? minItems,
65+
int? maxItems,
5266
}) : this(
5367
SchemaType.array,
5468
description: description,
69+
title: title,
5570
nullable: nullable,
5671
items: items,
72+
minItems: minItems,
73+
maxItems: maxItems,
5774
);
5875

5976
/// Construct a schema for bool value.
6077
Schema.boolean({
6178
String? description,
79+
String? title,
6280
bool? nullable,
6381
}) : this(
6482
SchemaType.boolean,
6583
description: description,
84+
title: title,
6685
nullable: nullable,
6786
);
6887

@@ -71,49 +90,95 @@ final class Schema {
7190
/// The [format] may be "int32" or "int64".
7291
Schema.integer({
7392
String? description,
93+
String? title,
7494
bool? nullable,
7595
String? format,
96+
int? minimum,
97+
int? maximum,
7698
}) : this(
7799
SchemaType.integer,
78100
description: description,
101+
title: title,
79102
nullable: nullable,
80103
format: format,
104+
minimum: minimum?.toDouble(),
105+
maximum: maximum?.toDouble(),
81106
);
82107

83108
/// Construct a schema for a non-integer number.
84109
///
85110
/// The [format] may be "float" or "double".
86111
Schema.number({
87112
String? description,
113+
String? title,
88114
bool? nullable,
89115
String? format,
116+
double? minimum,
117+
double? maximum,
90118
}) : this(
91119
SchemaType.number,
92120
description: description,
121+
title: title,
93122
nullable: nullable,
94123
format: format,
124+
minimum: minimum,
125+
maximum: maximum,
95126
);
96127

97128
/// Construct a schema for String value with enumerated possible values.
98129
Schema.enumString({
99130
required List<String> enumValues,
100131
String? description,
132+
String? title,
101133
bool? nullable,
102134
}) : this(
103135
SchemaType.string,
104136
enumValues: enumValues,
105137
description: description,
138+
title: title,
106139
nullable: nullable,
107140
format: 'enum',
108141
);
109142

110143
/// Construct a schema for a String value.
111144
Schema.string({
112145
String? description,
146+
String? title,
113147
bool? nullable,
114148
String? format,
115-
}) : this(SchemaType.string,
116-
description: description, nullable: nullable, format: format);
149+
}) : this(
150+
SchemaType.string,
151+
description: description,
152+
title: title,
153+
nullable: nullable,
154+
format: format,
155+
);
156+
157+
/// Construct a schema representing a value that must conform to
158+
/// *any* (one or more) of the provided sub-schemas.
159+
///
160+
/// This schema instructs the model to produce data that is valid against at
161+
/// least one of the schemas listed in the `schemas` array. This is useful
162+
/// when a field can accept multiple distinct types or structures.
163+
///
164+
/// **Example:** A field that can hold either a simple user ID (integer) or a
165+
/// detailed user object.
166+
/// ```
167+
/// Schema.anyOf(anyOf: [
168+
/// .Schema.integer(description: "User ID"),
169+
/// .Schema.object(properties: [
170+
/// "userId": Schema.integer(),
171+
/// "userName": Schema.string()
172+
/// ], description: "Detailed User Object")
173+
/// ])
174+
/// ```
175+
/// The generated data could be decoded based on which schema it matches.
176+
Schema.anyOf({
177+
required List<Schema> schemas,
178+
}) : this(
179+
SchemaType.anyOf, // The type will be ignored in toJson
180+
anyOf: schemas,
181+
);
117182

118183
/// The type of this value.
119184
SchemaType type;
@@ -134,6 +199,13 @@ final class Schema {
134199
/// Parameter description may be formatted as Markdown.
135200
String? description;
136201

202+
/// A human-readable name/summary for the schema or a specific property.
203+
///
204+
/// This helps document the schema's purpose but doesn't typically constrain
205+
/// the generated value. It can subtly guide the model by clarifying the
206+
/// intent of a field.
207+
String? title;
208+
137209
/// Whether the value mey be null.
138210
bool? nullable;
139211

@@ -143,6 +215,18 @@ final class Schema {
143215
/// Schema for the elements if this is a [SchemaType.array].
144216
Schema? items;
145217

218+
/// An integer specifying the minimum number of items [SchemaType.array] must contain.
219+
int? minItems;
220+
221+
/// An integer specifying the maximum number of items [SchemaType.array] must contain.
222+
int? maxItems;
223+
224+
/// The minimum value of a numeric type.
225+
double? minimum;
226+
227+
/// The maximum value of a numeric type.
228+
double? maximum;
229+
146230
/// Properties of this type if this is a [SchemaType.object].
147231
Map<String, Schema>? properties;
148232

@@ -153,14 +237,40 @@ final class Schema {
153237
/// treated as required properties
154238
List<String>? optionalProperties;
155239

240+
/// Suggesting order of the properties.
241+
///
242+
/// A specific hint provided to the Gemini model, suggesting the order in
243+
/// which the keys should appear in the generated JSON string.
244+
/// Important: Standard JSON objects are inherently unordered collections of
245+
/// key-value pairs. While the model will try to respect PropertyOrdering in
246+
/// its textual JSON output.
247+
List<String>? propertyOrdering;
248+
249+
/// An array of [Schema] objects to validate generated content.
250+
///
251+
/// The generated data must be valid against *any* (one or more)
252+
/// of the schemas listed in this array. This allows specifying multiple
253+
/// possible structures or types for a single field.
254+
///
255+
/// For example, a value could be either a `String` or an `Int`:
256+
/// ```
257+
/// Schema.anyOf(schemas: [Schema.string(), Schema.integer()]);
258+
List<Schema>? anyOf;
259+
156260
/// Convert to json object.
157261
Map<String, Object> toJson() => {
158-
'type': type.toJson(),
262+
if (type != SchemaType.anyOf)
263+
'type': type.toJson(), // Omit the field while type is anyOf
159264
if (format case final format?) 'format': format,
160265
if (description case final description?) 'description': description,
266+
if (title case final title?) 'title': title,
161267
if (nullable case final nullable?) 'nullable': nullable,
162268
if (enumValues case final enumValues?) 'enum': enumValues,
163269
if (items case final items?) 'items': items.toJson(),
270+
if (minItems case final minItems?) 'minItems': minItems,
271+
if (maxItems case final maxItems?) 'maxItems': maxItems,
272+
if (minimum case final minimum?) 'minimum': minimum,
273+
if (maximum case final maximum?) 'maximum': maximum,
164274
if (properties case final properties?)
165275
'properties': {
166276
for (final MapEntry(:key, :value) in properties.entries)
@@ -173,6 +283,10 @@ final class Schema {
173283
.where((key) => !optionalProperties!.contains(key))
174284
.toList()
175285
: properties!.keys.toList(),
286+
if (propertyOrdering case final propertyOrdering?)
287+
'propertyOrdering': propertyOrdering,
288+
if (anyOf case final anyOf?)
289+
'anyOf': anyOf.map((e) => e.toJson()).toList(),
176290
};
177291
}
178292

@@ -194,7 +308,10 @@ enum SchemaType {
194308
array,
195309

196310
/// object type
197-
object;
311+
object,
312+
313+
/// This schema is anyOf type.
314+
anyOf;
198315

199316
/// Convert to json object.
200317
String toJson() => switch (this) {
@@ -204,5 +321,6 @@ enum SchemaType {
204321
boolean => 'BOOLEAN',
205322
array => 'ARRAY',
206323
object => 'OBJECT',
324+
anyOf => 'null',
207325
};
208326
}

0 commit comments

Comments
 (0)