From f0d429038e8500ddfc86c520af51cd52d7bbae50 Mon Sep 17 00:00:00 2001 From: SeanChinJunKai Date: Fri, 13 Jun 2025 02:10:34 +0800 Subject: [PATCH 1/6] Add title parameter to all static functions in Schema class --- FirebaseAI/Sources/Types/Public/Schema.swift | 38 ++++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/FirebaseAI/Sources/Types/Public/Schema.swift b/FirebaseAI/Sources/Types/Public/Schema.swift index f5a378a4c11..47316ed1a7c 100644 --- a/FirebaseAI/Sources/Types/Public/Schema.swift +++ b/FirebaseAI/Sources/Types/Public/Schema.swift @@ -167,6 +167,7 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the string should contain or represent; may /// use Markdown format. + /// - title: An optional human-readable name/summary for the object schema. /// - nullable: If `true`, instructs the model that it *may* generate `null` instead of a /// string; defaults to `false`, enforcing that a string value is generated. /// - format: An optional modifier describing the expected format of the string. Currently no @@ -174,12 +175,13 @@ public final class Schema: Sendable { /// ``StringFormat/custom(_:)``, for example `.custom("email")` or `.custom("byte")`; these /// provide additional hints for how the model should respond but are not guaranteed to be /// adhered to. - public static func string(description: String? = nil, nullable: Bool = false, - format: StringFormat? = nil) -> Schema { + public static func string(description: String? = nil, title: String? = nil, + nullable: Bool = false, format: StringFormat? = nil) -> Schema { return self.init( type: .string, format: format?.rawValue, description: description, + title: title, nullable: nullable ) } @@ -202,15 +204,17 @@ public final class Schema: Sendable { /// - values: The list of string values that may be generated by the model. /// - description: An optional description of what the `values` contain or represent; may use /// Markdown format. + /// - title: An optional human-readable name/summary for the object schema. /// - nullable: If `true`, instructs the model that it *may* generate `null` instead of one of /// the strings specified in `values`; defaults to `false`, enforcing that one of the string /// values is generated. public static func enumeration(values: [String], description: String? = nil, - nullable: Bool = false) -> Schema { + title: String? = nil, nullable: Bool = false) -> Schema { return self.init( type: .string, format: "enum", description: description, + title: title, nullable: nullable, enumValues: values ) @@ -229,18 +233,20 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the number should contain or represent; may /// use Markdown format. + /// - title: An optional human-readable name/summary for the object schema. /// - nullable: If `true`, instructs the model that it may generate `null` instead of a number; /// defaults to `false`, enforcing that a number is generated. /// - minimum: If specified, instructs the model that the value should be greater than or /// equal to the specified minimum. /// - maximum: If specified, instructs the model that the value should be less than or equal /// to the specified maximum. - public static func float(description: String? = nil, nullable: Bool = false, + public static func float(description: String? = nil, title: String? = nil, nullable: Bool = false, minimum: Float? = nil, maximum: Float? = nil) -> Schema { return self.init( type: .number, format: "float", description: description, + title: title, nullable: nullable, minimum: minimum.map { Double($0) }, maximum: maximum.map { Double($0) } @@ -255,17 +261,20 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the number should contain or represent; may /// use Markdown format. + /// - title: An optional human-readable name/summary for the object schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of a number; /// defaults to `false`, enforcing that a number is returned. /// - minimum: If specified, instructs the model that the value should be greater than or /// equal to the specified minimum. /// - maximum: If specified, instructs the model that the value should be less than or equal /// to the specified maximum. - public static func double(description: String? = nil, nullable: Bool = false, + public static func double(description: String? = nil, title: String? = nil, + nullable: Bool = false, minimum: Double? = nil, maximum: Double? = nil) -> Schema { return self.init( type: .number, description: description, + title: title, nullable: nullable, minimum: minimum, maximum: maximum @@ -287,6 +296,7 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the integer should contain or represent; may /// use Markdown format. + /// - title: An optional human-readable name/summary for the object schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of an integer; /// defaults to `false`, enforcing that an integer is returned. /// - format: An optional modifier describing the expected format of the integer. Currently the @@ -296,13 +306,14 @@ public final class Schema: Sendable { /// equal to the specified minimum. /// - maximum: If specified, instructs the model that the value should be less than or equal /// to the specified maximum. - public static func integer(description: String? = nil, nullable: Bool = false, - format: IntegerFormat? = nil, + public static func integer(description: String? = nil, title: String? = nil, + nullable: Bool = false, format: IntegerFormat? = nil, minimum: Int? = nil, maximum: Int? = nil) -> Schema { return self.init( type: .integer, format: format?.rawValue, description: description, + title: title, nullable: nullable.self, minimum: minimum.map { Double($0) }, maximum: maximum.map { Double($0) } @@ -317,10 +328,12 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the boolean should contain or represent; may /// use Markdown format. + /// - title: An optional human-readable name/summary for the object schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of a boolean; /// defaults to `false`, enforcing that a boolean is returned. - public static func boolean(description: String? = nil, nullable: Bool = false) -> Schema { - return self.init(type: .boolean, description: description, nullable: nullable) + public static func boolean(description: String? = nil, title: String? = nil, + nullable: Bool = false) -> Schema { + return self.init(type: .boolean, description: description, title: title, nullable: nullable) } /// Returns a `Schema` representing an array. @@ -334,17 +347,20 @@ public final class Schema: Sendable { /// - items: The `Schema` of the elements that the array will hold. /// - description: An optional description of what the array should contain or represent; may /// use Markdown format. + /// - title: An optional human-readable name/summary for the object schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of an array; /// defaults to `false`, enforcing that an array is returned. /// - minItems: Instructs the model to produce at least the specified minimum number of elements /// in the array; defaults to `nil`, meaning any number. /// - maxItems: Instructs the model to produce at most the specified maximum number of elements /// in the array. - public static func array(items: Schema, description: String? = nil, nullable: Bool = false, - minItems: Int? = nil, maxItems: Int? = nil) -> Schema { + public static func array(items: Schema, description: String? = nil, title: String? = nil, + nullable: Bool = false, minItems: Int? = nil, + maxItems: Int? = nil) -> Schema { return self.init( type: .array, description: description, + title: title, nullable: nullable, items: items, minItems: minItems, From 7f51977a1f6bcee4f90b391b2265fc078799a9cd Mon Sep 17 00:00:00 2001 From: SeanChinJunKai Date: Fri, 13 Jun 2025 11:09:35 +0800 Subject: [PATCH 2/6] Update comments --- FirebaseAI/Sources/Types/Public/Schema.swift | 16 ++++++++-------- Package.swift | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/FirebaseAI/Sources/Types/Public/Schema.swift b/FirebaseAI/Sources/Types/Public/Schema.swift index 47316ed1a7c..b8b2ba6c16e 100644 --- a/FirebaseAI/Sources/Types/Public/Schema.swift +++ b/FirebaseAI/Sources/Types/Public/Schema.swift @@ -167,7 +167,7 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the string should contain or represent; may /// use Markdown format. - /// - title: An optional human-readable name/summary for the object schema. + /// - title: An optional human-readable name/summary for the schema. /// - nullable: If `true`, instructs the model that it *may* generate `null` instead of a /// string; defaults to `false`, enforcing that a string value is generated. /// - format: An optional modifier describing the expected format of the string. Currently no @@ -204,7 +204,7 @@ public final class Schema: Sendable { /// - values: The list of string values that may be generated by the model. /// - description: An optional description of what the `values` contain or represent; may use /// Markdown format. - /// - title: An optional human-readable name/summary for the object schema. + /// - title: An optional human-readable name/summary for the schema. /// - nullable: If `true`, instructs the model that it *may* generate `null` instead of one of /// the strings specified in `values`; defaults to `false`, enforcing that one of the string /// values is generated. @@ -233,7 +233,7 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the number should contain or represent; may /// use Markdown format. - /// - title: An optional human-readable name/summary for the object schema. + /// - title: An optional human-readable name/summary for the schema. /// - nullable: If `true`, instructs the model that it may generate `null` instead of a number; /// defaults to `false`, enforcing that a number is generated. /// - minimum: If specified, instructs the model that the value should be greater than or @@ -261,7 +261,7 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the number should contain or represent; may /// use Markdown format. - /// - title: An optional human-readable name/summary for the object schema. + /// - title: An optional human-readable name/summary for the schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of a number; /// defaults to `false`, enforcing that a number is returned. /// - minimum: If specified, instructs the model that the value should be greater than or @@ -296,7 +296,7 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the integer should contain or represent; may /// use Markdown format. - /// - title: An optional human-readable name/summary for the object schema. + /// - title: An optional human-readable name/summary for the schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of an integer; /// defaults to `false`, enforcing that an integer is returned. /// - format: An optional modifier describing the expected format of the integer. Currently the @@ -328,7 +328,7 @@ public final class Schema: Sendable { /// - Parameters: /// - description: An optional description of what the boolean should contain or represent; may /// use Markdown format. - /// - title: An optional human-readable name/summary for the object schema. + /// - title: An optional human-readable name/summary for the schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of a boolean; /// defaults to `false`, enforcing that a boolean is returned. public static func boolean(description: String? = nil, title: String? = nil, @@ -347,7 +347,7 @@ public final class Schema: Sendable { /// - items: The `Schema` of the elements that the array will hold. /// - description: An optional description of what the array should contain or represent; may /// use Markdown format. - /// - title: An optional human-readable name/summary for the object schema. + /// - title: An optional human-readable name/summary for the schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of an array; /// defaults to `false`, enforcing that an array is returned. /// - minItems: Instructs the model to produce at least the specified minimum number of elements @@ -400,7 +400,7 @@ public final class Schema: Sendable { /// generated JSON string. See ``propertyOrdering`` for details. /// - description: An optional description of what the object should contain or represent; may /// use Markdown format. - /// - title: An optional human-readable name/summary for the object schema. + /// - title: An optional human-readable name/summary for the schema. /// - nullable: If `true`, instructs the model that it may return `null` instead of an object; /// defaults to `false`, enforcing that an object is returned. public static func object(properties: [String: Schema], optionalProperties: [String] = [], diff --git a/Package.swift b/Package.swift index 1a46f5a94ab..f9bc487f2e7 100644 --- a/Package.swift +++ b/Package.swift @@ -1445,7 +1445,7 @@ func googleAppMeasurementDependency() -> Package.Dependency { return .package(url: appMeasurementURL, branch: "main") } - return .package(url: appMeasurementURL, exact: "11.14.0") + return .package(url: appMeasurementURL, branch: "main") } func abseilDependency() -> Package.Dependency { From 60419574ecabaa2f6354f81757b880f02d72fe3e Mon Sep 17 00:00:00 2001 From: SeanChinJunKai Date: Fri, 13 Jun 2025 12:09:28 +0800 Subject: [PATCH 3/6] Update unit tests --- FirebaseAI/Tests/Unit/Types/SchemaTests.swift | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/FirebaseAI/Tests/Unit/Types/SchemaTests.swift b/FirebaseAI/Tests/Unit/Types/SchemaTests.swift index e6eb839a39a..19290a2784b 100644 --- a/FirebaseAI/Tests/Unit/Types/SchemaTests.swift +++ b/FirebaseAI/Tests/Unit/Types/SchemaTests.swift @@ -43,14 +43,16 @@ final class SchemaTests: XCTestCase { func testEncodeSchema_string_allOptions() throws { let description = "Timestamp of the event." + let title = "Event Timestamp" let format = Schema.StringFormat.custom("date-time") - let schema = Schema.string(description: description, nullable: true, format: format) + let schema = Schema.string(description: description, title: title, nullable: true, format: format) let jsonData = try encoder.encode(schema) let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { + "title": "\(title)", "description" : "\(description)", "format" : "date-time", "nullable" : true, @@ -85,13 +87,15 @@ final class SchemaTests: XCTestCase { func testEncodeSchema_enumeration_allOptions() throws { let values = ["NORTH", "SOUTH", "EAST", "WEST"] let description = "Compass directions." - let schema = Schema.enumeration(values: values, description: description, nullable: true) + let title = "Directions" + let schema = Schema.enumeration(values: values, description: description, title: title, nullable: true) let jsonData = try encoder.encode(schema) let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { + "title": "\(title)", "description" : "\(description)", "enum" : [ "NORTH", @@ -125,10 +129,12 @@ final class SchemaTests: XCTestCase { func testEncodeSchema_float_allOptions() throws { let description = "Temperature in Celsius." + let title = "Temperature (°C)" let minimum: Float = -40.25 let maximum: Float = 50.5 let schema = Schema.float( description: description, + title: title, nullable: true, minimum: minimum, maximum: maximum @@ -139,6 +145,7 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { + "title": "\(title)", "description" : "\(description)", "format" : "float", "maximum" : \(maximum), @@ -167,10 +174,12 @@ final class SchemaTests: XCTestCase { func testEncodeSchema_double_allOptions() throws { let description = "Account balance." + let title = "Balance" let minimum = 0.01 let maximum = 1_000_000.99 let schema = Schema.double( description: description, + title: title, nullable: true, minimum: minimum, maximum: maximum @@ -181,6 +190,7 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { + "title": "\(title)", "description" : "\(description)", "maximum" : \(maximum), "minimum" : \(minimum), @@ -208,11 +218,13 @@ final class SchemaTests: XCTestCase { func testEncodeSchema_integer_allOptions() throws { let description = "User age." + let title = "Age" let minimum = 0 let maximum = 120 let format = Schema.IntegerFormat.int32 let schema = Schema.integer( description: description, + title: title, nullable: true, format: format, minimum: minimum, @@ -224,6 +236,7 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { + "title": "\(title)", "description" : "\(description)", "format" : "int32", "maximum" : \(maximum), @@ -252,13 +265,15 @@ final class SchemaTests: XCTestCase { func testEncodeSchema_boolean_allOptions() throws { let description = "Is the user an administrator?" - let schema = Schema.boolean(description: description, nullable: true) + let title = "Administrator Check" + let schema = Schema.boolean(description: description, title: title, nullable: true) let jsonData = try encoder.encode(schema) let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { + "title": "\(title)", "description" : "\(description)", "nullable" : true, "type" : "BOOLEAN" @@ -290,11 +305,13 @@ final class SchemaTests: XCTestCase { func testEncodeSchema_array_allOptions() throws { let itemsSchema = Schema.integer(format: .int64) let description = "List of product IDs." + let title = "Product IDs" let minItems = 1 let maxItems = 10 let schema = Schema.array( items: itemsSchema, description: description, + title: title, nullable: true, minItems: minItems, maxItems: maxItems @@ -305,6 +322,7 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { + "title": "\(title)", "description" : "\(description)", "items" : { "format" : "int64", From 92ac29a550306149345b570a819de93f899cc46d Mon Sep 17 00:00:00 2001 From: SeanChinJunKai Date: Sat, 14 Jun 2025 00:06:16 +0800 Subject: [PATCH 4/6] Fix style check failing --- FirebaseAI/Tests/Unit/Types/SchemaTests.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/FirebaseAI/Tests/Unit/Types/SchemaTests.swift b/FirebaseAI/Tests/Unit/Types/SchemaTests.swift index 19290a2784b..a1d0b94b1a8 100644 --- a/FirebaseAI/Tests/Unit/Types/SchemaTests.swift +++ b/FirebaseAI/Tests/Unit/Types/SchemaTests.swift @@ -45,7 +45,12 @@ final class SchemaTests: XCTestCase { let description = "Timestamp of the event." let title = "Event Timestamp" let format = Schema.StringFormat.custom("date-time") - let schema = Schema.string(description: description, title: title, nullable: true, format: format) + let schema = Schema.string( + description: description, + title: title, + nullable: true, + format: format + ) let jsonData = try encoder.encode(schema) @@ -88,7 +93,12 @@ final class SchemaTests: XCTestCase { let values = ["NORTH", "SOUTH", "EAST", "WEST"] let description = "Compass directions." let title = "Directions" - let schema = Schema.enumeration(values: values, description: description, title: title, nullable: true) + let schema = Schema.enumeration( + values: values, + description: description, + title: title, + nullable: true + ) let jsonData = try encoder.encode(schema) From 93ae699766a90112897bec35b3b28363f0d3e347 Mon Sep 17 00:00:00 2001 From: SeanChinJunKai Date: Sat, 14 Jun 2025 00:19:29 +0800 Subject: [PATCH 5/6] Fix failing unit test due to wrong key ordering --- FirebaseAI/Tests/Unit/Types/SchemaTests.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/FirebaseAI/Tests/Unit/Types/SchemaTests.swift b/FirebaseAI/Tests/Unit/Types/SchemaTests.swift index a1d0b94b1a8..bd8d595dd24 100644 --- a/FirebaseAI/Tests/Unit/Types/SchemaTests.swift +++ b/FirebaseAI/Tests/Unit/Types/SchemaTests.swift @@ -57,10 +57,10 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { - "title": "\(title)", "description" : "\(description)", "format" : "date-time", "nullable" : true, + "title": "\(title)", "type" : "STRING" } """) @@ -105,7 +105,6 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { - "title": "\(title)", "description" : "\(description)", "enum" : [ "NORTH", @@ -115,6 +114,7 @@ final class SchemaTests: XCTestCase { ], "format" : "enum", "nullable" : true, + "title": "\(title)", "type" : "STRING" } """) @@ -155,12 +155,12 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { - "title": "\(title)", "description" : "\(description)", "format" : "float", "maximum" : \(maximum), "minimum" : \(minimum), "nullable" : true, + "title": "\(title)", "type" : "NUMBER" } """) @@ -200,11 +200,11 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { - "title": "\(title)", "description" : "\(description)", "maximum" : \(maximum), "minimum" : \(minimum), "nullable" : true, + "title": "\(title)", "type" : "NUMBER" } """) @@ -246,12 +246,12 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { - "title": "\(title)", "description" : "\(description)", "format" : "int32", "maximum" : \(maximum), "minimum" : \(minimum), "nullable" : true, + "title": "\(title)", "type" : "INTEGER" } """) @@ -283,9 +283,9 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { - "title": "\(title)", "description" : "\(description)", "nullable" : true, + "title": "\(title)", "type" : "BOOLEAN" } """) @@ -332,7 +332,6 @@ final class SchemaTests: XCTestCase { let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8)) XCTAssertEqual(json, """ { - "title": "\(title)", "description" : "\(description)", "items" : { "format" : "int64", @@ -342,6 +341,7 @@ final class SchemaTests: XCTestCase { "maxItems" : \(maxItems), "minItems" : \(minItems), "nullable" : true, + "title": "\(title)", "type" : "ARRAY" } """) From d5c8e6ef0bd55f726a550badbbdcb0eb48ff7c47 Mon Sep 17 00:00:00 2001 From: SeanChinJunKai Date: Sat, 14 Jun 2025 00:31:31 +0800 Subject: [PATCH 6/6] Revert Package.swift --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index f9bc487f2e7..1a46f5a94ab 100644 --- a/Package.swift +++ b/Package.swift @@ -1445,7 +1445,7 @@ func googleAppMeasurementDependency() -> Package.Dependency { return .package(url: appMeasurementURL, branch: "main") } - return .package(url: appMeasurementURL, branch: "main") + return .package(url: appMeasurementURL, exact: "11.14.0") } func abseilDependency() -> Package.Dependency {