diff --git a/firebase-ai/CHANGELOG.md b/firebase-ai/CHANGELOG.md index 7c4c924ab9f..73591b8e3e2 100644 --- a/firebase-ai/CHANGELOG.md +++ b/firebase-ai/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +- [feature] Added support for configuring thinking levels with Gemini 3 series + models and onwards. (#7599) + # 17.7.0 - [changed] Added `LiveAudioConversationConfig` to control different aspects of the conversation diff --git a/firebase-ai/api.txt b/firebase-ai/api.txt index 357fb1c2a8b..c77d286500d 100644 --- a/firebase-ai/api.txt +++ b/firebase-ai/api.txt @@ -1289,12 +1289,26 @@ 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; + 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 { + } + 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); 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..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 @@ -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,14 +41,34 @@ 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 { + if (thinkingLevel != null) + 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. * @@ -56,15 +82,20 @@ private constructor( } 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..b124ca57296 --- /dev/null +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/ThinkingLevel.kt @@ -0,0 +1,53 @@ +/* + * 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) { + MINIMAL -> Internal.MINIMAL + LOW -> Internal.LOW + MEDIUM -> Internal.MEDIUM + HIGH -> Internal.HIGH + else -> throw makeMissingCaseException("ThinkingLevel", ordinal) + } + + @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(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(3) + } +}