Skip to content

Commit d7c5c6f

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Introduce the App class for defining agentic applications
This change adds the `App` class, which serves as the top-level container for an agentic application. It includes fields for the application's name, root agent, plugins, and strategies for content inclusion and compaction. PiperOrigin-RevId: 860636768
1 parent 5e6b429 commit d7c5c6f

7 files changed

Lines changed: 296 additions & 59 deletions

File tree

core/src/main/java/com/google/adk/agents/InvocationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616

1717
package com.google.adk.agents;
1818

19+
import com.google.adk.apps.ResumabilityConfig;
1920
import com.google.adk.artifacts.BaseArtifactService;
2021
import com.google.adk.events.Event;
21-
import com.google.adk.flows.llmflows.ResumabilityConfig;
2222
import com.google.adk.memory.BaseMemoryService;
2323
import com.google.adk.models.LlmCallsLimitExceededException;
2424
import com.google.adk.plugins.Plugin;
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.adk.apps;
18+
19+
import com.google.adk.agents.BaseAgent;
20+
import com.google.adk.plugins.BasePlugin;
21+
import com.google.adk.summarizer.EventsCompactionConfig;
22+
import com.google.common.collect.ImmutableList;
23+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
24+
import java.util.List;
25+
import java.util.regex.Pattern;
26+
import javax.annotation.Nullable;
27+
28+
/**
29+
* Represents an LLM-backed agentic application.
30+
*
31+
* <p>An {@code App} is the top-level container for an agentic system powered by LLMs. It manages a
32+
* root agent ({@code rootAgent}), which serves as the root of an agent tree, enabling coordination
33+
* and communication across all agents in the hierarchy. The {@code plugins} are application-wide
34+
* components that provide shared capabilities and services to the entire system.
35+
*/
36+
public class App {
37+
private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*");
38+
39+
private final String name;
40+
private final BaseAgent rootAgent;
41+
private final ImmutableList<BasePlugin> plugins;
42+
@Nullable private final EventsCompactionConfig eventsCompactionConfig;
43+
@Nullable private final ResumabilityConfig resumabilityConfig;
44+
45+
private App(
46+
String name,
47+
BaseAgent rootAgent,
48+
List<BasePlugin> plugins,
49+
@Nullable EventsCompactionConfig eventsCompactionConfig,
50+
@Nullable ResumabilityConfig resumabilityConfig) {
51+
this.name = name;
52+
this.rootAgent = rootAgent;
53+
this.plugins = ImmutableList.copyOf(plugins);
54+
this.eventsCompactionConfig = eventsCompactionConfig;
55+
this.resumabilityConfig = resumabilityConfig;
56+
}
57+
58+
public String name() {
59+
return name;
60+
}
61+
62+
public BaseAgent rootAgent() {
63+
return rootAgent;
64+
}
65+
66+
public ImmutableList<BasePlugin> plugins() {
67+
return plugins;
68+
}
69+
70+
@Nullable
71+
public EventsCompactionConfig eventsCompactionConfig() {
72+
return eventsCompactionConfig;
73+
}
74+
75+
@Nullable
76+
public ResumabilityConfig resumabilityConfig() {
77+
return resumabilityConfig;
78+
}
79+
80+
/** Builder for {@link App}. */
81+
public static class Builder {
82+
private String name;
83+
private BaseAgent rootAgent;
84+
private List<BasePlugin> plugins = ImmutableList.of();
85+
@Nullable private EventsCompactionConfig eventsCompactionConfig;
86+
@Nullable private ResumabilityConfig resumabilityConfig;
87+
88+
@CanIgnoreReturnValue
89+
public Builder name(String name) {
90+
this.name = name;
91+
return this;
92+
}
93+
94+
@CanIgnoreReturnValue
95+
public Builder rootAgent(BaseAgent rootAgent) {
96+
this.rootAgent = rootAgent;
97+
return this;
98+
}
99+
100+
@CanIgnoreReturnValue
101+
public Builder plugins(List<BasePlugin> plugins) {
102+
this.plugins = plugins;
103+
return this;
104+
}
105+
106+
@CanIgnoreReturnValue
107+
public Builder eventsCompactionConfig(EventsCompactionConfig eventsCompactionConfig) {
108+
this.eventsCompactionConfig = eventsCompactionConfig;
109+
return this;
110+
}
111+
112+
@CanIgnoreReturnValue
113+
public Builder resumabilityConfig(ResumabilityConfig resumabilityConfig) {
114+
this.resumabilityConfig = resumabilityConfig;
115+
return this;
116+
}
117+
118+
public App build() {
119+
if (name == null) {
120+
throw new IllegalStateException("App name must be provided.");
121+
}
122+
if (rootAgent == null) {
123+
throw new IllegalStateException("Root agent must be provided.");
124+
}
125+
validateAppName(name);
126+
return new App(name, rootAgent, plugins, eventsCompactionConfig, resumabilityConfig);
127+
}
128+
}
129+
130+
public static Builder builder() {
131+
return new Builder();
132+
}
133+
134+
private static void validateAppName(String name) {
135+
if (!IDENTIFIER_PATTERN.matcher(name).matches()) {
136+
throw new IllegalArgumentException(
137+
"Invalid app name '"
138+
+ name
139+
+ "': must be a valid identifier consisting of letters, digits, and underscores.");
140+
}
141+
if (name.equals("user")) {
142+
throw new IllegalArgumentException("App name cannot be 'user'; reserved for end-user input.");
143+
}
144+
}
145+
}

core/src/main/java/com/google/adk/flows/llmflows/ResumabilityConfig.java renamed to core/src/main/java/com/google/adk/apps/ResumabilityConfig.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
12+
* WITHOUT WARRANTIES OR CONDITIONS language governing permissions and
1413
* limitations under the License.
1514
*/
16-
package com.google.adk.flows.llmflows;
15+
package com.google.adk.apps;
1716

1817
/**
1918
* An app contains Resumability configuration for the agents.

core/src/main/java/com/google/adk/runner/Runner.java

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@
2323
import com.google.adk.agents.LiveRequestQueue;
2424
import com.google.adk.agents.LlmAgent;
2525
import com.google.adk.agents.RunConfig;
26+
import com.google.adk.apps.App;
27+
import com.google.adk.apps.ResumabilityConfig;
2628
import com.google.adk.artifacts.BaseArtifactService;
2729
import com.google.adk.artifacts.InMemoryArtifactService;
2830
import com.google.adk.events.Event;
2931
import com.google.adk.events.EventActions;
30-
import com.google.adk.flows.llmflows.ResumabilityConfig;
3132
import com.google.adk.memory.BaseMemoryService;
3233
import com.google.adk.models.Model;
3334
import com.google.adk.plugins.BasePlugin;
@@ -41,6 +42,7 @@
4142
import com.google.adk.tools.BaseTool;
4243
import com.google.adk.tools.FunctionTool;
4344
import com.google.adk.utils.CollectionUtils;
45+
import com.google.common.base.Preconditions;
4446
import com.google.common.collect.ImmutableList;
4547
import com.google.errorprone.annotations.CanIgnoreReturnValue;
4648
import com.google.genai.types.AudioTranscriptionConfig;
@@ -76,23 +78,31 @@ public class Runner {
7678

7779
/** Builder for {@link Runner}. */
7880
public static class Builder {
81+
private App app;
7982
private BaseAgent agent;
8083
private String appName;
8184
private BaseArtifactService artifactService = new InMemoryArtifactService();
8285
private BaseSessionService sessionService = new InMemorySessionService();
8386
@Nullable private BaseMemoryService memoryService = null;
8487
private List<BasePlugin> plugins = ImmutableList.of();
85-
private ResumabilityConfig resumabilityConfig = new ResumabilityConfig();
86-
@Nullable private EventsCompactionConfig eventsCompactionConfig;
88+
89+
@CanIgnoreReturnValue
90+
public Builder app(App app) {
91+
Preconditions.checkState(this.agent == null, "app() cannot be called when agent() is set.");
92+
this.app = app;
93+
return this;
94+
}
8795

8896
@CanIgnoreReturnValue
8997
public Builder agent(BaseAgent agent) {
98+
Preconditions.checkState(this.app == null, "agent() cannot be called when app is set.");
9099
this.agent = agent;
91100
return this;
92101
}
93102

94103
@CanIgnoreReturnValue
95104
public Builder appName(String appName) {
105+
Preconditions.checkState(this.app == null, "appName() cannot be called when app is set.");
96106
this.appName = appName;
97107
return this;
98108
}
@@ -117,28 +127,46 @@ public Builder memoryService(BaseMemoryService memoryService) {
117127

118128
@CanIgnoreReturnValue
119129
public Builder plugins(List<BasePlugin> plugins) {
130+
Preconditions.checkState(this.app == null, "plugins() cannot be called when app is set.");
120131
this.plugins = plugins;
121132
return this;
122133
}
123134

124-
@CanIgnoreReturnValue
125-
public Builder resumabilityConfig(ResumabilityConfig resumabilityConfig) {
126-
this.resumabilityConfig = resumabilityConfig;
127-
return this;
128-
}
129-
130-
@CanIgnoreReturnValue
131-
public Builder eventsCompactionConfig(EventsCompactionConfig eventsCompactionConfig) {
132-
this.eventsCompactionConfig = eventsCompactionConfig;
133-
return this;
134-
}
135-
136135
public Runner build() {
137-
if (agent == null) {
138-
throw new IllegalStateException("Agent must be provided.");
136+
BaseAgent buildAgent;
137+
String buildAppName;
138+
List<BasePlugin> buildPlugins;
139+
ResumabilityConfig buildResumabilityConfig;
140+
EventsCompactionConfig buildEventsCompactionConfig;
141+
142+
if (this.app != null) {
143+
if (this.agent != null) {
144+
throw new IllegalStateException("agent() cannot be called when app() is called.");
145+
}
146+
if (!this.plugins.isEmpty()) {
147+
throw new IllegalStateException("plugins() cannot be called when app() is called.");
148+
}
149+
buildAgent = this.app.rootAgent();
150+
buildPlugins = this.app.plugins();
151+
buildAppName = this.appName == null ? this.app.name() : this.appName;
152+
buildResumabilityConfig =
153+
this.app.resumabilityConfig() != null
154+
? this.app.resumabilityConfig()
155+
: new ResumabilityConfig();
156+
buildEventsCompactionConfig = this.app.eventsCompactionConfig();
157+
} else {
158+
buildAgent = this.agent;
159+
buildAppName = this.appName;
160+
buildPlugins = this.plugins;
161+
buildResumabilityConfig = new ResumabilityConfig();
162+
buildEventsCompactionConfig = null;
163+
}
164+
165+
if (buildAgent == null) {
166+
throw new IllegalStateException("Agent must be provided via app() or agent().");
139167
}
140-
if (appName == null) {
141-
throw new IllegalStateException("App name must be provided.");
168+
if (buildAppName == null) {
169+
throw new IllegalStateException("App name must be provided via app() or appName().");
142170
}
143171
if (artifactService == null) {
144172
throw new IllegalStateException("Artifact service must be provided.");
@@ -147,14 +175,14 @@ public Runner build() {
147175
throw new IllegalStateException("Session service must be provided.");
148176
}
149177
return new Runner(
150-
agent,
151-
appName,
178+
buildAgent,
179+
buildAppName,
152180
artifactService,
153181
sessionService,
154182
memoryService,
155-
plugins,
156-
resumabilityConfig,
157-
eventsCompactionConfig);
183+
buildPlugins,
184+
buildResumabilityConfig,
185+
buildEventsCompactionConfig);
158186
}
159187
}
160188

core/src/test/java/com/google/adk/agents/InvocationContextTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
import static com.google.common.truth.Truth.assertThat;
2020
import static org.mockito.Mockito.mock;
2121

22+
import com.google.adk.apps.ResumabilityConfig;
2223
import com.google.adk.artifacts.BaseArtifactService;
2324
import com.google.adk.events.Event;
24-
import com.google.adk.flows.llmflows.ResumabilityConfig;
2525
import com.google.adk.memory.BaseMemoryService;
2626
import com.google.adk.plugins.PluginManager;
2727
import com.google.adk.sessions.BaseSessionService;

0 commit comments

Comments
 (0)