From ac7294be2144eaf10dadc500ebd4a5371b315442 Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Thu, 11 Dec 2025 00:08:49 -0500 Subject: [PATCH 1/7] [AI] Add support for Thinking Levels Gemini 2.5 series models and newer utilize a thinking process before generating a response. Thinking levels are an alternative to thinking budgets as they are preset values that Gemini 3 models can use to limit how many tokens are dedicated to the thinking process. The [thinking documentation](https://firebase.google.com/docs/ai-logic/thinking) gives more details. --- .../google/firebase/ai/type/ThinkingConfig.kt | 39 +++++++++++++--- .../google/firebase/ai/type/ThinkingLevel.kt | 44 +++++++++++++++++++ 2 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt index b220de57e86..6d2680a0411 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt @@ -19,11 +19,17 @@ package com.google.firebase.ai.type import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -/** Configuration parameters for thinking features. */ +/** + * Gemini 2.5 series models and newer utilize a thinking process before generating a response. This + * allows them to reason through complex problems and plan a more coherent and accurate answer. See + * the [thinking documentation](https://firebase.google.com/docs/ai-logic/thinking) for more + * details. + */ public class ThinkingConfig private constructor( internal val thinkingBudget: Int? = null, - internal val includeThoughts: Boolean? = null + internal val includeThoughts: Boolean? = null, + internal val thinkingLevel: ThinkingLevel? = null ) { public class Builder() { @@ -35,11 +41,19 @@ private constructor( @set:JvmSynthetic // hide void setter from Java public var includeThoughts: Boolean? = null + @JvmField + @set:JvmSynthetic // hide void setter from Java + public var thinkingLevel: ThinkingLevel? = null + /** - * Indicates the thinking budget in tokens. `0` is disabled. `-1` is dynamic. The default values - * and allowed ranges are model dependent. + * Indicates the thinking budget in tokens. + * + * Use `0` for disabled, and `-1` for dynamic. The range of + * [supported thinking budget values](https://firebase.google.com/docs/ai-logic/thinking#supported-thinking-budget-values) + * depends on the model. */ public fun setThinkingBudget(thinkingBudget: Int): Builder = apply { + assert(thinkingLevel == null) { "Cannot set both `thinkingBudget` and `thinkingLevel`" } this.thinkingBudget = thinkingBudget } @@ -55,16 +69,27 @@ private constructor( this.includeThoughts = includeThoughts } + /** Indicates the thinking budget based in Levels. */ + public fun setThinkingLevel(thinkingLevel: ThinkingLevel): Builder = apply { + assert(thinkingBudget == null) { "Cannot set both `thinkingBudget` and `thinkingLevel`" } + this.thinkingLevel = thinkingLevel + } + public fun build(): ThinkingConfig = - ThinkingConfig(thinkingBudget = thinkingBudget, includeThoughts = includeThoughts) + ThinkingConfig( + thinkingBudget = thinkingBudget, + includeThoughts = includeThoughts, + thinkingLevel = thinkingLevel + ) } - internal fun toInternal() = Internal(thinkingBudget, includeThoughts) + internal fun toInternal() = Internal(thinkingBudget, includeThoughts, thinkingLevel?.toInternal()) @Serializable internal data class Internal( @SerialName("thinking_budget") val thinkingBudget: Int? = null, - val includeThoughts: Boolean? = null + val includeThoughts: Boolean? = null, + @SerialName("thinking_level") val thinkingLevel: ThinkingLevel.Internal? = null, ) } diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt new file mode 100644 index 00000000000..b745df90eab --- /dev/null +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.ai.type + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** Specifies the quality of the thinking response. */ +public class ThinkingLevel private constructor(public val ordinal: Int) { + internal fun toInternal() = + when (this) { + LOW -> Internal.LOW + HIGH -> Internal.HIGH + else -> throw makeMissingCaseException("ThinkingLevel", ordinal) + } + + @Serializable + internal enum class Internal { + @SerialName("THINKING_LEVEL_UNSPECIFIED") UNSPECIFIED, + LOW, + HIGH, + } + public companion object { + /** A lower quality thinking response, which provides low latency. */ + @JvmField public val LOW: ThinkingLevel = ThinkingLevel(0) + + /** A higher quality thinking response, which may increase latency. */ + @JvmField public val HIGH: ThinkingLevel = ThinkingLevel(1) + } +} From 86cdb5c818e6f87fce79640799b7d27a9ddf2046 Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Thu, 11 Dec 2025 00:15:46 -0500 Subject: [PATCH 2/7] Add changelog entry --- firebase-ai/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firebase-ai/CHANGELOG.md b/firebase-ai/CHANGELOG.md index 70e0cf2cf53..4e84bd09e1f 100644 --- a/firebase-ai/CHANGELOG.md +++ b/firebase-ai/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- [feature] Added support for configuring thinking levels with Gemini 3 series + mdels and onwards. - [changed] Added `LiveAudioConversationConfig` to control different aspects of the conversation while using the `startAudioConversation` function. - [fixed] Fixed an issue causing streaming chat interactions to drop thought signatures. (#7562) From 0fa258bee35c91faa868e30fbaa6bb01250d526f Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Thu, 11 Dec 2025 00:18:54 -0500 Subject: [PATCH 3/7] Add PR number to changelog entry --- firebase-ai/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-ai/CHANGELOG.md b/firebase-ai/CHANGELOG.md index 4e84bd09e1f..389f4fadbe1 100644 --- a/firebase-ai/CHANGELOG.md +++ b/firebase-ai/CHANGELOG.md @@ -1,7 +1,7 @@ # Unreleased - [feature] Added support for configuring thinking levels with Gemini 3 series - mdels and onwards. + mdels and onwards. (#7599) - [changed] Added `LiveAudioConversationConfig` to control different aspects of the conversation while using the `startAudioConversation` function. - [fixed] Fixed an issue causing streaming chat interactions to drop thought signatures. (#7562) From 6f3645c3ef2db1d5d0582a32584a2cd7a38b83fc Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Thu, 11 Dec 2025 00:34:00 -0500 Subject: [PATCH 4/7] Throw exception instead of using assert --- .../kotlin/com/google/firebase/ai/type/ThinkingConfig.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt index 6d2680a0411..2c21948cbb9 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt @@ -53,7 +53,8 @@ private constructor( * depends on the model. */ public fun setThinkingBudget(thinkingBudget: Int): Builder = apply { - assert(thinkingLevel == null) { "Cannot set both `thinkingBudget` and `thinkingLevel`" } + if (thinkingLevel != null) + throw IllegalArgumentException("Cannot set both `thinkingBudget` and `thinkingLevel`") this.thinkingBudget = thinkingBudget } @@ -71,7 +72,8 @@ private constructor( /** Indicates the thinking budget based in Levels. */ public fun setThinkingLevel(thinkingLevel: ThinkingLevel): Builder = apply { - assert(thinkingBudget == null) { "Cannot set both `thinkingBudget` and `thinkingLevel`" } + if (thinkingBudget != null) + throw IllegalArgumentException("Cannot set both `thinkingBudget` and `thinkingLevel`") this.thinkingLevel = thinkingLevel } From 05216ef56d13f5c7160fa378cb389b08e09deffd Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Thu, 11 Dec 2025 09:45:58 -0500 Subject: [PATCH 5/7] Update api.txt --- firebase-ai/api.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/firebase-ai/api.txt b/firebase-ai/api.txt index 357fb1c2a8b..0ce07c6b221 100644 --- a/firebase-ai/api.txt +++ b/firebase-ai/api.txt @@ -1289,12 +1289,24 @@ package com.google.firebase.ai.type { method public com.google.firebase.ai.type.ThinkingConfig build(); method public com.google.firebase.ai.type.ThinkingConfig.Builder setIncludeThoughts(boolean includeThoughts); method public com.google.firebase.ai.type.ThinkingConfig.Builder setThinkingBudget(int thinkingBudget); + method public com.google.firebase.ai.type.ThinkingConfig.Builder setThinkingLevel(com.google.firebase.ai.type.ThinkingLevel thinkingLevel); } public final class ThinkingConfigKt { method public static com.google.firebase.ai.type.ThinkingConfig thinkingConfig(kotlin.jvm.functions.Function1 init); } + public final class ThinkingLevel { + method public int getOrdinal(); + property public final int ordinal; + field public static final com.google.firebase.ai.type.ThinkingLevel.Companion Companion; + field public static final com.google.firebase.ai.type.ThinkingLevel HIGH; + field public static final com.google.firebase.ai.type.ThinkingLevel LOW; + } + + public static final class ThinkingLevel.Companion { + } + public final class Tool { method public static com.google.firebase.ai.type.Tool codeExecution(); method public static com.google.firebase.ai.type.Tool functionDeclarations(java.util.List functionDeclarations); From 8cc20dc9dfe4cd05924f9f996a47e8c9a47fa93f Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Mon, 15 Dec 2025 10:39:27 -0500 Subject: [PATCH 6/7] Update error message and add new levels. --- .../google/firebase/ai/type/ThinkingConfig.kt | 20 +++++++++++-------- .../google/firebase/ai/type/ThinkingLevel.kt | 13 ++++++++++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt index 2c21948cbb9..932dd8d79eb 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingConfig.kt @@ -54,10 +54,21 @@ private constructor( */ public fun setThinkingBudget(thinkingBudget: Int): Builder = apply { if (thinkingLevel != null) - throw IllegalArgumentException("Cannot set both `thinkingBudget` and `thinkingLevel`") + throw IllegalArgumentException( + "`thinkingLevel` already set. Cannot set both `thinkingBudget` and `thinkingLevel`" + ) this.thinkingBudget = thinkingBudget } + /** Indicates the thinking budget based in Levels. */ + public fun setThinkingLevel(thinkingLevel: ThinkingLevel): Builder = apply { + if (thinkingBudget != null) + throw IllegalArgumentException( + "`thinkingBudget` already set. Cannot set both `thinkingBudget` and `thinkingLevel`" + ) + this.thinkingLevel = thinkingLevel + } + /** * Indicates whether to request the model to include the thoughts parts in the response. * @@ -70,13 +81,6 @@ private constructor( this.includeThoughts = includeThoughts } - /** Indicates the thinking budget based in Levels. */ - public fun setThinkingLevel(thinkingLevel: ThinkingLevel): Builder = apply { - if (thinkingBudget != null) - throw IllegalArgumentException("Cannot set both `thinkingBudget` and `thinkingLevel`") - this.thinkingLevel = thinkingLevel - } - public fun build(): ThinkingConfig = ThinkingConfig( thinkingBudget = thinkingBudget, diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt index b745df90eab..b124ca57296 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt @@ -23,7 +23,9 @@ import kotlinx.serialization.Serializable public class ThinkingLevel private constructor(public val ordinal: Int) { internal fun toInternal() = when (this) { + MINIMAL -> Internal.MINIMAL LOW -> Internal.LOW + MEDIUM -> Internal.MEDIUM HIGH -> Internal.HIGH else -> throw makeMissingCaseException("ThinkingLevel", ordinal) } @@ -31,14 +33,21 @@ public class ThinkingLevel private constructor(public val ordinal: Int) { @Serializable internal enum class Internal { @SerialName("THINKING_LEVEL_UNSPECIFIED") UNSPECIFIED, + MINIMAL, LOW, + MEDIUM, HIGH, } public companion object { + /** A minimal quality thinking response, which provides the lowest latency. */ + @JvmField public val MINIMAL: ThinkingLevel = ThinkingLevel(0) /** A lower quality thinking response, which provides low latency. */ - @JvmField public val LOW: ThinkingLevel = ThinkingLevel(0) + @JvmField public val LOW: ThinkingLevel = ThinkingLevel(1) + + /** A medium quality thinking response. */ + @JvmField public val MEDIUM: ThinkingLevel = ThinkingLevel(2) /** A higher quality thinking response, which may increase latency. */ - @JvmField public val HIGH: ThinkingLevel = ThinkingLevel(1) + @JvmField public val HIGH: ThinkingLevel = ThinkingLevel(3) } } From 84830d433df8eeeb2ef27c2d6c6f299bf29f051d Mon Sep 17 00:00:00 2001 From: Rodrigo Lazo Paz Date: Mon, 15 Dec 2025 10:40:06 -0500 Subject: [PATCH 7/7] Update api.txt --- firebase-ai/api.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firebase-ai/api.txt b/firebase-ai/api.txt index 0ce07c6b221..c77d286500d 100644 --- a/firebase-ai/api.txt +++ b/firebase-ai/api.txt @@ -1302,6 +1302,8 @@ package com.google.firebase.ai.type { field public static final com.google.firebase.ai.type.ThinkingLevel.Companion Companion; field public static final com.google.firebase.ai.type.ThinkingLevel HIGH; field public static final com.google.firebase.ai.type.ThinkingLevel LOW; + field public static final com.google.firebase.ai.type.ThinkingLevel MEDIUM; + field public static final com.google.firebase.ai.type.ThinkingLevel MINIMAL; } public static final class ThinkingLevel.Companion {