Skip to content

Commit 38c121a

Browse files
committed
Merge branch 'main' into rl.thinking
2 parents 6fad595 + c85227e commit 38c121a

File tree

5 files changed

+37
-12
lines changed

5 files changed

+37
-12
lines changed

firebase-ai/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Unreleased
22
* [feature] Added support for returning thought summaries, which are synthesized
33
versions of a model's internal reasoning process.
4+
* [fixed] Fixed an issue causing the accessor methods in `GenerateContentResponse` to throw an exception
5+
when the response contained no candidates.
46
* [changed] Added better description for requests which fail due to the Gemini API not being
57
configured.
68
* [changed] Added a `dilation` parameter to `ImagenMaskReference.generateMaskAndPadForOutpainting`

firebase-ai/api.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ package com.google.firebase.ai.java {
157157

158158
package com.google.firebase.ai.type {
159159

160+
public final class APINotConfiguredException extends com.google.firebase.ai.type.FirebaseAIException {
161+
}
162+
160163
public final class AudioRecordInitializationFailedException extends com.google.firebase.ai.type.FirebaseAIException {
161164
ctor public AudioRecordInitializationFailedException(String message);
162165
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public class UnsupportedUserLocationException internal constructor(cause: Throwa
158158
* [steps](https://firebase.google.com/docs/ai-logic/faq-and-troubleshooting?api=dev#error-genai-config-not-found)
159159
* to enable the Gemini Developer API.
160160
*/
161-
internal class APINotConfiguredException internal constructor(cause: Throwable? = null) :
161+
public class APINotConfiguredException internal constructor(cause: Throwable? = null) :
162162
FirebaseAIException("Gemini Developer API not enabled in Firebase console.", cause)
163163

164164
/**

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ public class GenerateContentResponse(
3232
public val usageMetadata: UsageMetadata?,
3333
) {
3434
/**
35-
* Convenience field representing all text parts in the response as a single string, if they
36-
* exists.
35+
* Convenience field representing all the text parts in the response as a single string.
36+
*
37+
* The value is null if the response contains no valid text [candidates].
3738
*
3839
* Any part that's marked as a thought will be ignored. Learn more about
3940
* [thinking](https://firebase.google.com/docs/ai-logic/thinking?api=dev).
@@ -47,11 +48,13 @@ public class GenerateContentResponse(
4748
/**
4849
* Convenience field to list all the [FunctionCallPart]s in the response.
4950
*
51+
* The value is an empty list if the response contains no [candidates].
52+
*
5053
* Any part that's marked as a thought will be ignored. Learn more about
5154
* [thinking](https://firebase.google.com/docs/ai-logic/thinking?api=dev).
5255
*/
5356
public val functionCalls: List<FunctionCallPart> by lazy {
54-
candidates.firstOrNull()?.nonThoughtParts()?.filterIsInstance<FunctionCallPart>().orEmpty()
57+
candidates.firstOrNull()?.content?.parts?.filterIsInstance<FunctionCallPart>().orEmpty()
5558
}
5659

5760
/**
@@ -69,6 +72,8 @@ public class GenerateContentResponse(
6972
*
7073
* This also includes any [ImagePart], but they will be represented as [InlineDataPart] instead.
7174
*
75+
* The value is an empty list if the response contains no [candidates].
76+
*
7277
* Any part that's marked as a thought will be ignored. Learn more about
7378
* [thinking](https://firebase.google.com/docs/ai-logic/thinking?api=dev).
7479
*/

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ import com.google.firebase.ai.type.ResponseStoppedException
2222
import com.google.firebase.ai.type.ServerException
2323
import com.google.firebase.ai.util.goldenDevAPIUnaryFile
2424
import io.kotest.assertions.throwables.shouldThrow
25+
import io.kotest.matchers.collections.shouldBeEmpty
2526
import io.kotest.matchers.collections.shouldNotBeEmpty
2627
import io.kotest.matchers.nulls.shouldBeNull
2728
import io.kotest.matchers.nulls.shouldNotBeNull
2829
import io.kotest.matchers.should
2930
import io.kotest.matchers.shouldBe
30-
import io.kotest.matchers.shouldNotBe
3131
import io.ktor.http.HttpStatusCode
3232
import kotlin.time.Duration.Companion.seconds
3333
import kotlinx.coroutines.withTimeout
@@ -42,9 +42,24 @@ internal class DevAPIUnarySnapshotTests {
4242
withTimeout(testTimeout) {
4343
val response = model.generateContent("prompt")
4444

45-
response.candidates.isEmpty() shouldBe false
45+
response.candidates.shouldNotBeEmpty()
4646
response.candidates.first().finishReason shouldBe FinishReason.STOP
47-
response.candidates.first().content.parts.isEmpty() shouldBe false
47+
response.candidates.first().content.parts.shouldNotBeEmpty()
48+
}
49+
}
50+
51+
@Test
52+
fun `only prompt feedback reply`() =
53+
goldenDevAPIUnaryFile("unary-failure-only-prompt-feedback.json") {
54+
withTimeout(testTimeout) {
55+
val response = model.generateContent("prompt")
56+
57+
response.candidates.shouldBeEmpty()
58+
59+
// Check response from accessors
60+
response.text.shouldBeNull()
61+
response.functionCalls.shouldBeEmpty()
62+
response.inlineDataParts.shouldBeEmpty()
4863
}
4964
}
5065

@@ -54,9 +69,9 @@ internal class DevAPIUnarySnapshotTests {
5469
withTimeout(testTimeout) {
5570
val response = model.generateContent("prompt")
5671

57-
response.candidates.isEmpty() shouldBe false
72+
response.candidates.shouldNotBeEmpty()
5873
response.candidates.first().finishReason shouldBe FinishReason.STOP
59-
response.candidates.first().content.parts.isEmpty() shouldBe false
74+
response.candidates.first().content.parts.shouldNotBeEmpty()
6075
}
6176
}
6277

@@ -66,11 +81,11 @@ internal class DevAPIUnarySnapshotTests {
6681
withTimeout(testTimeout) {
6782
val response = model.generateContent("prompt")
6883

69-
response.candidates.isEmpty() shouldBe false
84+
response.candidates.shouldNotBeEmpty()
7085
response.candidates.first().citationMetadata?.citations?.size shouldBe 4
7186
response.candidates.first().citationMetadata?.citations?.forEach {
72-
it.startIndex shouldNotBe null
73-
it.endIndex shouldNotBe null
87+
it.startIndex.shouldNotBeNull()
88+
it.endIndex.shouldNotBeNull()
7489
}
7590
}
7691
}

0 commit comments

Comments
 (0)