diff --git a/firebase-ai-ondevice/src/main/kotlin/com/google/firebase/ai/ondevice/Converters.kt b/firebase-ai-ondevice/src/main/kotlin/com/google/firebase/ai/ondevice/Converters.kt index fb5c885ca28..ef615423857 100644 --- a/firebase-ai-ondevice/src/main/kotlin/com/google/firebase/ai/ondevice/Converters.kt +++ b/firebase-ai-ondevice/src/main/kotlin/com/google/firebase/ai/ondevice/Converters.kt @@ -16,6 +16,7 @@ package com.google.firebase.ai.ondevice +import android.graphics.Bitmap import com.google.firebase.ai.ondevice.interop.Candidate import com.google.firebase.ai.ondevice.interop.CountTokensResponse import com.google.firebase.ai.ondevice.interop.FinishReason @@ -24,6 +25,7 @@ import com.google.firebase.ai.ondevice.interop.GenerateContentResponse import com.google.mlkit.genai.prompt.GenerateContentRequest import com.google.mlkit.genai.prompt.ImagePart import com.google.mlkit.genai.prompt.TextPart +import kotlin.math.min // ==================================== // `Part` converter extension functions @@ -31,7 +33,7 @@ import com.google.mlkit.genai.prompt.TextPart internal fun com.google.firebase.ai.ondevice.interop.TextPart.toMlKit(): TextPart = TextPart(text) internal fun com.google.firebase.ai.ondevice.interop.ImagePart.toMlKit(): ImagePart = - ImagePart(bitmap) + ImagePart(downsizeBitmapIfNeeded(bitmap)) // ============================================ // `CountTokens*` converter extension functions @@ -87,3 +89,24 @@ private fun generateContentRequest( builder.init() return builder.build() } + +private fun downsizeBitmapIfNeeded(bitmap: Bitmap): Bitmap { + val IMAGE_SHORTER_DIMENSION_MAX_VALUE: Int = 768 + val width = bitmap.width + val height = bitmap.height + val shorterDimension: Int = min(width, height) + if (shorterDimension <= IMAGE_SHORTER_DIMENSION_MAX_VALUE) { + return bitmap + } + + val scaleFactor = (IMAGE_SHORTER_DIMENSION_MAX_VALUE.toDouble()) / shorterDimension + + val newWidth = (width * scaleFactor).toInt() + val newHeight = (height * scaleFactor).toInt() + + val resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, /* filter= */ false) + if (resizedBitmap != bitmap) { + bitmap.recycle() + } + return resizedBitmap +} diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/generativemodel/FallbackGenerativeModelProvider.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/generativemodel/FallbackGenerativeModelProvider.kt index 0a0dc385d1b..2ebc9857b9e 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/generativemodel/FallbackGenerativeModelProvider.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/generativemodel/FallbackGenerativeModelProvider.kt @@ -25,6 +25,9 @@ import com.google.firebase.ai.type.GenerateContentResponse import com.google.firebase.ai.type.GenerateObjectResponse import com.google.firebase.ai.type.JsonSchema import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach /** * A [GenerativeModelProvider] that delegates requests to a `defaultModel` and falls back to a @@ -59,7 +62,7 @@ internal class FallbackGenerativeModelProvider( } override fun generateContentStream(prompt: List): Flow { - return withFallback("generateContentStream") { generateContentStream(prompt) } + return withFlowFallback("generateContentStream") { generateContentStream(prompt) } } override suspend fun generateObject( @@ -104,6 +107,49 @@ internal class FallbackGenerativeModelProvider( } } + // Flow-based fallback differs significantly from regular call fallback, primarily due to: + // + // 1. Exception Timing: Exceptions are thrown during *flow collection*, not at flow creation. + // Therefore, a *wrapper flow* is utilized to manage emitting either default or fallback + // elements. + // 2. Partial Collection Rule: If a flow collection has *successfully started* before an exception + // occurs, *no fallback* should be triggered. This prevents inconsistent or mixed responses, + // where partial data from one source could be combined with data from another. + private inline fun withFlowFallback( + methodName: String, + crossinline block: GenerativeModelProvider.() -> Flow + ): Flow { + if (!precondition()) { + Log.w( + TAG, + "Precondition was not met, switching to fallback model `${fallbackModel.javaClass.simpleName}`" + ) + return fallbackModel.block() + } + return flow { + var hasEmitted = false + val defaultFlow = defaultModel.block().onEach { hasEmitted = true } + try { + emitAll(defaultFlow) + } catch (e: Exception) { + if ( + !hasEmitted && + shouldFallbackInException && + (e is FirebaseAIException || e is FirebaseAIOnDeviceException) + ) { + Log.w( + TAG, + "Error running `$methodName` on `${defaultModel.javaClass.simpleName}`. Falling back to `${fallbackModel.javaClass.simpleName}`", + e + ) + emitAll(fallbackModel.block()) + } else { + throw e + } + } + } + } + companion object { private val TAG = FallbackGenerativeModelProvider::class.java.simpleName } diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/FunctionDeclaration.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/FunctionDeclaration.kt index 7c7eb338ead..6b96b33a97f 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/FunctionDeclaration.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/FunctionDeclaration.kt @@ -67,8 +67,8 @@ public class FunctionDeclaration( internal data class Internal( val name: String, val description: String, - val parameters: Schema.InternalOpenAPI?, - val parametersJsonSchema: Schema.InternalJson?, - val responseJsonSchema: Schema.InternalJson?, + val parameters: Schema.InternalOpenAPI? = null, + val parametersJsonSchema: Schema.InternalJson? = null, + val responseJsonSchema: Schema.InternalJson? = null, ) } diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/generativeModel/FallbackGenerativeModelProviderTests.kt b/firebase-ai/src/test/java/com/google/firebase/ai/generativeModel/FallbackGenerativeModelProviderTests.kt index 4c0d6f59858..ae7b6d75e49 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/generativeModel/FallbackGenerativeModelProviderTests.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/generativeModel/FallbackGenerativeModelProviderTests.kt @@ -19,16 +19,24 @@ package com.google.firebase.ai.generativemodel import com.google.firebase.ai.type.Content import com.google.firebase.ai.type.CountTokensResponse import com.google.firebase.ai.type.FirebaseAIException +import com.google.firebase.ai.type.FirebaseAIOnDeviceInvalidRequestException import com.google.firebase.ai.type.GenerateContentResponse import com.google.firebase.ai.type.GenerateObjectResponse import com.google.firebase.ai.type.JsonSchema +import com.google.firebase.ai.type.PromptBlockedException +import com.google.firebase.ai.type.PublicPreviewAPI +import io.kotest.assertions.throwables.shouldNotThrowAnyUnit import io.kotest.assertions.throwables.shouldThrow +import io.kotest.assertions.throwables.shouldThrowUnit import io.kotest.matchers.shouldBe import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import io.mockk.verify +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Before @@ -51,10 +59,12 @@ internal class FallbackGenerativeModelProviderTests { coEvery { defaultModel.generateContent(prompt) } returns expectedResponse val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) - val response = provider.generateContent(prompt) + shouldNotThrowAnyUnit { + val response = provider.generateContent(prompt) - response shouldBe expectedResponse - coVerify(exactly = 0) { fallbackModel.generateContent(any()) } + response shouldBe expectedResponse + coVerify(exactly = 0) { fallbackModel.generateContent(any()) } + } } @Test @@ -64,27 +74,65 @@ internal class FallbackGenerativeModelProviderTests { val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel, precondition = { false }) - val response = provider.generateContent(prompt) + shouldNotThrowAnyUnit { + val response = provider.generateContent(prompt) - response shouldBe expectedResponse - coVerify(exactly = 0) { defaultModel.generateContent(any()) } - coVerify { fallbackModel.generateContent(prompt) } + response shouldBe expectedResponse + coVerify(exactly = 0) { defaultModel.generateContent(any()) } + coVerify { fallbackModel.generateContent(prompt) } + } } @Test fun `generateContent falls back when default model throws FirebaseAIException`() = runBlocking { val expectedResponse: GenerateContentResponse = mockk() - val exception = mockk() + // Test using an exception that extends FirebaseAIException + val exception = mockk() coEvery { defaultModel.generateContent(prompt) } throws exception coEvery { fallbackModel.generateContent(prompt) } returns expectedResponse val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) - val response = provider.generateContent(prompt) + shouldNotThrowAnyUnit { + val response = provider.generateContent(prompt) - response shouldBe expectedResponse - coVerify { fallbackModel.generateContent(prompt) } + response shouldBe expectedResponse + coVerify { fallbackModel.generateContent(prompt) } + } } + @OptIn(PublicPreviewAPI::class) + @Test + fun `generateContent shouldn't falls back when default model throws unrelated exception`(): Unit = + runBlocking { + val expectedResponse: GenerateContentResponse = mockk() + // Test using an exception that extends FirebaseAIOnDeviceException + val exception = mockk() + coEvery { defaultModel.generateContent(prompt) } throws exception + coEvery { fallbackModel.generateContent(prompt) } returns expectedResponse + + val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) + shouldThrowUnit { provider.generateContent(prompt) } + } + + @OptIn(PublicPreviewAPI::class) + @Test + fun `generateContent falls back when default model throws FirebaseAIOnDeviceException`() = + runBlocking { + val expectedResponse: GenerateContentResponse = mockk() + // Test using an exception that extends FirebaseAIOnDeviceException + val exception = mockk() + coEvery { defaultModel.generateContent(prompt) } throws exception + coEvery { fallbackModel.generateContent(prompt) } returns expectedResponse + + val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) + shouldNotThrowAnyUnit { + val response = provider.generateContent(prompt) + + response shouldBe expectedResponse + coVerify { fallbackModel.generateContent(prompt) } + } + } + @Test fun `generateContent rethrows FirebaseAIException when fallback is disabled`() = runBlocking { val exception = mockk() @@ -120,10 +168,12 @@ internal class FallbackGenerativeModelProviderTests { coEvery { fallbackModel.countTokens(prompt) } returns expectedResponse val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) - val response = provider.countTokens(prompt) + shouldNotThrowAnyUnit { + val response = provider.countTokens(prompt) - response shouldBe expectedResponse - coVerify { fallbackModel.countTokens(prompt) } + response shouldBe expectedResponse + coVerify { fallbackModel.countTokens(prompt) } + } } @Test @@ -131,30 +181,95 @@ internal class FallbackGenerativeModelProviderTests { runBlocking { val expectedResponse: GenerateContentResponse = mockk() val fallbackFlow = flowOf(expectedResponse) - val exception = mockk() - every { defaultModel.generateContentStream(prompt) } throws exception + // Test using an exception that extends FirebaseAIOnException + val exception = mockk() + // throw the exception during the flow collection + every { defaultModel.generateContentStream(prompt) } returns flow { throw exception } + every { fallbackModel.generateContentStream(prompt) } returns fallbackFlow + + val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) + shouldNotThrowAnyUnit { + val responseFlow = provider.generateContentStream(prompt) + + responseFlow.first() shouldBe expectedResponse + verify { fallbackModel.generateContentStream(prompt) } + } + } + + @OptIn(PublicPreviewAPI::class) + @Test + fun `generateContentStream falls back when default model throws FirebaseAIOnDeviceException`() = + runBlocking { + val expectedResponse: GenerateContentResponse = mockk() + val fallbackFlow = flowOf(expectedResponse) + // Test using an exception that extends FirebaseAIOnDeviceException + val exception = mockk() + // throw the exception during the flow collection + every { defaultModel.generateContentStream(prompt) } returns flow { throw exception } every { fallbackModel.generateContentStream(prompt) } returns fallbackFlow val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) - val responseFlow = provider.generateContentStream(prompt) + shouldNotThrowAnyUnit { + val responseFlow = provider.generateContentStream(prompt) - responseFlow shouldBe fallbackFlow - verify { fallbackModel.generateContentStream(prompt) } + responseFlow.first() shouldBe expectedResponse + verify { fallbackModel.generateContentStream(prompt) } + } } + @Test + fun `generateContentStream rethrows non-FirebaseAIException`() = runBlocking { + val expectedResponse: GenerateContentResponse = mockk() + val fallbackFlow = flowOf(expectedResponse) + val exception = mockk() + // throw the exception during the flow collection + every { defaultModel.generateContentStream(prompt) } returns flow { throw exception } + every { fallbackModel.generateContentStream(prompt) } returns fallbackFlow + + val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) + shouldThrow { provider.generateContentStream(prompt).first() } + + verify(exactly = 0) { fallbackModel.generateContentStream(prompt) } + } + + @Test + fun `generateContentStream rethrows exception if a value was already emitted`() = runBlocking { + val expectedResponse: GenerateContentResponse = mockk() + val fallbackFlow = flowOf(expectedResponse) + val exception = mockk() + // throw the exception during the flow collection + every { defaultModel.generateContentStream(prompt) } returns + flow { + emit(expectedResponse) + throw exception + } + every { fallbackModel.generateContentStream(prompt) } returns fallbackFlow + + val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) + // Even though it's an exception we can fall back from, we don't since a value has been + // generated + // already. + shouldThrow { provider.generateContentStream(prompt).collect() } + + verify(exactly = 0) { fallbackModel.generateContentStream(prompt) } + } + @Test fun `generateObject falls back when default model throws FirebaseAIException`() = runBlocking { val schema: JsonSchema = mockk() val expectedResponse: GenerateObjectResponse = mockk() + // Test using an exception that extends val exception = mockk() coEvery { defaultModel.generateObject(schema, prompt) } throws exception coEvery { fallbackModel.generateObject(schema, prompt) } returns expectedResponse val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) - val response = provider.generateObject(schema, prompt) + shouldNotThrowAnyUnit { + val response = provider.generateObject(schema, prompt) - response shouldBe expectedResponse - coVerify { fallbackModel.generateObject(schema, prompt) } + response shouldBe expectedResponse + coVerify { fallbackModel.generateObject(schema, prompt) } + } } @Test @@ -172,12 +287,12 @@ internal class FallbackGenerativeModelProviderTests { fun `generateContentStream rethrows CancellationException and does not fall back`() = runBlocking { val exception = kotlinx.coroutines.CancellationException("cancelled") - every { defaultModel.generateContentStream(prompt) } throws exception + every { defaultModel.generateContentStream(prompt) } returns flow { throw exception } val provider = FallbackGenerativeModelProvider(defaultModel, fallbackModel) shouldThrow { - provider.generateContentStream(prompt) + provider.generateContentStream(prompt).first() } verify(exactly = 0) { fallbackModel.generateContentStream(any()) } } diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/type/FunctionDeclarationTest.kt b/firebase-ai/src/test/java/com/google/firebase/ai/type/FunctionDeclarationTest.kt index 575dff886c7..7719044b498 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/type/FunctionDeclarationTest.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/type/FunctionDeclarationTest.kt @@ -48,9 +48,7 @@ internal class FunctionDeclarationTest { "required": [ "userID" ] - }, - "parametersJsonSchema": null, - "responseJsonSchema": null + } } """ .trimIndent() @@ -92,9 +90,7 @@ internal class FunctionDeclarationTest { "required": [ "userID" ] - }, - "parametersJsonSchema": null, - "responseJsonSchema": null + } } """ .trimIndent() diff --git a/release.json b/release.json new file mode 100644 index 00000000000..f0e3879ad9a --- /dev/null +++ b/release.json @@ -0,0 +1,16 @@ +{ + "name": "m177", + "libraries": [ + ":firebase-ai", + ":firebase-ai-ksp-processor", + ":firebase-ai-ondevice", + ":firebase-ai-ondevice-interop", + ":firebase-dataconnect", + ":firebase-firestore", + ":firebase-installations", + ":firebase-installations-interop", + ":transport:transport-api", + ":transport:transport-backend-cct", + ":transport:transport-runtime" + ] +} \ No newline at end of file diff --git a/release_report.json b/release_report.json new file mode 100644 index 00000000000..b1813a23ae4 --- /dev/null +++ b/release_report.json @@ -0,0 +1,267 @@ +{ + "changesByLibraryName": { + "firebase-ai": [ + { + "commitId": "75da2648c0b1322ca53c7363311f60382e5701e5", + "prId": "7739", + "author": "Rodrigo Lazo", + "message": "[AI] Add support for on-device / cloud hybrid inference (#7739)\n\nRefactors the generative model instantiation logic to support both\nin-cloud and on-device backends. This change introduces the\n`OnDeviceConfig` and `InferenceMode` types to the public API surface\nunder the `@PublicPreviewAPI` experimental annotation.\n\nBackwards compatibility for existing cloud-only callers is maintained.\n\nInternal references\n- b/483432762 \n- http://shortn/_HK4ZVe0ldY \n- http://shortn/_1pxGF3eYRY\n\n---------\n\nCo-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>\nCo-authored-by: Matthew Robertson \nCo-authored-by: Kai Bolay \nCo-authored-by: wu-hui <53845758+wu-hui@users.noreply.github.com>\nCo-authored-by: Tejas Deshpande \nCo-authored-by: Daniel La Rocque \nCo-authored-by: Rebecca He <31138589+rebehe@users.noreply.github.com>\nCo-authored-by: Google Open Source Bot \nCo-authored-by: emilypgoogle <110422458+emilypgoogle@users.noreply.github.com>\nCo-authored-by: YooJun Hong <46425142+Kick-snare@users.noreply.github.com>\nCo-authored-by: David Motsonashvili \nCo-authored-by: David Motsonashvili ", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/75da2648c0b1322ca53c7363311f60382e5701e5", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7739" + }, + { + "commitId": "d392c78d77e66f0868cd28f11d75a4ea913bd865", + "prId": "7745", + "author": "Rodrigo Lazo", + "message": "[AI] Improve refdoc readability (#7745)\n\nSeveral fixes to refdocs for the Firebase AI Logic SDK\n\nInternal b/480912573", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/d392c78d77e66f0868cd28f11d75a4ea913bd865", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7745" + }, + { + "commitId": "d093ee445f7d8ea3ad662481384a21d099bef420", + "prId": "7740", + "author": "Rodrigo Lazo", + "message": "[AI] Standardize tool handling and function call limits (#7740)\n\nChanged `tools` parameters and properties across `GenerativeModel`,\n`LiveGenerativeModel`, and `FirebaseAI` from nullable `List?` to\nnon-nullable `List` with a default `emptyList()`. This simplifies\ntool access and removes redundant null checks.\n\nIntroduced `requestOptions` as a direct property in `GenerativeModel` to\nprovide consistent access to request-specific settings. The\n`autoFunctionCallingTurnLimit` in `Chat` now directly accesses\n`model.requestOptions.autoFunctionCallingTurnLimit`, replacing the\n`getTurnLimit()` method. The `getTurnLimit()` method was subsequently\nremoved from `GenerativeModel` and `APIController`.", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/d093ee445f7d8ea3ad662481384a21d099bef420", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7740" + }, + { + "commitId": "829572116dc08466ff9bf2ce1c8d938316a1f79a", + "prId": "7734", + "author": "emilypgoogle", + "message": "Enable and test auto function calling (#7734)\n\nEnables auto function calling by making endpoints public, makes some\nminor adjustments, and adds testing for auto function calling.\n\n---------\n\nCo-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/829572116dc08466ff9bf2ce1c8d938316a1f79a", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7734" + }, + { + "commitId": "67e32eb904b42392610bd82fb17ba640da47babe", + "prId": "7706", + "author": "Rodrigo Lazo", + "message": "[AI] Consolidate `generateContent` and `countTokens` overloads (#7706)\n\nRefactored `GenerativeModel` to use a consistent `List` input\nfor its core `generateContent`, `generateContentStream`,\n`generateObject`, and `countTokens` methods.\n\nThis change:\n\n- Consolidates internal request building logic into\n`buildGenerateContentRequest` and `buildCountTokensRequest`.\n\n- Removes the need for `ExperimentalSerializationApi` annotation.\n\n- Renames `constructRequest` to `buildGenerateContentRequest` for better\nreadability", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/67e32eb904b42392610bd82fb17ba640da47babe", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7706" + }, + { + "commitId": "3dbc9b10191be29976721093720fbb295eab1f96", + "prId": "7695", + "author": "Rosário P. Fernandes", + "message": "add @JvmOverloads for Tool.urlContext() and Tool.googleSearch() (#7695)\n\nIn their current state, when called from Java, these functions require\nat least one argument:\n\n```java\nTool urlContext = Tool.urlContext(new UrlContext());\nTool googleSearch = Tool.googleSearch(new GoogleSearch()));\n```\n\nwhile their Kotlin counterpart can be called with no arguments:\n\n```kotlin\nval urlContext = Tool.urlContext()\nval googleSearch = Tool.googleSearch()\n```\n\nThis commit should add `@JvmOverloads` so that the compiler creates the\nrespective no argument methods to be used in Java.", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/3dbc9b10191be29976721093720fbb295eab1f96", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7695" + }, + { + "commitId": "976a718c27bfd3394bcdedd8466936e1becc825f", + "prId": "7688", + "author": "Rodrigo Lazo", + "message": "[AI] Don't end connection early when receiving GoAway (#7688)\n\nInstead of finishing the session when the go away message is received,\nkeeping it open leaves more room for developers to implement different\nstrategies in their callback method. Additionally, it preserves the\ncurrent behaviour.", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/976a718c27bfd3394bcdedd8466936e1becc825f", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7688" + }, + { + "commitId": "753a812a60b76e742dd0fcce8137e9b742116c45", + "prId": "7689", + "author": "Rodrigo Lazo", + "message": "[AI] Address missing updates from previous PRs (#7689)\n\n- Update entries in Changelog\n- Update api.txt\n- Update version", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/753a812a60b76e742dd0fcce8137e9b742116c45", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7689" + }, + { + "commitId": "338e2203fba8ca95ce71aa0924be1c7bc630de32", + "prId": "7523", + "author": "David Motsonashvili", + "message": "Add Firebase AI KSP Processor (#7523)\n\n* Adds the `Generable` and `Guide` annotations\n* Adds the KSP processor to turn them into firebase AI schema objects\n* Adds publishing plugin to export maven repo for ADMRS\n\n---------\n\nCo-authored-by: David Motsonashvili \nCo-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/338e2203fba8ca95ce71aa0924be1c7bc630de32", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7523" + }, + { + "commitId": "611bc3d11c134799a63203148966e06ae05e85a3", + "prId": "7649", + "author": "YooJun Hong", + "message": "[AI] Fix crash caused by unhandled goAway server message in LiveSession (#7649)\n\n## Description\n\nFixes #7648\n\nThis PR adds support for the `goAway` server message type in LiveSession\nto prevent crashes when the server initiates a disconnect. Previously,\nwhen the server sent a `goAway` message, the app would crash with a\n`SerializationException` because the message type was not recognized by\n`LiveServerMessageSerializer`.\n\n## Changes\n\n### 1. Add LiveServerGoAway message type\n\n- Added `LiveServerGoAway` class to represent server-initiated\ndisconnection\n- Includes `timeLeft: Duration?` field from parsed protobuf format\nstring (e.g., \"57s\", \"1.5s\")\n- Registered `goAway` message in `LiveServerMessageSerializer`\n\n### 2. Robust Handling & API Update\n\n- **Graceful Shutdown:** Implemented logic in `processModelResponses()`\nto recognize `GoAway` signals and automatically close the session,\npreventing zombie connections or further crashes.\n- **Public API:** Added `goAwayHandler` to\n`LiveAudioConversationConfig`.\n- *Rationale:* While internal handling prevents the crash, apps often\nneed to inform the user that the session ended due to server maintenance\nor timeouts. This handler exposes the `timeLeft` information to the\napplication layer.\n\n## Usage Example\n\nApps can now handle server disconnections gracefully using the\n`goAwayHandler`:\n\n```kotlin\nval config = LiveAudioConversationConfig.Builder()\n .setGoAwayHandler { goAway ->\n Log.i(TAG, \"Server disconnecting in ${goAway.timeLeft?.inWholeSeconds} seconds\")\n // Clean up resources, show UI notification, etc.\n }\n .build()\n\nliveGenerativeModel.startAudioConversation(config)\n```\n\n## Testing\n\n- unit tests\n- Tested with [real\napp](https://github.com/android/ai-samples/tree/prototype-ai-glasses)\nand confirmed graceful handling of goAway messages (Published to local\nMaven)\n- Spotless formatting applied\n\n## Related Issues\n\n- Android SDK: #7648\n- Flutter SDK: https://github.com/firebase/flutterfire/issues/17836\n\n---------\n\nCo-authored-by: Rodrigo Lazo ", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/611bc3d11c134799a63203148966e06ae05e85a3", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7649" + } + ], + "firebase-ai-ksp-processor": [ + { + "commitId": "e22820ca0b4e730b4592144f503c15c4d8d6b43a", + "prId": "7708", + "author": "Rodrigo Lazo", + "message": "[AI] Add CHANGELOG.md for `firebase-ai-ksp-processor` (#7708)\n\nAdds a new `CHANGELOG.md` file to the `firebase-ai-ksp-processor`\nmodule. This file will track all future changes and features, starting\nwith the initial release entry.", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/e22820ca0b4e730b4592144f503c15c4d8d6b43a", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7708" + }, + { + "commitId": "338e2203fba8ca95ce71aa0924be1c7bc630de32", + "prId": "7523", + "author": "David Motsonashvili", + "message": "Add Firebase AI KSP Processor (#7523)\n\n* Adds the `Generable` and `Guide` annotations\n* Adds the KSP processor to turn them into firebase AI schema objects\n* Adds publishing plugin to export maven repo for ADMRS\n\n---------\n\nCo-authored-by: David Motsonashvili \nCo-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/338e2203fba8ca95ce71aa0924be1c7bc630de32", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7523" + } + ], + "firebase-ai-ondevice": [ + { + "commitId": "75da2648c0b1322ca53c7363311f60382e5701e5", + "prId": "7739", + "author": "Rodrigo Lazo", + "message": "[AI] Add support for on-device / cloud hybrid inference (#7739)\n\nRefactors the generative model instantiation logic to support both\nin-cloud and on-device backends. This change introduces the\n`OnDeviceConfig` and `InferenceMode` types to the public API surface\nunder the `@PublicPreviewAPI` experimental annotation.\n\nBackwards compatibility for existing cloud-only callers is maintained.\n\nInternal references\n- b/483432762 \n- http://shortn/_HK4ZVe0ldY \n- http://shortn/_1pxGF3eYRY\n\n---------\n\nCo-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>\nCo-authored-by: Matthew Robertson \nCo-authored-by: Kai Bolay \nCo-authored-by: wu-hui <53845758+wu-hui@users.noreply.github.com>\nCo-authored-by: Tejas Deshpande \nCo-authored-by: Daniel La Rocque \nCo-authored-by: Rebecca He <31138589+rebehe@users.noreply.github.com>\nCo-authored-by: Google Open Source Bot \nCo-authored-by: emilypgoogle <110422458+emilypgoogle@users.noreply.github.com>\nCo-authored-by: YooJun Hong <46425142+Kick-snare@users.noreply.github.com>\nCo-authored-by: David Motsonashvili \nCo-authored-by: David Motsonashvili ", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/75da2648c0b1322ca53c7363311f60382e5701e5", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7739" + } + ], + "firebase-ai-ondevice-interop": [ + { + "commitId": "79a4811d15feb22ed8db4e951ecf4acec6cc148f", + "prId": "7820", + "author": "emilypgoogle", + "message": "Update metadata for new SDKs for m177 (#7820)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/79a4811d15feb22ed8db4e951ecf4acec6cc148f", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7820" + }, + { + "commitId": "75da2648c0b1322ca53c7363311f60382e5701e5", + "prId": "7739", + "author": "Rodrigo Lazo", + "message": "[AI] Add support for on-device / cloud hybrid inference (#7739)\n\nRefactors the generative model instantiation logic to support both\nin-cloud and on-device backends. This change introduces the\n`OnDeviceConfig` and `InferenceMode` types to the public API surface\nunder the `@PublicPreviewAPI` experimental annotation.\n\nBackwards compatibility for existing cloud-only callers is maintained.\n\nInternal references\n- b/483432762 \n- http://shortn/_HK4ZVe0ldY \n- http://shortn/_1pxGF3eYRY\n\n---------\n\nCo-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>\nCo-authored-by: Matthew Robertson \nCo-authored-by: Kai Bolay \nCo-authored-by: wu-hui <53845758+wu-hui@users.noreply.github.com>\nCo-authored-by: Tejas Deshpande \nCo-authored-by: Daniel La Rocque \nCo-authored-by: Rebecca He <31138589+rebehe@users.noreply.github.com>\nCo-authored-by: Google Open Source Bot \nCo-authored-by: emilypgoogle <110422458+emilypgoogle@users.noreply.github.com>\nCo-authored-by: YooJun Hong <46425142+Kick-snare@users.noreply.github.com>\nCo-authored-by: David Motsonashvili \nCo-authored-by: David Motsonashvili ", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/75da2648c0b1322ca53c7363311f60382e5701e5", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7739" + } + ], + "firebase-dataconnect": [ + { + "commitId": "02d5399fcd3807e9c331d2b0e983d6cc75056941", + "prId": "7821", + "author": "Denver Coneybeare", + "message": "dataconnect: temporarily remove public apis for offline caching (#7821)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/02d5399fcd3807e9c331d2b0e983d6cc75056941", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7821" + }, + { + "commitId": "9dffa7118e557e724e92c4a5767ad2703de15976", + "prId": "7759", + "author": "Denver Coneybeare", + "message": "dataconnect: add plumbing to serve query responses from cache (#7759)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/9dffa7118e557e724e92c4a5767ad2703de15976", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7759" + }, + { + "commitId": "0fb10337936c2b0844435330502170682fe958d9", + "prId": "7744", + "author": "Denver Coneybeare", + "message": "dataconnect: ci: upgrade data connect emulator to 3.1.3 (was 3.0.0) and firebase-tools to 15.6.0 (was 15.0.0) (#7744)\n\nCo-authored-by: Rodrigo Lazo ", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/0fb10337936c2b0844435330502170682fe958d9", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7744" + }, + { + "commitId": "11eeaad6f25074591d92e089f81558ae8ef35a9f", + "prId": "7720", + "author": "Denver Coneybeare", + "message": "dataconnect: caching: add sqlite database logic (#7720)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/11eeaad6f25074591d92e089f81558ae8ef35a9f", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7720" + }, + { + "commitId": "9fc310b6898185668b6723cc344ca4624d0d3ecc", + "prId": "7714", + "author": "Denver Coneybeare", + "message": "dataconnect: caching: add logic to handle query result dehydration and rehydration (#7714)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/9fc310b6898185668b6723cc344ca4624d0d3ecc", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7714" + }, + { + "commitId": "506716b7117de8667ae02cc993128ac072b1f19c", + "prId": "7716", + "author": "Denver Coneybeare", + "message": "dataconnect: caching public api added (#7716)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/506716b7117de8667ae02cc993128ac072b1f19c", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7716" + } + ], + "firebase-firestore": [ + { + "commitId": "d7ac3fe95a0a4ae2f7caac0cf6e84e23093e9d64", + "prId": "7741", + "author": "cherylEnkidu", + "message": "Fixed a race condition in test and remove null / nan related operations (#7741)\n\n## Description\n\nThis PR introduces the following Beta API changes and related fixes:\n\n* **Removed API Methods:** Removes the `isNan`, `isNotNan`, `isNull`,\nand `isNotNull` pipeline string/number operations.\n* **Changed `alias` Return Type:** Updates the `alias` method to now\nreturn an `AliasedExpression` instead of `Selectable`.\n* **Changelog Update:** Updates `CHANGELOG.md` to reflect these recent\nBeta API changes.\n* **Test Fixes:** Fixes build failures in `QueryToPipelineTest.java`\ncaused by the race condition.\n\n---------\n\nCo-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/d7ac3fe95a0a4ae2f7caac0cf6e84e23093e9d64", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7741" + }, + { + "commitId": "22b40539e9e3615322fc04251fab72cde89d7a43", + "prId": "7644", + "author": "wu-hui", + "message": "Make InternalOptions internal (#7644)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/22b40539e9e3615322fc04251fab72cde89d7a43", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7644" + }, + { + "commitId": "6404b283750aed647ad7b69ec91fe500debeb9eb", + "prId": "7669", + "author": "Daniel La Rocque", + "message": "[Firestore] Add support for `regexFind` and `regexFindAll` (#7669)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/6404b283750aed647ad7b69ec91fe500debeb9eb", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7669" + } + ], + "firebase-installations": [ + { + "commitId": "b94a35345b6d9a1195f63b9a3bc612105a9ee7eb", + "prId": "7096", + "author": "Vinay Guthal", + "message": "Fix FIS ID duplication issue from backup data (#7096)\n\nCurrently, FIS apps are writing their data into the backup directory.\nThis causes an issue when a new installation retrieves data from the\nbackup, as it also picks up the previous FIS ID. This results in the FCM\nserver throwing an error, indicating that the FIS ID is already\nassociated with another instance. This PR fixes the issue for new apps\nusing FIS.\nThis PR also adds a function to the interop api which allows clearing of\nfid cache. The implementation of the same is added in\nFirebaseInstallations.java\n\nTested by creating two functions getDataFileBackup which stores it in\nthe backup directory and insertOrUpdatePersistedInstallationEntry which\nthe app data in the backup directory. So I call\ninsertOrUpdatePersistedInstallationEntry on the PersistedInstallation\nobject first and call getDataFileBackup and see that its back up\ndirectory and then i call getDataFile() which would delete the\nbackupfile and return the nonbackupfile with that data. This was tested\nby updating the PersistedInstallationTest.java . Also tested clearing\nall this data with the clear function and it works\n\n---------\n\nCo-authored-by: Rodrigo Lazo ", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/b94a35345b6d9a1195f63b9a3bc612105a9ee7eb", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7096" + } + ], + "firebase-installations-interop": [ + { + "commitId": "226a3780136d0a2efdbb3325dafa3f70311edbec", + "prId": "7824", + "author": "emilypgoogle", + "message": "Add changelog entry for m177 (#7824)\n\n", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/226a3780136d0a2efdbb3325dafa3f70311edbec", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7824" + }, + { + "commitId": "b94a35345b6d9a1195f63b9a3bc612105a9ee7eb", + "prId": "7096", + "author": "Vinay Guthal", + "message": "Fix FIS ID duplication issue from backup data (#7096)\n\nCurrently, FIS apps are writing their data into the backup directory.\nThis causes an issue when a new installation retrieves data from the\nbackup, as it also picks up the previous FIS ID. This results in the FCM\nserver throwing an error, indicating that the FIS ID is already\nassociated with another instance. This PR fixes the issue for new apps\nusing FIS.\nThis PR also adds a function to the interop api which allows clearing of\nfid cache. The implementation of the same is added in\nFirebaseInstallations.java\n\nTested by creating two functions getDataFileBackup which stores it in\nthe backup directory and insertOrUpdatePersistedInstallationEntry which\nthe app data in the backup directory. So I call\ninsertOrUpdatePersistedInstallationEntry on the PersistedInstallation\nobject first and call getDataFileBackup and see that its back up\ndirectory and then i call getDataFile() which would delete the\nbackupfile and return the nonbackupfile with that data. This was tested\nby updating the PersistedInstallationTest.java . Also tested clearing\nall this data with the clear function and it works\n\n---------\n\nCo-authored-by: Rodrigo Lazo ", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/b94a35345b6d9a1195f63b9a3bc612105a9ee7eb", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7096" + } + ], + "transport/transport-api": [ + { + "commitId": "d37e2fc38944b676ac11307aa83a57a6a888db7f", + "prId": "7667", + "author": "Vinay Guthal", + "message": "add support for experimental ids (#7667)\n\nThis PR does the following\n- Adds field experimentIdsEncryptedList to EventContext\n- Adds field experimentIdsEncryptedList to EventInternal\n- Updates Sqlite schema to include the above mentioned field\n- Updates the ExperimentIds class to have setEncryptedBlob as a\nlist (base 64 encoded of the byte array)\n- Updates test proto to match the prod proto", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/d37e2fc38944b676ac11307aa83a57a6a888db7f", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7667" + } + ], + "transport/transport-backend-cct": [ + { + "commitId": "d37e2fc38944b676ac11307aa83a57a6a888db7f", + "prId": "7667", + "author": "Vinay Guthal", + "message": "add support for experimental ids (#7667)\n\nThis PR does the following\n- Adds field experimentIdsEncryptedList to EventContext\n- Adds field experimentIdsEncryptedList to EventInternal\n- Updates Sqlite schema to include the above mentioned field\n- Updates the ExperimentIds class to have setEncryptedBlob as a\nlist (base 64 encoded of the byte array)\n- Updates test proto to match the prod proto", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/d37e2fc38944b676ac11307aa83a57a6a888db7f", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7667" + } + ], + "transport/transport-runtime": [ + { + "commitId": "d37e2fc38944b676ac11307aa83a57a6a888db7f", + "prId": "7667", + "author": "Vinay Guthal", + "message": "add support for experimental ids (#7667)\n\nThis PR does the following\n- Adds field experimentIdsEncryptedList to EventContext\n- Adds field experimentIdsEncryptedList to EventInternal\n- Updates Sqlite schema to include the above mentioned field\n- Updates the ExperimentIds class to have setEncryptedBlob as a\nlist (base 64 encoded of the byte array)\n- Updates test proto to match the prod proto", + "commitLink": "https://github.com/firebase/firebase-android-sdk/commit/d37e2fc38944b676ac11307aa83a57a6a888db7f", + "prLink": "https://github.com/firebase/firebase-android-sdk/pull/7667" + } + ] + }, + "changedLibrariesWithNoChangelog": [] +} \ No newline at end of file diff --git a/release_report.md b/release_report.md new file mode 100644 index 00000000000..7b4c5e77106 --- /dev/null +++ b/release_report.md @@ -0,0 +1,115 @@ +# Release Report +## firebase-ai + +* [AI] Add support for on-device / cloud hybrid inference (#7739) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7739) [commit](https://github.com/firebase/firebase-android-sdk/commit/75da2648c0b1322ca53c7363311f60382e5701e5) [Rodrigo Lazo] + +* [AI] Improve refdoc readability (#7745) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7745) [commit](https://github.com/firebase/firebase-android-sdk/commit/d392c78d77e66f0868cd28f11d75a4ea913bd865) [Rodrigo Lazo] + +* [AI] Standardize tool handling and function call limits (#7740) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7740) [commit](https://github.com/firebase/firebase-android-sdk/commit/d093ee445f7d8ea3ad662481384a21d099bef420) [Rodrigo Lazo] + +* Enable and test auto function calling (#7734) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7734) [commit](https://github.com/firebase/firebase-android-sdk/commit/829572116dc08466ff9bf2ce1c8d938316a1f79a) [emilypgoogle] + +* [AI] Consolidate `generateContent` and `countTokens` overloads (#7706) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7706) [commit](https://github.com/firebase/firebase-android-sdk/commit/67e32eb904b42392610bd82fb17ba640da47babe) [Rodrigo Lazo] + +* add @JvmOverloads for Tool.urlContext() and Tool.googleSearch() (#7695) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7695) [commit](https://github.com/firebase/firebase-android-sdk/commit/3dbc9b10191be29976721093720fbb295eab1f96) [Rosário P. Fernandes] + +* [AI] Don't end connection early when receiving GoAway (#7688) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7688) [commit](https://github.com/firebase/firebase-android-sdk/commit/976a718c27bfd3394bcdedd8466936e1becc825f) [Rodrigo Lazo] + +* [AI] Address missing updates from previous PRs (#7689) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7689) [commit](https://github.com/firebase/firebase-android-sdk/commit/753a812a60b76e742dd0fcce8137e9b742116c45) [Rodrigo Lazo] + +* Add Firebase AI KSP Processor (#7523) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7523) [commit](https://github.com/firebase/firebase-android-sdk/commit/338e2203fba8ca95ce71aa0924be1c7bc630de32) [David Motsonashvili] + +* [AI] Fix crash caused by unhandled goAway server message in LiveSession (#7649) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7649) [commit](https://github.com/firebase/firebase-android-sdk/commit/611bc3d11c134799a63203148966e06ae05e85a3) [YooJun Hong] + +## firebase-ai-ksp-processor + +* [AI] Add CHANGELOG.md for `firebase-ai-ksp-processor` (#7708) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7708) [commit](https://github.com/firebase/firebase-android-sdk/commit/e22820ca0b4e730b4592144f503c15c4d8d6b43a) [Rodrigo Lazo] + +* Add Firebase AI KSP Processor (#7523) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7523) [commit](https://github.com/firebase/firebase-android-sdk/commit/338e2203fba8ca95ce71aa0924be1c7bc630de32) [David Motsonashvili] + +## firebase-ai-ondevice + +* [AI] Add support for on-device / cloud hybrid inference (#7739) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7739) [commit](https://github.com/firebase/firebase-android-sdk/commit/75da2648c0b1322ca53c7363311f60382e5701e5) [Rodrigo Lazo] + +## firebase-ai-ondevice-interop + +* Update metadata for new SDKs for m177 (#7820) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7820) [commit](https://github.com/firebase/firebase-android-sdk/commit/79a4811d15feb22ed8db4e951ecf4acec6cc148f) [emilypgoogle] + +* [AI] Add support for on-device / cloud hybrid inference (#7739) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7739) [commit](https://github.com/firebase/firebase-android-sdk/commit/75da2648c0b1322ca53c7363311f60382e5701e5) [Rodrigo Lazo] + +## firebase-dataconnect + +* dataconnect: temporarily remove public apis for offline caching (#7821) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7821) [commit](https://github.com/firebase/firebase-android-sdk/commit/02d5399fcd3807e9c331d2b0e983d6cc75056941) [Denver Coneybeare] + +* dataconnect: add plumbing to serve query responses from cache (#7759) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7759) [commit](https://github.com/firebase/firebase-android-sdk/commit/9dffa7118e557e724e92c4a5767ad2703de15976) [Denver Coneybeare] + +* dataconnect: ci: upgrade data connect emulator to 3.1.3 (was 3.0.0) and firebase-tools to 15.6.0 (was 15.0.0) (#7744) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7744) [commit](https://github.com/firebase/firebase-android-sdk/commit/0fb10337936c2b0844435330502170682fe958d9) [Denver Coneybeare] + +* dataconnect: caching: add sqlite database logic (#7720) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7720) [commit](https://github.com/firebase/firebase-android-sdk/commit/11eeaad6f25074591d92e089f81558ae8ef35a9f) [Denver Coneybeare] + +* dataconnect: caching: add logic to handle query result dehydration and rehydration (#7714) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7714) [commit](https://github.com/firebase/firebase-android-sdk/commit/9fc310b6898185668b6723cc344ca4624d0d3ecc) [Denver Coneybeare] + +* dataconnect: caching public api added (#7716) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7716) [commit](https://github.com/firebase/firebase-android-sdk/commit/506716b7117de8667ae02cc993128ac072b1f19c) [Denver Coneybeare] + +## firebase-firestore + +* Fixed a race condition in test and remove null / nan related operations (#7741) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7741) [commit](https://github.com/firebase/firebase-android-sdk/commit/d7ac3fe95a0a4ae2f7caac0cf6e84e23093e9d64) [cherylEnkidu] + +* Make InternalOptions internal (#7644) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7644) [commit](https://github.com/firebase/firebase-android-sdk/commit/22b40539e9e3615322fc04251fab72cde89d7a43) [wu-hui] + +* [Firestore] Add support for `regexFind` and `regexFindAll` (#7669) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7669) [commit](https://github.com/firebase/firebase-android-sdk/commit/6404b283750aed647ad7b69ec91fe500debeb9eb) [Daniel La Rocque] + +## firebase-installations + +* Fix FIS ID duplication issue from backup data (#7096) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7096) [commit](https://github.com/firebase/firebase-android-sdk/commit/b94a35345b6d9a1195f63b9a3bc612105a9ee7eb) [Vinay Guthal] + +## firebase-installations-interop + +* Add changelog entry for m177 (#7824) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7824) [commit](https://github.com/firebase/firebase-android-sdk/commit/226a3780136d0a2efdbb3325dafa3f70311edbec) [emilypgoogle] + +* Fix FIS ID duplication issue from backup data (#7096) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7096) [commit](https://github.com/firebase/firebase-android-sdk/commit/b94a35345b6d9a1195f63b9a3bc612105a9ee7eb) [Vinay Guthal] + +## transport/transport-api + +* add support for experimental ids (#7667) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7667) [commit](https://github.com/firebase/firebase-android-sdk/commit/d37e2fc38944b676ac11307aa83a57a6a888db7f) [Vinay Guthal] + +## transport/transport-backend-cct + +* add support for experimental ids (#7667) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7667) [commit](https://github.com/firebase/firebase-android-sdk/commit/d37e2fc38944b676ac11307aa83a57a6a888db7f) [Vinay Guthal] + +## transport/transport-runtime + +* add support for experimental ids (#7667) + [pr](https://github.com/firebase/firebase-android-sdk/pull/7667) [commit](https://github.com/firebase/firebase-android-sdk/commit/d37e2fc38944b676ac11307aa83a57a6a888db7f) [Vinay Guthal] + + +## SDKs with changes, but no changelogs