Skip to content

Add support for Code Execution #7265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
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
6 changes: 6 additions & 0 deletions firebase-ai/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -382,13 +382,17 @@ package com.google.firebase.ai.type {
public final class GenerateContentResponse {
ctor public GenerateContentResponse(java.util.List<com.google.firebase.ai.type.Candidate> candidates, com.google.firebase.ai.type.PromptFeedback? promptFeedback, com.google.firebase.ai.type.UsageMetadata? usageMetadata);
method public java.util.List<com.google.firebase.ai.type.Candidate> getCandidates();
method public java.util.List<com.google.firebase.ai.type.CodeExecutionResultPart> getCodeExecutionResults();
method public java.util.List<com.google.firebase.ai.type.ExecutableCodePart> getExecutableCodeList();
method public java.util.List<com.google.firebase.ai.type.FunctionCallPart> getFunctionCalls();
method public java.util.List<com.google.firebase.ai.type.InlineDataPart> getInlineDataParts();
method public com.google.firebase.ai.type.PromptFeedback? getPromptFeedback();
method public String? getText();
method public String? getThoughtSummary();
method public com.google.firebase.ai.type.UsageMetadata? getUsageMetadata();
property public final java.util.List<com.google.firebase.ai.type.Candidate> candidates;
property public final java.util.List<com.google.firebase.ai.type.CodeExecutionResultPart> codeExecutionResults;
property public final java.util.List<com.google.firebase.ai.type.ExecutableCodePart> executableCodeList;
property public final java.util.List<com.google.firebase.ai.type.FunctionCallPart> functionCalls;
property public final java.util.List<com.google.firebase.ai.type.InlineDataPart> inlineDataParts;
property public final com.google.firebase.ai.type.PromptFeedback? promptFeedback;
Expand Down Expand Up @@ -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<com.google.firebase.ai.type.FunctionDeclaration> 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<com.google.firebase.ai.type.FunctionDeclaration> functionDeclarations);
method public com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch());
}
Expand Down
49 changes: 49 additions & 0 deletions firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Outcome.kt
Original file line number Diff line number Diff line change
@@ -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<Internal> 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)
}
}
22 changes: 17 additions & 5 deletions firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Part.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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,
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -410,7 +422,7 @@ internal fun InternalPart.toPublic(): Part {
)
is CodeExecutionResultPart.Internal ->
CodeExecutionResultPart(
codeExecutionResult.outcome,
codeExecutionResult.outcome.toPublic(),
codeExecutionResult.output,
thought ?: false,
thoughtSignature
Expand Down
16 changes: 12 additions & 4 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 @@ -26,12 +26,14 @@ import kotlinx.serialization.json.JsonObject
public class Tool
internal constructor(
internal val functionDeclarations: List<FunctionDeclaration>?,
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(
Expand All @@ -49,7 +51,13 @@ internal constructor(
*/
@JvmStatic
public fun functionDeclarations(functionDeclarations: List<FunctionDeclaration>): 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()))
}

/**
Expand All @@ -70,7 +78,7 @@ internal constructor(
*/
@JvmStatic
public fun googleSearch(googleSearch: GoogleSearch = GoogleSearch()): Tool {
return Tool(null, googleSearch)
return Tool(null, googleSearch, null)
}
}
}
10 changes: 10 additions & 0 deletions firebase-ai/src/test/java/com/google/firebase/ai/type/ToolTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ internal class ToolTest {

tool.googleSearch.shouldNotBeNull()
tool.functionDeclarations.shouldBeNull()
tool.codeExecution.shouldBeNull()
}

@Test
Expand All @@ -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()
}
}
Loading