Skip to content

Commit 9392f66

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: 860201571
1 parent 55144ac commit 9392f66

File tree

7 files changed

+282
-59
lines changed

7 files changed

+282
-59
lines changed

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: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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+
44+
private App(
45+
String name,
46+
BaseAgent rootAgent,
47+
List<BasePlugin> plugins,
48+
@Nullable EventsCompactionConfig eventsCompactionConfig) {
49+
this.name = name;
50+
this.rootAgent = rootAgent;
51+
this.plugins = ImmutableList.copyOf(plugins);
52+
this.eventsCompactionConfig = eventsCompactionConfig;
53+
}
54+
55+
public String name() {
56+
return name;
57+
}
58+
59+
public BaseAgent rootAgent() {
60+
return rootAgent;
61+
}
62+
63+
public ImmutableList<BasePlugin> plugins() {
64+
return plugins;
65+
}
66+
67+
@Nullable
68+
public EventsCompactionConfig eventsCompactionConfig() {
69+
return eventsCompactionConfig;
70+
}
71+
72+
/** Builder for {@link App}. */
73+
public static class Builder {
74+
private String name;
75+
private BaseAgent rootAgent;
76+
private List<BasePlugin> plugins = ImmutableList.of();
77+
@Nullable private EventsCompactionConfig eventsCompactionConfig;
78+
79+
@CanIgnoreReturnValue
80+
public Builder name(String name) {
81+
this.name = name;
82+
return this;
83+
}
84+
85+
@CanIgnoreReturnValue
86+
public Builder rootAgent(BaseAgent rootAgent) {
87+
this.rootAgent = rootAgent;
88+
return this;
89+
}
90+
91+
@CanIgnoreReturnValue
92+
public Builder plugins(List<BasePlugin> plugins) {
93+
this.plugins = plugins;
94+
return this;
95+
}
96+
97+
@CanIgnoreReturnValue
98+
public Builder eventsCompactionConfig(EventsCompactionConfig eventsCompactionConfig) {
99+
this.eventsCompactionConfig = eventsCompactionConfig;
100+
return this;
101+
}
102+
103+
public App build() {
104+
if (name == null) {
105+
throw new IllegalStateException("App name must be provided.");
106+
}
107+
if (rootAgent == null) {
108+
throw new IllegalStateException("Root agent must be provided.");
109+
}
110+
validateAppName(name);
111+
return new App(name, rootAgent, plugins, eventsCompactionConfig);
112+
}
113+
}
114+
115+
public static Builder builder() {
116+
return new Builder();
117+
}
118+
119+
private static void validateAppName(String name) {
120+
if (!IDENTIFIER_PATTERN.matcher(name).matches()) {
121+
throw new IllegalArgumentException(
122+
"Invalid app name '"
123+
+ name
124+
+ "': must be a valid identifier consisting of letters, digits, and underscores.");
125+
}
126+
if (name.equals("user")) {
127+
throw new IllegalArgumentException("App name cannot be 'user'; reserved for end-user input.");
128+
}
129+
}
130+
}

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)