From d1a1cea4a633f376463d7e47b79bfb67126537ad Mon Sep 17 00:00:00 2001 From: Google Team Member Date: Thu, 18 Dec 2025 06:29:19 -0800 Subject: [PATCH] feat: enable LoopAgent configuration PiperOrigin-RevId: 846239087 --- .../google/adk/agents/ConfigAgentUtils.java | 4 +++ .../java/com/google/adk/agents/LoopAgent.java | 33 ++++++++++++++++++ .../google/adk/agents/LoopAgentConfig.java | 34 +++++++++++++++++++ .../com/google/adk/tools/ExitLoopTool.java | 3 +- .../adk/agents/ConfigAgentUtilsTest.java | 30 ++++++++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/com/google/adk/agents/LoopAgentConfig.java diff --git a/core/src/main/java/com/google/adk/agents/ConfigAgentUtils.java b/core/src/main/java/com/google/adk/agents/ConfigAgentUtils.java index 62528ac7d..6c605a182 100644 --- a/core/src/main/java/com/google/adk/agents/ConfigAgentUtils.java +++ b/core/src/main/java/com/google/adk/agents/ConfigAgentUtils.java @@ -307,6 +307,10 @@ private static Class getConfigClassForAgent( return ParallelAgentConfig.class; } + if (agentClass == LoopAgent.class) { + return LoopAgentConfig.class; + } + // TODO: Add more agent class to config class mappings as needed // Example: // if (agentClass == CustomAgent.class) { diff --git a/core/src/main/java/com/google/adk/agents/LoopAgent.java b/core/src/main/java/com/google/adk/agents/LoopAgent.java index 03bf928b6..d9d049f80 100644 --- a/core/src/main/java/com/google/adk/agents/LoopAgent.java +++ b/core/src/main/java/com/google/adk/agents/LoopAgent.java @@ -16,11 +16,14 @@ package com.google.adk.agents; +import com.google.adk.agents.ConfigAgentUtils.ConfigurationException; import com.google.adk.events.Event; import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.reactivex.rxjava3.core.Flowable; import java.util.List; import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An agent that runs its sub-agents sequentially in a loop. @@ -29,6 +32,7 @@ * reached (if specified). */ public class LoopAgent extends BaseAgent { + private static final Logger logger = LoggerFactory.getLogger(LoopAgent.class); private final Optional maxIterations; @@ -82,6 +86,35 @@ public static Builder builder() { return new Builder(); } + /** + * Creates a LoopAgent from configuration. + * + * @param config The agent configuration. + * @param configAbsPath The absolute path to the agent config file. + * @return the configured LoopAgent + * @throws ConfigurationException if the configuration is invalid + */ + public static LoopAgent fromConfig(LoopAgentConfig config, String configAbsPath) + throws ConfigurationException { + logger.debug("Creating LoopAgent from config: {}", config.name()); + + Builder builder = builder(); + ConfigAgentUtils.resolveAndSetCommonAgentFields(builder, config, configAbsPath); + + if (config.maxIterations() != null) { + builder.maxIterations(config.maxIterations()); + } + + // Build and return the agent + LoopAgent agent = builder.build(); + logger.info( + "Successfully created LoopAgent: {} with {} subagents", + agent.name(), + agent.subAgents() != null ? agent.subAgents().size() : 0); + + return agent; + } + @Override protected Flowable runAsyncImpl(InvocationContext invocationContext) { List subAgents = subAgents(); diff --git a/core/src/main/java/com/google/adk/agents/LoopAgentConfig.java b/core/src/main/java/com/google/adk/agents/LoopAgentConfig.java new file mode 100644 index 000000000..368665247 --- /dev/null +++ b/core/src/main/java/com/google/adk/agents/LoopAgentConfig.java @@ -0,0 +1,34 @@ +/* + * 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.agents; + +/** Configuration for LoopAgent. */ +public class LoopAgentConfig extends BaseAgentConfig { + private Integer maxIterations; + + public LoopAgentConfig() { + super("LoopAgent"); + } + + public Integer maxIterations() { + return maxIterations; + } + + public void setMaxIterations(Integer maxIterations) { + this.maxIterations = maxIterations; + } +} diff --git a/core/src/main/java/com/google/adk/tools/ExitLoopTool.java b/core/src/main/java/com/google/adk/tools/ExitLoopTool.java index 89ba4e132..901ccbec0 100644 --- a/core/src/main/java/com/google/adk/tools/ExitLoopTool.java +++ b/core/src/main/java/com/google/adk/tools/ExitLoopTool.java @@ -42,7 +42,8 @@ public final class ExitLoopTool { name = "exit_loop", description = "Exits the loop.\n\nCall this function only when you are instructed to do so.") public static void exitLoop(ToolContext toolContext) { - toolContext.setActions(toolContext.actions().toBuilder().escalate(true).build()); + toolContext.setActions( + toolContext.actions().toBuilder().escalate(true).skipSummarization(true).build()); } private ExitLoopTool() {} diff --git a/core/src/test/java/com/google/adk/agents/ConfigAgentUtilsTest.java b/core/src/test/java/com/google/adk/agents/ConfigAgentUtilsTest.java index 4f144a8f8..4825efca1 100644 --- a/core/src/test/java/com/google/adk/agents/ConfigAgentUtilsTest.java +++ b/core/src/test/java/com/google/adk/agents/ConfigAgentUtilsTest.java @@ -1381,4 +1381,34 @@ public void testCallbackRefAccessors() { callbackRef.setName("updated-name"); assertThat(callbackRef.name()).isEqualTo("updated-name"); } + + @Test + public void fromConfig_validYamlLoopAgent_createsLoopAgent() + throws IOException, ConfigurationException { + File subAgentFile = tempFolder.newFile("sub_agent.yaml"); + Files.writeString( + subAgentFile.toPath(), + """ + agent_class: LlmAgent + name: sub_agent + description: A test subagent + instruction: You are a helpful subagent + """); + + File configFile = tempFolder.newFile("loop_agent.yaml"); + Files.writeString( + configFile.toPath(), + """ + name: testLoopAgent + description: A test loop agent + agent_class: LoopAgent + max_iterations: 5 + sub_agents: + - config_path: sub_agent.yaml + """); + String configPath = configFile.getAbsolutePath(); + BaseAgent agent = ConfigAgentUtils.fromConfig(configPath); + assertThat(agent).isNotNull(); + assertThat(agent).isInstanceOf(LoopAgent.class); + } }