diff --git a/firebase-ai/api.txt b/firebase-ai/api.txt index 92445cb8f45..d7ba984bc92 100644 --- a/firebase-ai/api.txt +++ b/firebase-ai/api.txt @@ -382,6 +382,8 @@ 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(); @@ -389,6 +391,8 @@ package com.google.firebase.ai.type { method public String? getThoughtSummary(); 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; @@ -1184,12 +1188,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/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 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 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..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 @@ -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,13 @@ internal constructor( */ @JvmStatic public fun functionDeclarations(functionDeclarations: List): Tool { - return Tool(functionDeclarations, null) + 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())) } /** @@ -70,7 +78,7 @@ internal constructor( */ @JvmStatic public fun googleSearch(googleSearch: GoogleSearch = GoogleSearch()): Tool { - return Tool(null, googleSearch) + return Tool(null, googleSearch, null) } } } 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() } }