Skip to content

Commit a7d5c3e

Browse files
paulb777gemini-code-assist[bot]andrewheard
authored
[AI] Nullable support in structured output and more integration tests (#15752)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Andrew Heard <andrewheard@google.com>
1 parent 5b40312 commit a7d5c3e

File tree

4 files changed

+499
-0
lines changed

4 files changed

+499
-0
lines changed

FirebaseAI/Sources/Protocols/Public/StructuredOutput/FirebaseGenerable.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ extension Optional: ConvertibleToModelOutput where Wrapped: ConvertibleToModelOu
4040
}
4141
}
4242

43+
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
44+
extension Optional: ConvertibleFromModelOutput where Wrapped: ConvertibleFromModelOutput {
45+
public init(_ content: ModelOutput) throws {
46+
if case .null = content.kind {
47+
self = nil
48+
return
49+
}
50+
self = try Wrapped(content)
51+
}
52+
}
53+
4354
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
4455
extension Bool: FirebaseGenerable {
4556
public static var jsonSchema: JSONSchema {

FirebaseAI/Sources/Types/Public/StructuredOutput/ModelOutput.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ public struct ModelOutput: Sendable, CustomDebugStringConvertible, FirebaseGener
120120
return nil
121121
}
122122

123+
if case .null = value.kind {
124+
return nil
125+
}
126+
123127
return try Value(value)
124128
}
125129
}

FirebaseAI/Tests/TestApp/Packages/FirebaseAILogicExtended/Tests/FirebaseGenerableMacroTests/FirebaseGenerableTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ final class FirebaseAILogicMacrosTests: XCTestCase {
3636
@FirebaseGenerable
3737
struct Person {
3838
let firstName: String
39+
let middleName: String?
3940
let lastName: String
4041
let age: Int
4142
}
4243
""",
4344
expandedSource: """
4445
struct Person {
4546
let firstName: String
47+
let middleName: String?
4648
let lastName: String
4749
let age: Int
4850
@@ -51,6 +53,7 @@ final class FirebaseAILogicMacrosTests: XCTestCase {
5153
type: Self.self,
5254
properties: [
5355
FirebaseAILogic.JSONSchema.Property(name: "firstName", type: String.self),
56+
FirebaseAILogic.JSONSchema.Property(name: "middleName", type: String?.self),
5457
FirebaseAILogic.JSONSchema.Property(name: "lastName", type: String.self),
5558
FirebaseAILogic.JSONSchema.Property(name: "age", type: Int.self)
5659
]
@@ -60,6 +63,7 @@ final class FirebaseAILogicMacrosTests: XCTestCase {
6063
nonisolated var modelOutput: FirebaseAILogic.ModelOutput {
6164
var properties = [(name: String, value: any ConvertibleToModelOutput)]()
6265
addProperty(name: "firstName", value: self.firstName)
66+
addProperty(name: "middleName", value: self.middleName)
6367
addProperty(name: "lastName", value: self.lastName)
6468
addProperty(name: "age", value: self.age)
6569
return ModelOutput(
@@ -81,11 +85,13 @@ final class FirebaseAILogicMacrosTests: XCTestCase {
8185
nonisolated struct Partial: Identifiable, FirebaseAILogic.ConvertibleFromModelOutput {
8286
var id: FirebaseAILogic.ResponseID
8387
var firstName: String.Partial?
88+
var middleName: String?.Partial?
8489
var lastName: String.Partial?
8590
var age: Int.Partial?
8691
nonisolated init(_ content: FirebaseAILogic.ModelOutput) throws {
8792
self.id = content.id ?? FirebaseAILogic.ResponseID()
8893
self.firstName = try content.value(forProperty: "firstName")
94+
self.middleName = try content.value(forProperty: "middleName")
8995
self.lastName = try content.value(forProperty: "lastName")
9096
self.age = try content.value(forProperty: "age")
9197
}
@@ -96,6 +102,7 @@ final class FirebaseAILogicMacrosTests: XCTestCase {
96102
extension Person: FirebaseAILogic.FirebaseGenerable {
97103
nonisolated init(_ content: FirebaseAILogic.ModelOutput) throws {
98104
self.firstName = try content.value(forProperty: "firstName")
105+
self.middleName = try content.value(forProperty: "middleName")
99106
self.lastName = try content.value(forProperty: "lastName")
100107
self.age = try content.value(forProperty: "age")
101108
}

0 commit comments

Comments
 (0)