Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ai-logic/firebase-ai/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
- [feature] Added support for [Maps Grounding](https://ai.google.dev/gemini-api/docs/maps-grounding) (#7950)
- [fixed] Fixed an issue causing network timeouts to throw the incorrect exception type, instead of
`RequestTimeoutException` (#7966)
- [fixed] Fixed an issue causing the SDK to throw an exception if an unknown message was received
from the LiveAPI model, instead of ignoring it (#7975)

# 17.10.1

Expand Down
3 changes: 3 additions & 0 deletions ai-logic/firebase-ai/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,9 @@ package com.google.firebase.ai.type {
property public final java.util.List<java.lang.String> functionIds;
}

@com.google.firebase.ai.type.PublicPreviewAPI public final class LiveServerUnknownMessage implements com.google.firebase.ai.type.LiveServerMessage {
}

@com.google.firebase.ai.type.PublicPreviewAPI public final class LiveSession {
method public suspend Object? close(kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public boolean isAudioConversationActive();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.google.firebase.ai.type

import android.util.Log
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.serialization.DeserializationStrategy
Expand Down Expand Up @@ -136,6 +137,15 @@ public class LiveServerSetupComplete : LiveServerMessage {
}
}

@PublicPreviewAPI
public class LiveServerUnknownMessage private constructor() : LiveServerMessage {
@Serializable
internal data class InternalWrapper(@Transient val unused: Unit? = null) :
InternalLiveServerMessage {
override fun toPublic() = LiveServerUnknownMessage()
}
}

/**
* Request for the client to execute the provided [functionCalls].
*
Expand Down Expand Up @@ -233,10 +243,13 @@ internal object LiveServerMessageSerializer :
"toolCallCancellation" in jsonObject ->
LiveServerToolCallCancellation.InternalWrapper.serializer()
"goAway" in jsonObject -> LiveServerGoAway.InternalWrapper.serializer()
else ->
throw SerializationException(
"Unknown LiveServerMessage response type. Keys found: ${jsonObject.keys}"
else -> {
Log.w(
"LiveServerMsgSerializer",
"Ignoring unknown LiveServerMessage response type. Keys found: ${jsonObject.keys}"
)
LiveServerUnknownMessage.InternalWrapper.serializer()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ internal constructor(
// Notify the application
goAwayHandler?.invoke(it)
}
is LiveServerUnknownMessage -> {} // Ignore. Logging happens at de-serialization time
}
}
.launchIn(networkScope)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package com.google.firebase.ai.type

import com.google.firebase.ai.common.JSON
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.nulls.shouldBeNull
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
Expand Down Expand Up @@ -109,10 +108,11 @@ internal class LiveServerMessageTests {
}

@Test
fun `LiveServerMessageSerializer throws on unknown message type`() {
fun `LiveServerMessageSerializer returns LiveServerUnknownMessage for unrecognized message`() {
val json = """{"unknownType": {"data": "value"}}"""

shouldThrow<SerializationException> { JSON.decodeFromString<InternalLiveServerMessage>(json) }
val message = JSON.decodeFromString<InternalLiveServerMessage>(json)
message.toPublic().shouldBeInstanceOf<LiveServerUnknownMessage>()
}

@Test
Expand Down
Loading