Skip to content

Commit 879f636

Browse files
authored
Merge branch 'main' into rl.vertex.check.update
2 parents fb493bf + 24dd7c4 commit 879f636

File tree

11 files changed

+144
-32
lines changed

11 files changed

+144
-32
lines changed

firebase-ai/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Unreleased
2+
* [fixed] **Breaking Change**: Fixed missing builder methods and return types in builders.
23
* [changed] **Breaking Change**: `LiveModelFutures.connect` now returns `ListenableFuture<LiveSessionFutures>` instead of `ListenableFuture<LiveSession>`.
34
* **Action Required:** Remove any transformations from LiveSession object to LiveSessionFutures object.
45
* **Action Required:** Change type of variable handling `LiveModelFutures.connect` to `ListenableFuture<LiveSessionsFutures>`

firebase-ai/api.txt

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,10 @@ package com.google.firebase.ai.type {
216216
method public <T extends com.google.firebase.ai.type.Part> com.google.firebase.ai.type.Content.Builder addPart(T data);
217217
method public com.google.firebase.ai.type.Content.Builder addText(String text);
218218
method public com.google.firebase.ai.type.Content build();
219-
method public java.util.List<com.google.firebase.ai.type.Part> getParts();
220-
method public String? getRole();
221-
method public void setParts(java.util.List<com.google.firebase.ai.type.Part>);
222-
method public void setRole(String?);
223-
property public final java.util.List<com.google.firebase.ai.type.Part> parts;
224-
property public final String? role;
219+
method public com.google.firebase.ai.type.Content.Builder setParts(java.util.List<com.google.firebase.ai.type.Part> parts);
220+
method public com.google.firebase.ai.type.Content.Builder setRole(String? role);
221+
field public java.util.List<com.google.firebase.ai.type.Part> parts;
222+
field public String? role;
225223
}
226224

227225
public final class ContentBlockedException extends com.google.firebase.ai.type.FirebaseAIException {
@@ -355,6 +353,17 @@ package com.google.firebase.ai.type {
355353
public static final class GenerationConfig.Builder {
356354
ctor public GenerationConfig.Builder();
357355
method public com.google.firebase.ai.type.GenerationConfig build();
356+
method public com.google.firebase.ai.type.GenerationConfig.Builder setCandidateCount(Integer? candidateCount);
357+
method public com.google.firebase.ai.type.GenerationConfig.Builder setFrequencyPenalty(Float? frequencyPenalty);
358+
method public com.google.firebase.ai.type.GenerationConfig.Builder setMaxOutputTokens(Integer? maxOutputTokens);
359+
method public com.google.firebase.ai.type.GenerationConfig.Builder setPresencePenalty(Float? presencePenalty);
360+
method public com.google.firebase.ai.type.GenerationConfig.Builder setResponseMimeType(String? responseMimeType);
361+
method public com.google.firebase.ai.type.GenerationConfig.Builder setResponseModalities(java.util.List<com.google.firebase.ai.type.ResponseModality>? responseModalities);
362+
method public com.google.firebase.ai.type.GenerationConfig.Builder setResponseSchema(com.google.firebase.ai.type.Schema? responseSchema);
363+
method public com.google.firebase.ai.type.GenerationConfig.Builder setStopSequences(java.util.List<java.lang.String>? stopSequences);
364+
method public com.google.firebase.ai.type.GenerationConfig.Builder setTemperature(Float? temperature);
365+
method public com.google.firebase.ai.type.GenerationConfig.Builder setTopK(Integer? topK);
366+
method public com.google.firebase.ai.type.GenerationConfig.Builder setTopP(Float? topP);
358367
field public Integer? candidateCount;
359368
field public Float? frequencyPenalty;
360369
field public Integer? maxOutputTokens;

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Candidate.kt

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ internal constructor(
5151
val groundingMetadata: GroundingMetadata? = null,
5252
) {
5353
internal fun toPublic(): Candidate {
54-
val safetyRatings = safetyRatings?.map { it.toPublic() }.orEmpty()
54+
val safetyRatings = safetyRatings?.mapNotNull { it.toPublic() }.orEmpty()
5555
val citations = citationMetadata?.toPublic()
5656
val finishReason = finishReason?.toPublic()
5757

@@ -120,23 +120,31 @@ internal constructor(
120120
internal data class Internal
121121
@JvmOverloads
122122
constructor(
123-
val category: HarmCategory.Internal,
124-
val probability: HarmProbability.Internal,
123+
val category: HarmCategory.Internal? = null,
124+
val probability: HarmProbability.Internal? = null,
125125
val blocked: Boolean? = null, // TODO(): any reason not to default to false?
126126
val probabilityScore: Float? = null,
127127
val severity: HarmSeverity.Internal? = null,
128128
val severityScore: Float? = null,
129129
) {
130130

131131
internal fun toPublic() =
132-
SafetyRating(
133-
category = category.toPublic(),
134-
probability = probability.toPublic(),
135-
probabilityScore = probabilityScore ?: 0f,
136-
blocked = blocked,
137-
severity = severity?.toPublic(),
138-
severityScore = severityScore
139-
)
132+
// Due to a bug in the backend, it's possible that we receive
133+
// an invalid `SafetyRating` value, without either category or
134+
// probability. We return null in those cases to enable
135+
// filtering by the higher level types.
136+
if (category == null || probability == null) {
137+
null
138+
} else {
139+
SafetyRating(
140+
category = category.toPublic(),
141+
probability = probability.toPublic(),
142+
probabilityScore = probabilityScore ?: 0f,
143+
blocked = blocked,
144+
severity = severity?.toPublic(),
145+
severityScore = severityScore
146+
)
147+
}
140148
}
141149
}
142150

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Content.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,17 @@ constructor(public val role: String? = "user", public val parts: List<Part>) {
4444
public class Builder {
4545

4646
/** The producer of the content. Must be either 'user' or 'model'. By default, it's "user". */
47-
public var role: String? = "user"
47+
@JvmField public var role: String? = "user"
4848

4949
/**
5050
* The mutable list of [Part]s comprising the [Content].
5151
*
5252
* Prefer using the provided helper methods over modifying this list directly.
5353
*/
54-
public var parts: MutableList<Part> = arrayListOf()
54+
@JvmField public var parts: MutableList<Part> = arrayListOf()
55+
56+
public fun setRole(role: String?): Content.Builder = apply { this.role = role }
57+
public fun setParts(parts: MutableList<Part>): Content.Builder = apply { this.parts = parts }
5558

5659
/** Adds a new [Part] to [parts]. */
5760
@JvmName("addPart")

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerationConfig.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,36 @@ private constructor(
136136
@JvmField public var responseSchema: Schema? = null
137137
@JvmField public var responseModalities: List<ResponseModality>? = null
138138

139+
public fun setTemperature(temperature: Float?): Builder = apply {
140+
this.temperature = temperature
141+
}
142+
public fun setTopK(topK: Int?): Builder = apply { this.topK = topK }
143+
public fun setTopP(topP: Float?): Builder = apply { this.topP = topP }
144+
public fun setCandidateCount(candidateCount: Int?): Builder = apply {
145+
this.candidateCount = candidateCount
146+
}
147+
public fun setMaxOutputTokens(maxOutputTokens: Int?): Builder = apply {
148+
this.maxOutputTokens = maxOutputTokens
149+
}
150+
public fun setPresencePenalty(presencePenalty: Float?): Builder = apply {
151+
this.presencePenalty = presencePenalty
152+
}
153+
public fun setFrequencyPenalty(frequencyPenalty: Float?): Builder = apply {
154+
this.frequencyPenalty = frequencyPenalty
155+
}
156+
public fun setStopSequences(stopSequences: List<String>?): Builder = apply {
157+
this.stopSequences = stopSequences
158+
}
159+
public fun setResponseMimeType(responseMimeType: String?): Builder = apply {
160+
this.responseMimeType = responseMimeType
161+
}
162+
public fun setResponseSchema(responseSchema: Schema?): Builder = apply {
163+
this.responseSchema = responseSchema
164+
}
165+
public fun setResponseModalities(responseModalities: List<ResponseModality>?): Builder = apply {
166+
this.responseModalities = responseModalities
167+
}
168+
139169
/** Create a new [GenerationConfig] with the attached arguments. */
140170
public fun build(): GenerationConfig =
141171
GenerationConfig(

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/PromptFeedback.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class PromptFeedback(
4242
) {
4343

4444
internal fun toPublic(): PromptFeedback {
45-
val safetyRatings = safetyRatings?.map { it.toPublic() }.orEmpty()
45+
val safetyRatings = safetyRatings?.mapNotNull { it.toPublic() }.orEmpty()
4646
return PromptFeedback(blockReason?.toPublic(), safetyRatings, blockReasonMessage)
4747
}
4848
}

firebase-ai/src/test/java/com/google/firebase/ai/VertexAIStreamingSnapshotTests.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ import kotlinx.coroutines.flow.collect
3636
import kotlinx.coroutines.flow.toList
3737
import kotlinx.coroutines.withTimeout
3838
import org.junit.Test
39+
import org.junit.runner.RunWith
40+
import org.robolectric.RobolectricTestRunner
3941

42+
@RunWith(RobolectricTestRunner::class)
4043
internal class VertexAIStreamingSnapshotTests {
4144
private val testTimeout = 5.seconds
4245

@@ -85,6 +88,18 @@ internal class VertexAIStreamingSnapshotTests {
8588
}
8689
}
8790

91+
@Test
92+
fun `invalid safety ratings during image generation`() =
93+
goldenVertexStreamingFile("streaming-success-image-invalid-safety-ratings.txt") {
94+
val responses = model.generateContentStream("prompt")
95+
96+
withTimeout(testTimeout) {
97+
val responseList = responses.toList()
98+
99+
responseList.isEmpty() shouldBe false
100+
}
101+
}
102+
88103
@Test
89104
fun `unknown enum in finish reason`() =
90105
goldenVertexStreamingFile("streaming-failure-unknown-finish-enum.txt") {

firebase-ai/src/test/java/com/google/firebase/ai/VertexAIUnarySnapshotTests.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,11 @@ import kotlinx.serialization.json.jsonObject
5555
import kotlinx.serialization.json.jsonPrimitive
5656
import org.json.JSONArray
5757
import org.junit.Test
58+
import org.junit.runner.RunWith
59+
import org.robolectric.RobolectricTestRunner
5860

5961
@OptIn(PublicPreviewAPI::class)
62+
@RunWith(RobolectricTestRunner::class)
6063
internal class VertexAIUnarySnapshotTests {
6164
private val testTimeout = 5.seconds
6265

@@ -125,6 +128,16 @@ internal class VertexAIUnarySnapshotTests {
125128
}
126129
}
127130

131+
@Test
132+
fun `invalid safety ratings during image generation`() =
133+
goldenVertexUnaryFile("unary-success-image-invalid-safety-ratings.json") {
134+
withTimeout(testTimeout) {
135+
val response = model.generateContent("prompt")
136+
137+
response.candidates.isEmpty() shouldBe false
138+
}
139+
}
140+
128141
@Test
129142
fun `unknown enum in finish reason`() =
130143
goldenVertexUnaryFile("unary-failure-unknown-enum-finish-reason.json") {

firebase-ai/src/testUtil/java/com/google/firebase/ai/JavaCompileTests.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@
5757
import com.google.firebase.ai.type.PublicPreviewAPI;
5858
import com.google.firebase.ai.type.ResponseModality;
5959
import com.google.firebase.ai.type.SafetyRating;
60+
import com.google.firebase.ai.type.Schema;
6061
import com.google.firebase.ai.type.SpeechConfig;
6162
import com.google.firebase.ai.type.TextPart;
6263
import com.google.firebase.ai.type.UsageMetadata;
6364
import com.google.firebase.ai.type.Voices;
6465
import com.google.firebase.concurrent.FirebaseExecutors;
66+
import java.util.ArrayList;
6567
import java.util.Calendar;
6668
import java.util.List;
6769
import java.util.Map;
@@ -92,8 +94,37 @@ public void initializeJava() throws Exception {
9294
}
9395

9496
private GenerationConfig getConfig() {
95-
return new GenerationConfig.Builder().build();
96-
// TODO b/406558430 GenerationConfig.Builder.setParts returns void
97+
return new GenerationConfig.Builder()
98+
.setTopK(10)
99+
.setTopP(11.0F)
100+
.setTemperature(32.0F)
101+
.setCandidateCount(1)
102+
.setMaxOutputTokens(0xCAFEBABE)
103+
.setFrequencyPenalty(1.0F)
104+
.setPresencePenalty(2.0F)
105+
.setStopSequences(List.of("foo", "bar"))
106+
.setResponseMimeType("image/jxl")
107+
.setResponseModalities(List.of(ResponseModality.TEXT, ResponseModality.TEXT))
108+
.setResponseSchema(getSchema())
109+
.build();
110+
}
111+
112+
private Schema getSchema() {
113+
return Schema.obj(
114+
Map.of(
115+
"foo", Schema.numInt(),
116+
"bar", Schema.numInt("Some integer"),
117+
"baz", Schema.numInt("Some integer", false),
118+
"qux", Schema.numDouble(),
119+
"quux", Schema.numFloat("Some floating point number"),
120+
"xyzzy", Schema.array(Schema.numInt(), "A list of integers"),
121+
"fee", Schema.numLong(),
122+
"ber",
123+
Schema.obj(
124+
Map.of(
125+
"bez", Schema.array(Schema.numDouble("Nullable double", true)),
126+
"qez", Schema.enumeration(List.of("A", "B", "C"), "One of 3 letters"),
127+
"qeez", Schema.str("A funny string")))));
97128
}
98129

99130
private LiveGenerationConfig getLiveConfig() {
@@ -113,13 +144,14 @@ private LiveGenerationConfig getLiveConfig() {
113144
private void testFutures(GenerativeModelFutures futures) throws Exception {
114145
Content content =
115146
new Content.Builder()
147+
.setParts(new ArrayList<>())
116148
.addText("Fake prompt")
117149
.addFileData("fakeuri", "image/png")
118150
.addInlineData(new byte[] {}, "text/json")
119151
.addImage(Bitmap.createBitmap(0, 0, Bitmap.Config.HARDWARE))
120152
.addPart(new FunctionCallPart("fakeFunction", Map.of("fakeArg", JsonNull.INSTANCE)))
153+
.setRole("user")
121154
.build();
122-
// TODO b/406558430 Content.Builder.setParts and Content.Builder.setRole return void
123155
Executor executor = FirebaseExecutors.directExecutor();
124156
ListenableFuture<CountTokensResponse> countResponse = futures.countTokens(content);
125157
validateCountTokensResponse(countResponse.get());

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CommonUtils.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ static Architecture getValue() {
139139
public static String streamToString(InputStream is) {
140140
// Previous code was running into this: http://code.google.com/p/android/issues/detail?id=14562
141141
// on Android 2.3.3. The below code below does not exhibit that problem.
142-
final java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
143-
return s.hasNext() ? s.next() : "";
142+
try (final java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A")) {
143+
return s.hasNext() ? s.next() : "";
144+
}
144145
}
145146

146147
public static String sha1(String source) {

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsController.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -661,15 +661,15 @@ private InputStream getResourceAsStream(String resource) {
661661
}
662662

663663
private static byte[] readResource(InputStream is) throws IOException {
664-
ByteArrayOutputStream out = new ByteArrayOutputStream();
665-
byte[] buffer = new byte[1024];
666-
int length;
664+
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
665+
byte[] buffer = new byte[1024];
666+
int length;
667667

668-
while ((length = is.read(buffer)) != -1) {
669-
out.write(buffer, 0, length);
668+
while ((length = is.read(buffer)) != -1) {
669+
out.write(buffer, 0, length);
670+
}
671+
return out.toByteArray();
670672
}
671-
672-
return out.toByteArray();
673673
}
674674

675675
private void finalizePreviousNativeSession(String previousSessionId) {

0 commit comments

Comments
 (0)