Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ private constructor(
internal val responseMimeType: String?,
internal val responseSchema: Schema?,
internal val responseModalities: List<ResponseModality>?,
internal val thinkingConfig: ThinkingConfig?,
) {

/**
Expand Down Expand Up @@ -121,6 +122,8 @@ private constructor(
*
* @property responseModalities See [GenerationConfig.responseModalities].
*
* @property thinkingConfig See [GenerationConfig.thinkingConfig].
*
* @see [generationConfig]
*/
public class Builder {
Expand All @@ -135,6 +138,7 @@ private constructor(
@JvmField public var responseMimeType: String? = null
@JvmField public var responseSchema: Schema? = null
@JvmField public var responseModalities: List<ResponseModality>? = null
@JvmField public var thinkingConfig: ThinkingConfig? = null

public fun setTemperature(temperature: Float?): Builder = apply {
this.temperature = temperature
Expand Down Expand Up @@ -165,6 +169,9 @@ private constructor(
public fun setResponseModalities(responseModalities: List<ResponseModality>?): Builder = apply {
this.responseModalities = responseModalities
}
public fun setThinkingConfig(thinkingConfig: ThinkingConfig?): Builder = apply {
this.thinkingConfig = thinkingConfig
}

/** Create a new [GenerationConfig] with the attached arguments. */
public fun build(): GenerationConfig =
Expand All @@ -179,7 +186,8 @@ private constructor(
frequencyPenalty = frequencyPenalty,
responseMimeType = responseMimeType,
responseSchema = responseSchema,
responseModalities = responseModalities
responseModalities = responseModalities,
thinkingConfig = thinkingConfig
)
}

Expand All @@ -195,7 +203,8 @@ private constructor(
presencePenalty = presencePenalty,
responseMimeType = responseMimeType,
responseSchema = responseSchema?.toInternal(),
responseModalities = responseModalities?.map { it.toInternal() }
responseModalities = responseModalities?.map { it.toInternal() },
thinkingConfig = thinkingConfig?.toInternal()
)

@Serializable
Expand All @@ -210,7 +219,8 @@ private constructor(
@SerialName("presence_penalty") val presencePenalty: Float? = null,
@SerialName("frequency_penalty") val frequencyPenalty: Float? = null,
@SerialName("response_schema") val responseSchema: Schema.Internal? = null,
@SerialName("response_modalities") val responseModalities: List<String>? = null
@SerialName("response_modalities") val responseModalities: List<String>? = null,
@SerialName("thinking_config") val thinkingConfig: ThinkingConfig.Internal? = null
)

public companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2024 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

/**
* Configuration for the "Thinking" feature, which allows the model to process information more
* deeply before responding.
*
* @property includeThoughts Whether to include the model's thoughts in the response.
*/
public class ThinkingConfig(
public val includeThoughts: Boolean = false,
) {
Comment on lines +28 to +30
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better usability and adherence to Kotlin idioms for value-holding classes, consider converting ThinkingConfig into a data class. This will automatically provide equals(), hashCode(), toString(), and copy() methods, which is useful for a configuration object.

Suggested change
public class ThinkingConfig(
public val includeThoughts: Boolean = false,
) {
public data class ThinkingConfig(
public val includeThoughts: Boolean = false,
) {

internal fun toInternal() = Internal(includeThoughts = includeThoughts)

@Serializable
internal data class Internal(
@SerialName("include_thoughts") val includeThoughts: Boolean,
)
}
26 changes: 21 additions & 5 deletions firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.google.firebase.ai.type

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject

Expand All @@ -26,13 +27,22 @@ import kotlinx.serialization.json.JsonObject
* @param functionDeclarations The set of functions that this tool allows the model access to
*/
public class Tool
internal constructor(internal val functionDeclarations: List<FunctionDeclaration>?) {
internal fun toInternal() = Internal(functionDeclarations?.map { it.toInternal() } ?: emptyList())
internal constructor(
internal val functionDeclarations: List<FunctionDeclaration>? = null,
internal val googleSearchRetrieval: Boolean = false
) {
internal fun toInternal() =
Internal(
functionDeclarations = functionDeclarations?.map { it.toInternal() },
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This change alters the serialization of functionDeclarations. Previously, a null list was sent as an empty array ([]). Now, it will be null and likely omitted from the JSON payload. This is a potential breaking change for the backend API. To maintain API compatibility, the previous behavior of sending an empty list should be preserved.

Suggested change
functionDeclarations = functionDeclarations?.map { it.toInternal() },
functionDeclarations = functionDeclarations?.map { it.toInternal() } ?: emptyList(),

googleSearchRetrieval = if (googleSearchRetrieval) JsonObject(emptyMap()) else null
)

@Serializable
internal data class Internal(
val functionDeclarations: List<FunctionDeclaration.Internal>? = null,
@SerialName("function_declarations") val functionDeclarations: List<FunctionDeclaration.Internal>? = null,
// This is a json object because it is not possible to make a data class with no parameters.
val codeExecution: JsonObject? = null,
@SerialName("code_execution") val codeExecution: JsonObject? = null,
@SerialName("google_search_retrieval") val googleSearchRetrieval: JsonObject? = null,
)
public companion object {

Expand All @@ -43,7 +53,13 @@ internal constructor(internal val functionDeclarations: List<FunctionDeclaration
*/
@JvmStatic
public fun functionDeclarations(functionDeclarations: List<FunctionDeclaration>): Tool {
return Tool(functionDeclarations)
return Tool(functionDeclarations = functionDeclarations)
}

/** Creates a [Tool] instance that provides the model with access to Google Search. */
@JvmStatic
public fun googleSearchRetrieval(): Tool {
return Tool(googleSearchRetrieval = true)
}
}
}
Loading