From 3c0c2c465fbcc607dfb08ba9d15757ebe85299f3 Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Mon, 18 Aug 2025 12:05:02 -0400 Subject: [PATCH 1/6] add code exeuction parts --- .../firebase/ai/type/GenerateContentResponse.kt | 12 ++++++++++++ .../kotlin/com/google/firebase/ai/type/Tool.kt | 15 +++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt index be2b50f3be4..f6ed8606da6 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt @@ -44,6 +44,18 @@ public class GenerateContentResponse( candidates.first().content.parts.filterIsInstance() } + /** + * Convenience field to list all the [CodeExecutionResultPart]s in the response, if they exist. + */ + public val codeExecutionResults: List by lazy { + candidates.first().content.parts.filterIsInstance() + } + + /** Convenience field to list all the [ExecutableCodePart]s in the response, if they exist. */ + public val executableCodeList: List by lazy { + candidates.first().content.parts.filterIsInstance() + } + /** * Convenience field representing all the [InlineDataPart]s in the first candidate, if they exist. * diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt index 3b00115ebd8..8bb712c0764 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt @@ -26,12 +26,14 @@ import kotlinx.serialization.json.JsonObject public class Tool internal constructor( internal val functionDeclarations: List?, - internal val googleSearch: GoogleSearch? + internal val googleSearch: GoogleSearch?, + internal val codeExecution: JsonObject?, ) { internal fun toInternal() = Internal( functionDeclarations?.map { it.toInternal() } ?: emptyList(), - googleSearch = this.googleSearch?.toInternal() + googleSearch = this.googleSearch?.toInternal(), + codeExecution = this.codeExecution ) @Serializable internal data class Internal( @@ -49,7 +51,12 @@ internal constructor( */ @JvmStatic public fun functionDeclarations(functionDeclarations: List): Tool { - return Tool(functionDeclarations, null) + return Tool(functionDeclarations, null, null) + } + + @JvmStatic + public fun codeExecution(): Tool { + return Tool(null, null, JsonObject(emptyMap())) } /** @@ -70,7 +77,7 @@ internal constructor( */ @JvmStatic public fun googleSearch(googleSearch: GoogleSearch = GoogleSearch()): Tool { - return Tool(null, googleSearch) + return Tool(null, googleSearch, null) } } } From dd0dd2efbb49d9dc3e775a8cf5bc350907b4ffe2 Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Mon, 18 Aug 2025 12:08:00 -0400 Subject: [PATCH 2/6] update api text file --- firebase-ai/api.txt | 6 ++++++ .../src/main/kotlin/com/google/firebase/ai/type/Tool.kt | 1 + 2 files changed, 7 insertions(+) diff --git a/firebase-ai/api.txt b/firebase-ai/api.txt index 43ee5243d6a..d58c16a7717 100644 --- a/firebase-ai/api.txt +++ b/firebase-ai/api.txt @@ -369,12 +369,16 @@ package com.google.firebase.ai.type { public final class GenerateContentResponse { ctor public GenerateContentResponse(java.util.List candidates, com.google.firebase.ai.type.PromptFeedback? promptFeedback, com.google.firebase.ai.type.UsageMetadata? usageMetadata); method public java.util.List getCandidates(); + method public java.util.List getCodeExecutionResults(); + method public java.util.List getExecutableCodeList(); method public java.util.List getFunctionCalls(); method public java.util.List getInlineDataParts(); method public com.google.firebase.ai.type.PromptFeedback? getPromptFeedback(); method public String? getText(); method public com.google.firebase.ai.type.UsageMetadata? getUsageMetadata(); property public final java.util.List candidates; + property public final java.util.List codeExecutionResults; + property public final java.util.List executableCodeList; property public final java.util.List functionCalls; property public final java.util.List inlineDataParts; property public final com.google.firebase.ai.type.PromptFeedback? promptFeedback; @@ -1158,12 +1162,14 @@ package com.google.firebase.ai.type { } 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); method public static com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch()); field public static final com.google.firebase.ai.type.Tool.Companion Companion; } public static final class Tool.Companion { + method public com.google.firebase.ai.type.Tool codeExecution(); method public com.google.firebase.ai.type.Tool functionDeclarations(java.util.List functionDeclarations); method public com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch()); } diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt index 8bb712c0764..4d3a5196b1c 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt @@ -54,6 +54,7 @@ internal constructor( return Tool(functionDeclarations, null, null) } + /** Creates a [Tool] instance that allows the model to use Code Execution. */ @JvmStatic public fun codeExecution(): Tool { return Tool(null, null, JsonObject(emptyMap())) From 6868ce5160e67ca66edacbf09dc9f40c6e21c51c Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Mon, 18 Aug 2025 12:11:22 -0400 Subject: [PATCH 3/6] update tests --- .../test/java/com/google/firebase/ai/type/ToolTest.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/type/ToolTest.kt b/firebase-ai/src/test/java/com/google/firebase/ai/type/ToolTest.kt index c5213cf8613..a9f11dd2595 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/type/ToolTest.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/type/ToolTest.kt @@ -27,6 +27,7 @@ internal class ToolTest { tool.googleSearch.shouldNotBeNull() tool.functionDeclarations.shouldBeNull() + tool.codeExecution.shouldBeNull() } @Test @@ -36,5 +37,14 @@ internal class ToolTest { tool.functionDeclarations?.first() shouldBe functionDeclaration tool.googleSearch.shouldBeNull() + tool.codeExecution.shouldBeNull() + } + + @Test + fun `codeExecution() creates a tool with code execution`() { + val tool = Tool.codeExecution() + tool.codeExecution.shouldNotBeNull() + tool.functionDeclarations.shouldBeNull() + tool.googleSearch.shouldBeNull() } } From 38e7e860e290bb95a4ddf61f290e910ba410f4fd Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Mon, 18 Aug 2025 12:48:29 -0400 Subject: [PATCH 4/6] update code execution part --- .../com/google/firebase/ai/type/Part.kt | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt index 5f3e1bc12a9..bcaddc5aca1 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt @@ -39,7 +39,21 @@ public class TextPart(public val text: String) : Part { @Serializable internal data class Internal(val text: String) : InternalPart } -public class CodeExecutionResultPart(public val outcome: String, public val output: String) : Part { +/* Represents the result of the code execution */ +public enum class Outcome { + OUTCOME_UNSPECIFIED, + OUTCOME_OK, + OUTCOME_FAILED, + OUTCOME_DEADLINE_EXCEEDED +} + +/** + * Represents the code execution result from the model. + * @property outcome The result of the execution. + * @property output The stdout from the code execution, or an error message if it failed. + */ +public class CodeExecutionResultPart(public val outcome: Outcome, public val output: String) : + Part { @Serializable internal data class Internal( @@ -48,12 +62,17 @@ public class CodeExecutionResultPart(public val outcome: String, public val outp @Serializable internal data class CodeExecutionResult( - @SerialName("outcome") val outcome: String, + @SerialName("outcome") val outcome: Outcome, val output: String ) } } +/** + * Represents the code that is executed by the model. + * @property language The programming language of the code. + * @property code The source code to be executed. + */ public class ExecutableCodePart(public val language: String, public val code: String) : Part { @Serializable From b9e991baedb1ccef6caa7894af32fc8e86f9d3c2 Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Wed, 20 Aug 2025 13:25:15 -0400 Subject: [PATCH 5/6] update according to comments --- .../ai/type/GenerateContentResponse.kt | 12 ----- .../com/google/firebase/ai/type/Outcome.kt | 49 +++++++++++++++++++ .../com/google/firebase/ai/type/Part.kt | 14 ++---- 3 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Outcome.kt diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt index f6ed8606da6..be2b50f3be4 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/GenerateContentResponse.kt @@ -44,18 +44,6 @@ public class GenerateContentResponse( candidates.first().content.parts.filterIsInstance() } - /** - * Convenience field to list all the [CodeExecutionResultPart]s in the response, if they exist. - */ - public val codeExecutionResults: List by lazy { - candidates.first().content.parts.filterIsInstance() - } - - /** Convenience field to list all the [ExecutableCodePart]s in the response, if they exist. */ - public val executableCodeList: List by lazy { - candidates.first().content.parts.filterIsInstance() - } - /** * Convenience field representing all the [InlineDataPart]s in the first candidate, if they exist. * diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Outcome.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Outcome.kt new file mode 100644 index 00000000000..6cb41b4e91a --- /dev/null +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Outcome.kt @@ -0,0 +1,49 @@ +package com.google.firebase.ai.type + +import com.google.firebase.ai.common.util.FirstOrdinalSerializer +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable + +/** Represents the result of the code execution */ +public class Outcome private constructor(public val ordinal: Int) { + + @Serializable(Internal.Serializer::class) + internal enum class Internal { + OUTCOME_UNSPECIFIED, + OUTCOME_OK, + OUTCOME_FAILED, + OUTCOME_DEADLINE_EXCEEDED; + + internal object Serializer : KSerializer by FirstOrdinalSerializer(Internal::class) + + internal fun toPublic() = + when (this) { + OUTCOME_UNSPECIFIED -> Outcome.OUTCOME_UNSPECIFIED + OUTCOME_OK -> Outcome.OUTCOME_OK + OUTCOME_FAILED -> Outcome.OUTCOME_FAILED + OUTCOME_DEADLINE_EXCEEDED -> Outcome.OUTCOME_DEADLINE_EXCEEDED + } + } + + internal fun toInternal() = + when (this) { + OUTCOME_UNSPECIFIED -> Internal.OUTCOME_UNSPECIFIED + OUTCOME_OK -> Internal.OUTCOME_OK + OUTCOME_FAILED -> Internal.OUTCOME_FAILED + else -> Internal.OUTCOME_DEADLINE_EXCEEDED + } + public companion object { + + /** Represents that the code execution outcome is unspecified */ + @JvmField public val OUTCOME_UNSPECIFIED: Outcome = Outcome(0) + + /** Represents that the code execution succeeded */ + @JvmField public val OUTCOME_OK: Outcome = Outcome(1) + + /** Represents that the code execution failed */ + @JvmField public val OUTCOME_FAILED: Outcome = Outcome(2) + + /** Represents that the code execution timed out */ + @JvmField public val OUTCOME_DEADLINE_EXCEEDED: Outcome = Outcome(3) + } +} diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt index bcaddc5aca1..cf735e565ed 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt @@ -39,14 +39,6 @@ public class TextPart(public val text: String) : Part { @Serializable internal data class Internal(val text: String) : InternalPart } -/* Represents the result of the code execution */ -public enum class Outcome { - OUTCOME_UNSPECIFIED, - OUTCOME_OK, - OUTCOME_FAILED, - OUTCOME_DEADLINE_EXCEEDED -} - /** * Represents the code execution result from the model. * @property outcome The result of the execution. @@ -62,7 +54,7 @@ public class CodeExecutionResultPart(public val outcome: Outcome, public val out @Serializable internal data class CodeExecutionResult( - @SerialName("outcome") val outcome: Outcome, + @SerialName("outcome") val outcome: Outcome.Internal, val output: String ) } @@ -261,7 +253,7 @@ internal fun Part.toInternal(): InternalPart { ExecutableCodePart.Internal(ExecutableCodePart.Internal.ExecutableCode(language, code)) is CodeExecutionResultPart -> CodeExecutionResultPart.Internal( - CodeExecutionResultPart.Internal.CodeExecutionResult(outcome, output) + CodeExecutionResultPart.Internal.CodeExecutionResult(outcome.toInternal(), output) ) else -> throw com.google.firebase.ai.type.SerializationException( @@ -300,7 +292,7 @@ internal fun InternalPart.toPublic(): Part { is ExecutableCodePart.Internal -> ExecutableCodePart(executableCode.language, executableCode.code) is CodeExecutionResultPart.Internal -> - CodeExecutionResultPart(codeExecutionResult.outcome, codeExecutionResult.output) + CodeExecutionResultPart(codeExecutionResult.outcome.toPublic(), codeExecutionResult.output) else -> throw com.google.firebase.ai.type.SerializationException( "Unsupported part type \"${javaClass.simpleName}\" provided. This model may not be supported by this SDK." From 82ba5a4747f89d12c504072eeaff8db81cebffa7 Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Wed, 20 Aug 2025 13:42:25 -0400 Subject: [PATCH 6/6] update parts --- .../com/google/firebase/ai/type/Part.kt | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt index 34898631479..ae51be9ae5a 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt @@ -53,15 +53,21 @@ internal constructor( ) : InternalPart } +/** + * Represents the code execution result from the model. + * @property outcome The result of the execution. + * @property output The stdout from the code execution, or an error message if it failed. + * @property isThought Indicates whether the response is a thought. + */ public class CodeExecutionResultPart internal constructor( - public val outcome: String, + public val outcome: Outcome, public val output: String, public override val isThought: Boolean, internal val thoughtSignature: String? ) : Part { - public constructor(outcome: String, output: String) : this(outcome, output, false, null) + public constructor(outcome: Outcome, output: String) : this(outcome, output, false, null) @Serializable internal data class Internal( @@ -72,12 +78,18 @@ internal constructor( @Serializable internal data class CodeExecutionResult( - @SerialName("outcome") val outcome: String, + @SerialName("outcome") val outcome: Outcome.Internal, val output: String ) } } +/** + * Represents the code that is executed by the model. + * @property language The programming language of the code. + * @property code The source code to be executed. + * @property isThought Indicates whether the response is a thought. + */ public class ExecutableCodePart internal constructor( public val language: String, @@ -354,7 +366,7 @@ internal fun Part.toInternal(): InternalPart { ) is CodeExecutionResultPart -> CodeExecutionResultPart.Internal( - CodeExecutionResultPart.Internal.CodeExecutionResult(outcome, output), + CodeExecutionResultPart.Internal.CodeExecutionResult(outcome.toInternal(), output), isThought, thoughtSignature ) @@ -410,7 +422,7 @@ internal fun InternalPart.toPublic(): Part { ) is CodeExecutionResultPart.Internal -> CodeExecutionResultPart( - codeExecutionResult.outcome, + codeExecutionResult.outcome.toPublic(), codeExecutionResult.output, thought ?: false, thoughtSignature