Skip to content
Merged
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
15 changes: 15 additions & 0 deletions core/src/main/java/com/google/adk/agents/LlmAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.google.adk.agents.Callbacks.BeforeToolCallbackBase;
import com.google.adk.agents.Callbacks.BeforeToolCallbackSync;
import com.google.adk.agents.ConfigAgentUtils.ConfigurationException;
import com.google.adk.codeexecutors.BaseCodeExecutor;
import com.google.adk.events.Event;
import com.google.adk.examples.BaseExampleProvider;
import com.google.adk.examples.Example;
Expand Down Expand Up @@ -106,6 +107,7 @@ public enum IncludeContents {
private final Optional<Schema> outputSchema;
private final Optional<Executor> executor;
private final Optional<String> outputKey;
private final Optional<BaseCodeExecutor> codeExecutor;

private volatile Model resolvedModel;
private final BaseLlmFlow llmFlow;
Expand Down Expand Up @@ -139,6 +141,7 @@ protected LlmAgent(Builder builder) {
this.executor = Optional.ofNullable(builder.executor);
this.outputKey = Optional.ofNullable(builder.outputKey);
this.toolsUnion = builder.toolsUnion != null ? builder.toolsUnion : ImmutableList.of();
this.codeExecutor = Optional.ofNullable(builder.codeExecutor);

this.llmFlow = determineLlmFlow();

Expand Down Expand Up @@ -179,6 +182,7 @@ public static class Builder {
private Schema outputSchema;
private Executor executor;
private String outputKey;
private BaseCodeExecutor codeExecutor;

@CanIgnoreReturnValue
public Builder name(String name) {
Expand Down Expand Up @@ -563,6 +567,12 @@ public Builder outputKey(String outputKey) {
return this;
}

@CanIgnoreReturnValue
public Builder codeExecutor(BaseCodeExecutor codeExecutor) {
this.codeExecutor = codeExecutor;
return this;
}

protected void validate() {
this.disallowTransferToParent =
this.disallowTransferToParent != null && this.disallowTransferToParent;
Expand Down Expand Up @@ -816,6 +826,11 @@ public Optional<String> outputKey() {
return outputKey;
}

@Nullable
public BaseCodeExecutor codeExecutor() {
return codeExecutor.orElse(null);
}

public Model resolvedModel() {
if (resolvedModel == null) {
synchronized (this) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2025 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.adk.codeexecutors;

import com.google.adk.JsonBaseModel;
import com.google.adk.agents.InvocationContext;
import com.google.adk.codeexecutors.CodeExecutionUtils.CodeExecutionInput;
import com.google.adk.codeexecutors.CodeExecutionUtils.CodeExecutionResult;
import com.google.common.collect.ImmutableList;
import java.util.List;

/**
* Abstract base class for all code executors.
*
* <p>The code executor allows the agent to execute code blocks from model responses and incorporate
* the execution results into the final response.
*/
public abstract class BaseCodeExecutor extends JsonBaseModel {
/**
* If true, extract and process data files from the model request and attach them to the code
* executor.
*
* <p>Supported data file MimeTypes are [text/csv]. Default to False.
*/
public boolean optimizeDataFile() {
return false;
}

/** Whether the code executor is stateful. Default to False. */
public boolean stateful() {
return false;
}

/**
* The number of attempts to retry on consecutive code execution errors.
*
* <p>Default to 2.
*/
public int errorRetryAttempts() {
return 2;
}

/**
* The list of the enclosing delimiters to identify the code blocks.
*
* <p>For example, the delimiter ('```python\n', '\n```') can be used to identify code blocks with
* the following format:
*
* <p>```python
*
* <p>print("hello")
*
* <p>```
*/
public List<List<String>> codeBlockDelimiters() {
return ImmutableList.of(
ImmutableList.of("```tool_code\n", "\n```"), ImmutableList.of("```python\n", "\n```"));
}

/** The delimiters to format the code execution result. */
public List<String> executionResultDelimiters() {
return ImmutableList.of("```tool_output\n", "\n```");
}

/**
* Executes code and return the code execution result.
*
* @param invocationContext The invocation context of the code execution.
* @param codeExecutionInput The code execution input.
* @return The code execution result.
*/
public abstract CodeExecutionResult executeCode(
InvocationContext invocationContext, CodeExecutionInput codeExecutionInput);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2025 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.adk.codeexecutors;

import com.google.adk.agents.InvocationContext;
import com.google.adk.codeexecutors.CodeExecutionUtils.CodeExecutionInput;
import com.google.adk.codeexecutors.CodeExecutionUtils.CodeExecutionResult;
import com.google.adk.models.LlmRequest;
import com.google.adk.utils.ModelNameUtils;
import com.google.common.collect.ImmutableList;
import com.google.genai.types.GenerateContentConfig;
import com.google.genai.types.Tool;
import com.google.genai.types.ToolCodeExecution;

/**
* A code executor that uses the Model's built-in code executor.
*
* <p>Currently only supports Gemini 2.0+ models, but will be expanded to other models.
*/
public class BuiltInCodeExecutor extends BaseCodeExecutor {

@Override
public CodeExecutionResult executeCode(
InvocationContext invocationContext, CodeExecutionInput codeExecutionInput) {
throw new UnsupportedOperationException(
"Code execution is not supported for built-in code executor.");
}

/** Pre-process the LLM request for Gemini 2.0+ models to use the code execution tool. */
public void processLlmRequest(LlmRequest.Builder llmRequestBuilder) {
LlmRequest llmRequest = llmRequestBuilder.build();
if (ModelNameUtils.isGemini2Model(llmRequest.model().orElse(null))) {
GenerateContentConfig.Builder configBuilder =
llmRequest.config().map(c -> c.toBuilder()).orElse(GenerateContentConfig.builder());
ImmutableList.Builder<Tool> toolsBuilder =
ImmutableList.<Tool>builder()
.addAll(configBuilder.build().tools().orElse(ImmutableList.<Tool>of()));
toolsBuilder.add(Tool.builder().codeExecution(ToolCodeExecution.builder().build()).build());
configBuilder.tools(toolsBuilder.build());
llmRequestBuilder.config(configBuilder.build());
return;
}
throw new IllegalArgumentException(
"Gemini code execution tool is not supported for model " + llmRequest.model().orElse(""));
}
}
Loading