Skip to content

Commit 3a4c311

Browse files
[Firebase AI] Add includeSafetyAttributes for Imagen requests (#15191)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 5caaeea commit 3a4c311

File tree

7 files changed

+62
-11
lines changed

7 files changed

+62
-11
lines changed

.github/workflows/firebaseai.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,17 @@ jobs:
8282
setup_command: scripts/update_vertexai_responses.sh
8383

8484
quickstart:
85-
runs-on: macos-15
85+
strategy:
86+
matrix:
87+
include:
88+
- os: macos-15
89+
xcode: Xcode_16.4
90+
runs-on: ${{ matrix.os }}
8691
env:
8792
BRANCH_NAME: ${{ github.head_ref || github.ref_name || 'main' }}
8893
steps:
8994
- uses: actions/checkout@v4
95+
- name: Xcode
96+
run: sudo xcode-select -s /Applications/${{ matrix.xcode }}.app/Contents/Developer
9097
- name: Build Quickstart
9198
run: scripts/quickstart_build_spm.sh FirebaseAI

FirebaseAI/Sources/Types/Internal/Imagen/ImageGenerationParameters.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct ImageGenerationParameters {
2323
let outputOptions: ImageGenerationOutputOptions?
2424
let addWatermark: Bool?
2525
let includeResponsibleAIFilterReason: Bool?
26+
let includeSafetyAttributes: Bool?
2627
}
2728

2829
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
@@ -42,6 +43,7 @@ extension ImageGenerationParameters: Encodable {
4243
case outputOptions
4344
case addWatermark
4445
case includeResponsibleAIFilterReason = "includeRaiReason"
46+
case includeSafetyAttributes
4547
}
4648

4749
func encode(to encoder: any Encoder) throws {
@@ -58,5 +60,6 @@ extension ImageGenerationParameters: Encodable {
5860
includeResponsibleAIFilterReason,
5961
forKey: .includeResponsibleAIFilterReason
6062
)
63+
try container.encodeIfPresent(includeSafetyAttributes, forKey: .includeSafetyAttributes)
6164
}
6265
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
17+
/// A `safetyAttributes` "prediction" from Imagen.
18+
///
19+
/// This prediction is currently unused by the SDK and is only checked to be valid JSON. This type
20+
/// is currently only used to avoid logging unsupported prediction types.
21+
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
22+
struct ImagenSafetyAttributes: Decodable {
23+
let safetyAttributes: JSONObject
24+
}

FirebaseAI/Sources/Types/Public/Imagen/ImagenGenerationResponse.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ extension ImagenGenerationResponse: Decodable where T: Decodable {
6060
images.append(image)
6161
} else if let filteredReason = try? predictionsContainer.decode(RAIFilteredReason.self) {
6262
filteredReasons.append(filteredReason.raiFilteredReason)
63+
} else if let _ = try? predictionsContainer.decode(ImagenSafetyAttributes.self) {
64+
// Ignore SafetyAttributes "prediction" to avoid logging in `unsupportedPrediction` below.
6365
} else if let unsupportedPrediction = try? predictionsContainer.decode(JSONObject.self) {
6466
AILog.warning(
6567
code: .decodedUnsupportedImagenPredictionType,

FirebaseAI/Sources/Types/Public/Imagen/ImagenModel.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ public final class ImagenModel {
159159
)
160160
},
161161
addWatermark: generationConfig?.addWatermark,
162-
includeResponsibleAIFilterReason: true
162+
includeResponsibleAIFilterReason: true,
163+
includeSafetyAttributes: true
163164
)
164165
}
165166
}

FirebaseAI/Tests/Unit/Types/Imagen/ImageGenerationParametersTests.swift

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ final class ImageGenerationParametersTests: XCTestCase {
3434
personGeneration: nil,
3535
outputOptions: nil,
3636
addWatermark: nil,
37-
includeResponsibleAIFilterReason: true
37+
includeResponsibleAIFilterReason: true,
38+
includeSafetyAttributes: true
3839
)
3940

4041
let parameters = ImagenModel.imageGenerationParameters(
@@ -57,7 +58,8 @@ final class ImageGenerationParametersTests: XCTestCase {
5758
personGeneration: nil,
5859
outputOptions: nil,
5960
addWatermark: nil,
60-
includeResponsibleAIFilterReason: true
61+
includeResponsibleAIFilterReason: true,
62+
includeSafetyAttributes: true
6163
)
6264

6365
let parameters = ImagenModel.imageGenerationParameters(
@@ -95,7 +97,8 @@ final class ImageGenerationParametersTests: XCTestCase {
9597
compressionQuality: imageFormat.compressionQuality
9698
),
9799
addWatermark: addWatermark,
98-
includeResponsibleAIFilterReason: true
100+
includeResponsibleAIFilterReason: true,
101+
includeSafetyAttributes: true
99102
)
100103

101104
let parameters = ImagenModel.imageGenerationParameters(
@@ -124,7 +127,8 @@ final class ImageGenerationParametersTests: XCTestCase {
124127
personGeneration: personFilterLevel.rawValue,
125128
outputOptions: nil,
126129
addWatermark: nil,
127-
includeResponsibleAIFilterReason: true
130+
includeResponsibleAIFilterReason: true,
131+
includeSafetyAttributes: true
128132
)
129133

130134
let parameters = ImagenModel.imageGenerationParameters(
@@ -170,7 +174,8 @@ final class ImageGenerationParametersTests: XCTestCase {
170174
compressionQuality: imageFormat.compressionQuality
171175
),
172176
addWatermark: addWatermark,
173-
includeResponsibleAIFilterReason: true
177+
includeResponsibleAIFilterReason: true,
178+
includeSafetyAttributes: true
174179
)
175180

176181
let parameters = ImagenModel.imageGenerationParameters(
@@ -200,6 +205,7 @@ final class ImageGenerationParametersTests: XCTestCase {
200205
let outputOptions = ImageGenerationOutputOptions(mimeType: mimeType, compressionQuality: nil)
201206
let addWatermark = false
202207
let includeRAIReason = true
208+
let includeSafetyAttributes = true
203209
let parameters = ImageGenerationParameters(
204210
sampleCount: sampleCount,
205211
storageURI: storageURI,
@@ -209,7 +215,8 @@ final class ImageGenerationParametersTests: XCTestCase {
209215
personGeneration: personGeneration,
210216
outputOptions: outputOptions,
211217
addWatermark: addWatermark,
212-
includeResponsibleAIFilterReason: includeRAIReason
218+
includeResponsibleAIFilterReason: includeRAIReason,
219+
includeSafetyAttributes: includeSafetyAttributes
213220
)
214221

215222
let jsonData = try encoder.encode(parameters)
@@ -220,6 +227,7 @@ final class ImageGenerationParametersTests: XCTestCase {
220227
"addWatermark" : \(addWatermark),
221228
"aspectRatio" : "\(aspectRatio)",
222229
"includeRaiReason" : \(includeRAIReason),
230+
"includeSafetyAttributes" : \(includeSafetyAttributes),
223231
"negativePrompt" : "\(negativePrompt)",
224232
"outputOptions" : {
225233
"mimeType" : "\(mimeType)"
@@ -246,7 +254,8 @@ final class ImageGenerationParametersTests: XCTestCase {
246254
personGeneration: nil,
247255
outputOptions: nil,
248256
addWatermark: addWatermark,
249-
includeResponsibleAIFilterReason: nil
257+
includeResponsibleAIFilterReason: nil,
258+
includeSafetyAttributes: nil
250259
)
251260

252261
let jsonData = try encoder.encode(parameters)
@@ -272,7 +281,8 @@ final class ImageGenerationParametersTests: XCTestCase {
272281
personGeneration: nil,
273282
outputOptions: nil,
274283
addWatermark: nil,
275-
includeResponsibleAIFilterReason: nil
284+
includeResponsibleAIFilterReason: nil,
285+
includeSafetyAttributes: nil
276286
)
277287

278288
let jsonData = try encoder.encode(parameters)

FirebaseAI/Tests/Unit/Types/Imagen/ImagenGenerationRequestTests.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ final class ImagenGenerationRequestTests: XCTestCase {
2525
let aspectRatio = "16:9"
2626
let safetyFilterLevel = "block_low_and_above"
2727
let includeResponsibleAIFilterReason = true
28+
let includeSafetyAttributes = true
2829
lazy var parameters = ImageGenerationParameters(
2930
sampleCount: sampleCount,
3031
storageURI: nil,
@@ -34,7 +35,8 @@ final class ImagenGenerationRequestTests: XCTestCase {
3435
personGeneration: nil,
3536
outputOptions: nil,
3637
addWatermark: nil,
37-
includeResponsibleAIFilterReason: includeResponsibleAIFilterReason
38+
includeResponsibleAIFilterReason: includeResponsibleAIFilterReason,
39+
includeSafetyAttributes: includeSafetyAttributes
3840
)
3941
let apiConfig = FirebaseAI.defaultVertexAIAPIConfig
4042

@@ -108,6 +110,7 @@ final class ImagenGenerationRequestTests: XCTestCase {
108110
"parameters" : {
109111
"aspectRatio" : "\(aspectRatio)",
110112
"includeRaiReason" : \(includeResponsibleAIFilterReason),
113+
"includeSafetyAttributes" : \(includeSafetyAttributes),
111114
"safetySetting" : "\(safetyFilterLevel)",
112115
"sampleCount" : \(sampleCount)
113116
}
@@ -137,6 +140,7 @@ final class ImagenGenerationRequestTests: XCTestCase {
137140
"parameters" : {
138141
"aspectRatio" : "\(aspectRatio)",
139142
"includeRaiReason" : \(includeResponsibleAIFilterReason),
143+
"includeSafetyAttributes" : \(includeSafetyAttributes),
140144
"safetySetting" : "\(safetyFilterLevel)",
141145
"sampleCount" : \(sampleCount)
142146
}

0 commit comments

Comments
 (0)