Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package com.google.adk.agents;

import com.google.adk.apps.ResumabilityConfig;
import com.google.adk.artifacts.BaseArtifactService;
import com.google.adk.events.Event;
import com.google.adk.flows.llmflows.ResumabilityConfig;
import com.google.adk.memory.BaseMemoryService;
import com.google.adk.models.LlmCallsLimitExceededException;
import com.google.adk.plugins.Plugin;
Expand Down
145 changes: 145 additions & 0 deletions core/src/main/java/com/google/adk/apps/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright 2026 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.apps;

import com.google.adk.agents.BaseAgent;
import com.google.adk.plugins.BasePlugin;
import com.google.adk.summarizer.EventsCompactionConfig;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.List;
import java.util.regex.Pattern;
import javax.annotation.Nullable;

/**
* Represents an LLM-backed agentic application.
*
* <p>An {@code App} is the top-level container for an agentic system powered by LLMs. It manages a
* root agent ({@code rootAgent}), which serves as the root of an agent tree, enabling coordination
* and communication across all agents in the hierarchy. The {@code plugins} are application-wide
* components that provide shared capabilities and services to the entire system.
*/
public class App {
private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*");

private final String name;
private final BaseAgent rootAgent;
private final ImmutableList<BasePlugin> plugins;
@Nullable private final EventsCompactionConfig eventsCompactionConfig;
@Nullable private final ResumabilityConfig resumabilityConfig;

private App(
String name,
BaseAgent rootAgent,
List<BasePlugin> plugins,
@Nullable EventsCompactionConfig eventsCompactionConfig,
@Nullable ResumabilityConfig resumabilityConfig) {
this.name = name;
this.rootAgent = rootAgent;
this.plugins = ImmutableList.copyOf(plugins);
this.eventsCompactionConfig = eventsCompactionConfig;
this.resumabilityConfig = resumabilityConfig;
}

public String name() {
return name;
}

public BaseAgent rootAgent() {
return rootAgent;
}

public ImmutableList<BasePlugin> plugins() {
return plugins;
}

@Nullable
public EventsCompactionConfig eventsCompactionConfig() {
return eventsCompactionConfig;
}

@Nullable
public ResumabilityConfig resumabilityConfig() {
return resumabilityConfig;
}

/** Builder for {@link App}. */
public static class Builder {
private String name;
private BaseAgent rootAgent;
private List<BasePlugin> plugins = ImmutableList.of();
@Nullable private EventsCompactionConfig eventsCompactionConfig;
@Nullable private ResumabilityConfig resumabilityConfig;

@CanIgnoreReturnValue
public Builder name(String name) {
this.name = name;
return this;
}

@CanIgnoreReturnValue
public Builder rootAgent(BaseAgent rootAgent) {
this.rootAgent = rootAgent;
return this;
}

@CanIgnoreReturnValue
public Builder plugins(List<BasePlugin> plugins) {
this.plugins = plugins;
return this;
}

@CanIgnoreReturnValue
public Builder eventsCompactionConfig(EventsCompactionConfig eventsCompactionConfig) {
this.eventsCompactionConfig = eventsCompactionConfig;
return this;
}

@CanIgnoreReturnValue
public Builder resumabilityConfig(ResumabilityConfig resumabilityConfig) {
this.resumabilityConfig = resumabilityConfig;
return this;
}

public App build() {
if (name == null) {
throw new IllegalStateException("App name must be provided.");
}
if (rootAgent == null) {
throw new IllegalStateException("Root agent must be provided.");
}
validateAppName(name);
return new App(name, rootAgent, plugins, eventsCompactionConfig, resumabilityConfig);
}
}

public static Builder builder() {
return new Builder();
}

private static void validateAppName(String name) {
if (!IDENTIFIER_PATTERN.matcher(name).matches()) {
throw new IllegalArgumentException(
"Invalid app name '"
+ name
+ "': must be a valid identifier consisting of letters, digits, and underscores.");
}
if (name.equals("user")) {
throw new IllegalArgumentException("App name cannot be 'user'; reserved for end-user input.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
*
* 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
* WITHOUT WARRANTIES OR CONDITIONS language governing permissions and
* limitations under the License.
*/
package com.google.adk.flows.llmflows;
package com.google.adk.apps;

/**
* An app contains Resumability configuration for the agents.
Expand Down
76 changes: 52 additions & 24 deletions core/src/main/java/com/google/adk/runner/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@
import com.google.adk.agents.LiveRequestQueue;
import com.google.adk.agents.LlmAgent;
import com.google.adk.agents.RunConfig;
import com.google.adk.apps.App;
import com.google.adk.apps.ResumabilityConfig;
import com.google.adk.artifacts.BaseArtifactService;
import com.google.adk.artifacts.InMemoryArtifactService;
import com.google.adk.events.Event;
import com.google.adk.events.EventActions;
import com.google.adk.flows.llmflows.ResumabilityConfig;
import com.google.adk.memory.BaseMemoryService;
import com.google.adk.models.Model;
import com.google.adk.plugins.BasePlugin;
Expand All @@ -41,6 +42,7 @@
import com.google.adk.tools.BaseTool;
import com.google.adk.tools.FunctionTool;
import com.google.adk.utils.CollectionUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.genai.types.AudioTranscriptionConfig;
Expand Down Expand Up @@ -76,23 +78,31 @@ public class Runner {

/** Builder for {@link Runner}. */
public static class Builder {
private App app;
private BaseAgent agent;
private String appName;
private BaseArtifactService artifactService = new InMemoryArtifactService();
private BaseSessionService sessionService = new InMemorySessionService();
@Nullable private BaseMemoryService memoryService = null;
private List<BasePlugin> plugins = ImmutableList.of();
private ResumabilityConfig resumabilityConfig = new ResumabilityConfig();
@Nullable private EventsCompactionConfig eventsCompactionConfig;

@CanIgnoreReturnValue
public Builder app(App app) {
Preconditions.checkState(this.agent == null, "app() cannot be called when agent() is set.");
this.app = app;
return this;
}

@CanIgnoreReturnValue
public Builder agent(BaseAgent agent) {
Preconditions.checkState(this.app == null, "agent() cannot be called when app is set.");
this.agent = agent;
return this;
}

@CanIgnoreReturnValue
public Builder appName(String appName) {
Preconditions.checkState(this.app == null, "appName() cannot be called when app is set.");
this.appName = appName;
return this;
}
Expand All @@ -117,28 +127,46 @@ public Builder memoryService(BaseMemoryService memoryService) {

@CanIgnoreReturnValue
public Builder plugins(List<BasePlugin> plugins) {
Preconditions.checkState(this.app == null, "plugins() cannot be called when app is set.");
this.plugins = plugins;
return this;
}

@CanIgnoreReturnValue
public Builder resumabilityConfig(ResumabilityConfig resumabilityConfig) {
this.resumabilityConfig = resumabilityConfig;
return this;
}

@CanIgnoreReturnValue
public Builder eventsCompactionConfig(EventsCompactionConfig eventsCompactionConfig) {
this.eventsCompactionConfig = eventsCompactionConfig;
return this;
}

public Runner build() {
if (agent == null) {
throw new IllegalStateException("Agent must be provided.");
BaseAgent buildAgent;
String buildAppName;
List<BasePlugin> buildPlugins;
ResumabilityConfig buildResumabilityConfig;
EventsCompactionConfig buildEventsCompactionConfig;

if (this.app != null) {
if (this.agent != null) {
throw new IllegalStateException("agent() cannot be called when app() is called.");
}
if (!this.plugins.isEmpty()) {
throw new IllegalStateException("plugins() cannot be called when app() is called.");
}
buildAgent = this.app.rootAgent();
buildPlugins = this.app.plugins();
buildAppName = this.appName == null ? this.app.name() : this.appName;
buildResumabilityConfig =
this.app.resumabilityConfig() != null
? this.app.resumabilityConfig()
: new ResumabilityConfig();
buildEventsCompactionConfig = this.app.eventsCompactionConfig();
} else {
buildAgent = this.agent;
buildAppName = this.appName;
buildPlugins = this.plugins;
buildResumabilityConfig = new ResumabilityConfig();
buildEventsCompactionConfig = null;
}

if (buildAgent == null) {
throw new IllegalStateException("Agent must be provided via app() or agent().");
}
if (appName == null) {
throw new IllegalStateException("App name must be provided.");
if (buildAppName == null) {
throw new IllegalStateException("App name must be provided via app() or appName().");
}
if (artifactService == null) {
throw new IllegalStateException("Artifact service must be provided.");
Expand All @@ -147,14 +175,14 @@ public Runner build() {
throw new IllegalStateException("Session service must be provided.");
}
return new Runner(
agent,
appName,
buildAgent,
buildAppName,
artifactService,
sessionService,
memoryService,
plugins,
resumabilityConfig,
eventsCompactionConfig);
buildPlugins,
buildResumabilityConfig,
buildEventsCompactionConfig);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;

import com.google.adk.apps.ResumabilityConfig;
import com.google.adk.artifacts.BaseArtifactService;
import com.google.adk.events.Event;
import com.google.adk.flows.llmflows.ResumabilityConfig;
import com.google.adk.memory.BaseMemoryService;
import com.google.adk.plugins.PluginManager;
import com.google.adk.sessions.BaseSessionService;
Expand Down
Loading