Skip to content

Commit 16f1dfc

Browse files
[AI] Gracefully handle unknown LiveServerMessage types (#7975)
Introduce `LiveServerUnknownMessage` to represent unrecognized messages received from the server. Previously, the `LiveServerMessageSerializer` would throw a `SerializationException` when encountering an unknown message type. This change modifies the serializer to instead log a warning and return a `LiveServerUnknownMessage` for any unhandled types. The `LiveSession`'s message handler is updated to explicitly ignore these unknown messages. This prevents crashes due to unexpected server messages and allows for forward compatibility with new message types. --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent cc7d1b4 commit 16f1dfc

5 files changed

Lines changed: 25 additions & 6 deletions

File tree

ai-logic/firebase-ai/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
- [feature] Added support for [Maps Grounding](https://ai.google.dev/gemini-api/docs/maps-grounding) (#7950)
44
- [fixed] Fixed an issue causing network timeouts to throw the incorrect exception type, instead of
55
`RequestTimeoutException` (#7966)
6+
- [fixed] Fixed an issue causing the SDK to throw an exception if an unknown message was received
7+
from the LiveAPI model, instead of ignoring it (#7975)
68

79
# 17.10.1
810

ai-logic/firebase-ai/api.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,9 @@ package com.google.firebase.ai.type {
13011301
property public final java.util.List<java.lang.String> functionIds;
13021302
}
13031303

1304+
@com.google.firebase.ai.type.PublicPreviewAPI public final class LiveServerUnknownMessage implements com.google.firebase.ai.type.LiveServerMessage {
1305+
}
1306+
13041307
@com.google.firebase.ai.type.PublicPreviewAPI public final class LiveSession {
13051308
method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
13061309
method public boolean isAudioConversationActive();

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.firebase.ai.type
1818

19+
import android.util.Log
1920
import kotlin.time.Duration
2021
import kotlin.time.Duration.Companion.seconds
2122
import kotlinx.serialization.DeserializationStrategy
@@ -136,6 +137,15 @@ public class LiveServerSetupComplete : LiveServerMessage {
136137
}
137138
}
138139

140+
@PublicPreviewAPI
141+
public class LiveServerUnknownMessage private constructor() : LiveServerMessage {
142+
@Serializable
143+
internal data class InternalWrapper(@Transient val unused: Unit? = null) :
144+
InternalLiveServerMessage {
145+
override fun toPublic() = LiveServerUnknownMessage()
146+
}
147+
}
148+
139149
/**
140150
* Request for the client to execute the provided [functionCalls].
141151
*
@@ -233,10 +243,13 @@ internal object LiveServerMessageSerializer :
233243
"toolCallCancellation" in jsonObject ->
234244
LiveServerToolCallCancellation.InternalWrapper.serializer()
235245
"goAway" in jsonObject -> LiveServerGoAway.InternalWrapper.serializer()
236-
else ->
237-
throw SerializationException(
238-
"Unknown LiveServerMessage response type. Keys found: ${jsonObject.keys}"
246+
else -> {
247+
Log.w(
248+
"LiveServerMsgSerializer",
249+
"Ignoring unknown LiveServerMessage response type. Keys found: ${jsonObject.keys}"
239250
)
251+
LiveServerUnknownMessage.InternalWrapper.serializer()
252+
}
240253
}
241254
}
242255
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ internal constructor(
578578
// Notify the application
579579
goAwayHandler?.invoke(it)
580580
}
581+
is LiveServerUnknownMessage -> {} // Ignore. Logging happens at de-serialization time
581582
}
582583
}
583584
.launchIn(networkScope)

ai-logic/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveServerMessageTests.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.google.firebase.ai.type
1818

1919
import com.google.firebase.ai.common.JSON
20-
import io.kotest.assertions.throwables.shouldThrow
2120
import io.kotest.matchers.nulls.shouldBeNull
2221
import io.kotest.matchers.nulls.shouldNotBeNull
2322
import io.kotest.matchers.shouldBe
@@ -109,10 +108,11 @@ internal class LiveServerMessageTests {
109108
}
110109

111110
@Test
112-
fun `LiveServerMessageSerializer throws on unknown message type`() {
111+
fun `LiveServerMessageSerializer returns LiveServerUnknownMessage for unrecognized message`() {
113112
val json = """{"unknownType": {"data": "value"}}"""
114113

115-
shouldThrow<SerializationException> { JSON.decodeFromString<InternalLiveServerMessage>(json) }
114+
val message = JSON.decodeFromString<InternalLiveServerMessage>(json)
115+
message.toPublic().shouldBeInstanceOf<LiveServerUnknownMessage>()
116116
}
117117

118118
@Test

0 commit comments

Comments
 (0)