From acfdb04013747e79a6ff4ec33a8c6986a03ea71b Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Tue, 11 Nov 2025 15:02:04 -0500 Subject: [PATCH 1/7] [Firebase AI] Add support for `thinkingLevel` in `ThinkingConfig` --- .../Sources/Types/Public/ThinkingConfig.swift | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift index a339f8fa1d1..6fe68d04a02 100644 --- a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift +++ b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift @@ -37,6 +37,9 @@ public struct ThinkingConfig: Sendable { /// feature or if the specified budget is not within the model's supported range. let thinkingBudget: Int? + /// The level of thoughts tokens that the model should generate. + let thinkingLevel: ThinkingLevel? + /// Whether summaries of the model's "thoughts" are included in responses. /// /// When `includeThoughts` is set to `true`, the model will return a summary of its internal @@ -54,8 +57,61 @@ public struct ThinkingConfig: Sendable { /// - includeThoughts: If true, summaries of the model's "thoughts" are included in responses. public init(thinkingBudget: Int? = nil, includeThoughts: Bool? = nil) { self.thinkingBudget = thinkingBudget + thinkingLevel = nil self.includeThoughts = includeThoughts } + + /// Initializes a new `ThinkingConfig`. + /// + /// - Parameters: + /// - thinkingLevel: The level of thoughts tokens that the model should generate. + /// - includeThoughts: If true, summaries of the model's "thoughts" are included in responses. + public init(thinkingLevel: ThinkingLevel, includeThoughts: Bool? = nil) { + switch thinkingLevel { + case .dynamic: + thinkingBudget = -1 + self.thinkingLevel = nil + case .none: + thinkingBudget = 0 + self.thinkingLevel = nil + default: + thinkingBudget = nil + self.thinkingLevel = thinkingLevel + } + self.includeThoughts = includeThoughts + } +} + +public extension ThinkingConfig { + /// The thinking level for the model. + struct ThinkingLevel: EncodableProtoEnum, Equatable { + enum Kind: String { + case low = "LOW" + case medium = "MEDIUM" + case high = "HIGH" + + // The following cases do not exist in the backend and must be mapped to a `thinkingBudget`. + case dynamic = "DYNAMIC" // Client-only enum value that maps to a thinking budget of -1. + case none = "NONE" // Client-only enum value that maps to a thinking budget of 0. + } + + /// The model will adjust the budget based on the complexity of the request. + public static let dynamic = ThinkingLevel(kind: .dynamic) + + /// Thinking is disabled. + public static let none = ThinkingLevel(kind: .none) + + /// Low thinking level. + public static let low = ThinkingLevel(kind: .low) + + /// Medium thinking level. + public static let medium = ThinkingLevel(kind: .medium) + + /// High thinking level. + public static let high = ThinkingLevel(kind: .high) + + var rawValue: String + } } // MARK: - Codable Conformances From d4b8053c6b42a38479d62762bf5b60746880b407 Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Tue, 11 Nov 2025 17:27:56 -0500 Subject: [PATCH 2/7] Remove `dynamic` and `none` enum values --- .../Sources/Types/Public/ThinkingConfig.swift | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift index 6fe68d04a02..f6c8e76e5c5 100644 --- a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift +++ b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift @@ -67,17 +67,8 @@ public struct ThinkingConfig: Sendable { /// - thinkingLevel: The level of thoughts tokens that the model should generate. /// - includeThoughts: If true, summaries of the model's "thoughts" are included in responses. public init(thinkingLevel: ThinkingLevel, includeThoughts: Bool? = nil) { - switch thinkingLevel { - case .dynamic: - thinkingBudget = -1 - self.thinkingLevel = nil - case .none: - thinkingBudget = 0 - self.thinkingLevel = nil - default: - thinkingBudget = nil - self.thinkingLevel = thinkingLevel - } + thinkingBudget = nil + self.thinkingLevel = thinkingLevel self.includeThoughts = includeThoughts } } @@ -89,18 +80,8 @@ public extension ThinkingConfig { case low = "LOW" case medium = "MEDIUM" case high = "HIGH" - - // The following cases do not exist in the backend and must be mapped to a `thinkingBudget`. - case dynamic = "DYNAMIC" // Client-only enum value that maps to a thinking budget of -1. - case none = "NONE" // Client-only enum value that maps to a thinking budget of 0. } - /// The model will adjust the budget based on the complexity of the request. - public static let dynamic = ThinkingLevel(kind: .dynamic) - - /// Thinking is disabled. - public static let none = ThinkingLevel(kind: .none) - /// Low thinking level. public static let low = ThinkingLevel(kind: .low) From 2216000e52848535f3231c0fbd7b77936b46a9cc Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Fri, 28 Nov 2025 14:22:00 -0500 Subject: [PATCH 3/7] Remove unsupported `medium` thinking level --- FirebaseAI/Sources/Types/Public/ThinkingConfig.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift index f6c8e76e5c5..8d179d554f6 100644 --- a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift +++ b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift @@ -78,16 +78,12 @@ public extension ThinkingConfig { struct ThinkingLevel: EncodableProtoEnum, Equatable { enum Kind: String { case low = "LOW" - case medium = "MEDIUM" case high = "HIGH" } /// Low thinking level. public static let low = ThinkingLevel(kind: .low) - /// Medium thinking level. - public static let medium = ThinkingLevel(kind: .medium) - /// High thinking level. public static let high = ThinkingLevel(kind: .high) From f18166ed4ac6517f16325124888293448b9071e0 Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Fri, 28 Nov 2025 15:17:24 -0500 Subject: [PATCH 4/7] Add documentation comments --- .../Sources/Types/Public/ThinkingConfig.swift | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift index 8d179d554f6..053785295fa 100644 --- a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift +++ b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift @@ -14,9 +14,10 @@ /// Configuration for controlling the "thinking" behavior of compatible Gemini models. /// -/// Certain models, like Gemini 2.5 Flash and Pro, utilize a thinking process before generating a -/// response. This allows them to reason through complex problems and plan a more coherent and -/// accurate answer. +/// 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 struct ThinkingConfig: Sendable { /// The thinking budget in tokens. /// @@ -27,12 +28,6 @@ public struct ThinkingConfig: Sendable { /// If you don't specify a budget (`nil`), the model will automatically determine the appropriate /// amount of thinking based on the complexity of the prompt. /// - /// **Model-Specific Behavior:** - /// - **Gemini 2.5 Flash:** The budget can range from `0` to `24576`. Setting the budget to `0` - /// disables the thinking process, which prioritizes the lowest latency and cost. - /// - **Gemini 2.5 Pro:** The budget must be an integer between `128` and `32768`. Thinking cannot - /// be disabled for this model. - /// /// An error will be thrown if you set a thinking budget for a model that does not support this /// feature or if the specified budget is not within the model's supported range. let thinkingBudget: Int? @@ -61,10 +56,18 @@ public struct ThinkingConfig: Sendable { self.includeThoughts = includeThoughts } - /// Initializes a new `ThinkingConfig`. + /// Initializes a `ThinkingConfig` with a ``ThinkingLevel``. + /// + /// If you don't specify a thinking level, Gemini will use the model's default dynamic thinking + /// level. + /// + /// > Important: Gemini 2.5 series models do not support thinking levels; use + /// > ``init(thinkingBudget:includeThoughts:)`` to set a thinking budget instead. /// /// - Parameters: - /// - thinkingLevel: The level of thoughts tokens that the model should generate. + /// - thinkingLevel: A preset that controls the model's "thinking" process. Use + /// ``ThinkingLevel/low`` for faster responses on less complex tasks, and + /// ``ThinkingLevel/high`` for better reasoning on more complex tasks. /// - includeThoughts: If true, summaries of the model's "thoughts" are included in responses. public init(thinkingLevel: ThinkingLevel, includeThoughts: Bool? = nil) { thinkingBudget = nil @@ -74,17 +77,24 @@ public struct ThinkingConfig: Sendable { } public extension ThinkingConfig { - /// The thinking level for the model. + /// A preset that balances the trade-off between reasoning quality and response speed for a + /// model's "thinking" process. struct ThinkingLevel: EncodableProtoEnum, Equatable { enum Kind: String { case low = "LOW" case high = "HIGH" } - /// Low thinking level. + /// A low thinking level optimized for speed and efficiency. + /// + /// This level is suitable for tasks that are less complex and do not require deep reasoning. It + /// provides a faster response time and lower computational cost. public static let low = ThinkingLevel(kind: .low) - /// High thinking level. + /// A high thinking level designed for complex tasks that require deep reasoning and planning. + /// + /// This level may result in higher quality, more coherent, and accurate responses, but with + /// increased latency and computational cost. public static let high = ThinkingLevel(kind: .high) var rawValue: String From 71732c38f5441253e07b0728faf0690619d7d260 Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Fri, 28 Nov 2025 15:47:46 -0500 Subject: [PATCH 5/7] Add more docs --- FirebaseAI/Sources/Types/Public/ThinkingConfig.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift index 053785295fa..bf7ab4e4f58 100644 --- a/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift +++ b/FirebaseAI/Sources/Types/Public/ThinkingConfig.swift @@ -49,6 +49,14 @@ public struct ThinkingConfig: Sendable { /// /// - Parameters: /// - thinkingBudget: The maximum number of tokens to be used for the model's thinking process. + /// The range of [supported thinking budget values + /// ](https://firebase.google.com/docs/ai-logic/thinking#supported-thinking-budget-values) + /// depends on the model. + /// - To use the default thinking budget or thinking level for a model, set this value to + /// `nil` or omit it. + /// - To disable thinking, when supported by the model, set this value to `0`. + /// - To use dynamic thinking, allowing the model to decide on the thinking budget based on + /// the task, set this value to `-1`. /// - includeThoughts: If true, summaries of the model's "thoughts" are included in responses. public init(thinkingBudget: Int? = nil, includeThoughts: Bool? = nil) { self.thinkingBudget = thinkingBudget From 67f4c2d9165f6cfe6a8ace83d145219604a3139a Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Fri, 28 Nov 2025 15:48:00 -0500 Subject: [PATCH 6/7] Add a CHANGELOG entry --- FirebaseAI/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FirebaseAI/CHANGELOG.md b/FirebaseAI/CHANGELOG.md index 8a923e651e4..6a265d51932 100644 --- a/FirebaseAI/CHANGELOG.md +++ b/FirebaseAI/CHANGELOG.md @@ -1,4 +1,6 @@ # 12.7.0 +- [feature] Added support for configuring thinking levels with Gemini 3 series + mdels and onwards. (#15557) - [fixed] Fixed support for API keys with iOS+ app [Bundle ID restrictions](https://docs.cloud.google.com/docs/authentication/api-keys#adding-application-restrictions) by setting the `x-ios-bundle-identifier` header. (#15475) From c812dfc6eeee2069af5464273c946243500132c3 Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Tue, 16 Dec 2025 11:01:21 -0500 Subject: [PATCH 7/7] Fix typo in FirebaseAI/CHANGELOG.md Co-authored-by: Rodrigo Lazo --- FirebaseAI/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseAI/CHANGELOG.md b/FirebaseAI/CHANGELOG.md index 6a265d51932..8b821bc9427 100644 --- a/FirebaseAI/CHANGELOG.md +++ b/FirebaseAI/CHANGELOG.md @@ -1,6 +1,6 @@ # 12.7.0 - [feature] Added support for configuring thinking levels with Gemini 3 series - mdels and onwards. (#15557) + models and onwards. (#15557) - [fixed] Fixed support for API keys with iOS+ app [Bundle ID restrictions](https://docs.cloud.google.com/docs/authentication/api-keys#adding-application-restrictions) by setting the `x-ios-bundle-identifier` header. (#15475)