Skip to content

Commit 85b06da

Browse files
Jacksunweicopybara-github
authored andcommitted
refactor: Refine AgentProvider interface and auto-wire it directly into AdkWebServer
- Change to `listAgents` and `getAgent` two methods, so that later we can implement config-based agent easily with reload feature. - Adds `@ThreadSafe` annotation to remind user to implement it in a thread-safe manner. PiperOrigin-RevId: 793917077
1 parent b02c559 commit 85b06da

File tree

5 files changed

+143
-103
lines changed

5 files changed

+143
-103
lines changed

maven_plugin/examples/simple-agent/README.md

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,26 +43,39 @@ Example modifications:
4343
### Add a New Agent
4444
```java
4545
private BaseAgent createMathTutor() {
46-
return LlmAgent.builder()
47-
.name("math_tutor")
48-
.description("A mathematics tutoring agent")
49-
.model("gemini-2.0-flash")
50-
.instruction("You are a patient math tutor. " +
51-
"Help students understand mathematical concepts step by step.")
52-
.build();
46+
return LlmAgent.builder()
47+
.name("math_tutor")
48+
.description("A mathematics tutoring agent")
49+
.model("gemini-2.0-flash")
50+
.instruction(
51+
"You are a patient math tutor. "
52+
+ "Help students understand mathematical concepts step by step.")
53+
.build();
5354
}
5455
```
5556

56-
Then add it to the `getAgents()` map:
57+
Then add it to the `listAgents()` method and `getAgent()` switch statement:
5758
```java
5859
@Override
59-
public Map<String, BaseAgent> getAgents() {
60-
return Map.of(
61-
"chat_assistant", createChatAssistant(),
62-
"search_agent", createSearchAgent(),
63-
"code_helper", createCodeHelper(),
64-
"math_tutor", createMathTutor() // Add the new agent
65-
);
60+
@Nonnull
61+
public ImmutableList<String> listAgents() {
62+
return ImmutableList.of("chat_assistant", "search_agent", "code_helper", "math_tutor");
63+
}
64+
65+
@Override
66+
public BaseAgent getAgent(String name) {
67+
switch (name) {
68+
case "chat_assistant":
69+
return createChatAssistant();
70+
case "search_agent":
71+
return createSearchAgent();
72+
case "code_helper":
73+
return createCodeHelper();
74+
case "math_tutor":
75+
return createMathTutor(); // Add the new agent
76+
default:
77+
throw new NoSuchElementException("Agent not found: " + name);
78+
}
6679
}
6780
```
6881

maven_plugin/examples/simple-agent/src/main/java/com/example/SimpleAgentProvider.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,43 @@
2020
import com.google.adk.agents.LlmAgent;
2121
import com.google.adk.maven.AgentProvider;
2222
import com.google.adk.tools.GoogleSearchTool;
23-
import java.util.Map;
23+
import com.google.common.base.Suppliers;
24+
import com.google.common.collect.ImmutableList;
25+
import com.google.common.collect.ImmutableMap;
26+
import java.util.NoSuchElementException;
27+
import java.util.function.Supplier;
28+
import javax.annotation.Nonnull;
29+
import javax.annotation.concurrent.ThreadSafe;
2430

2531
/** Example AgentProvider that creates simple agents for demonstration. */
32+
@ThreadSafe
2633
public class SimpleAgentProvider implements AgentProvider {
2734

28-
// Static instance for easy access
2935
public static final SimpleAgentProvider INSTANCE = new SimpleAgentProvider();
3036

37+
private final ImmutableMap<String, Supplier<BaseAgent>> agentSuppliers;
38+
39+
public SimpleAgentProvider() {
40+
this.agentSuppliers =
41+
ImmutableMap.of(
42+
"chat_assistant", Suppliers.memoize(this::createChatAssistant),
43+
"search_agent", Suppliers.memoize(this::createSearchAgent),
44+
"code_helper", Suppliers.memoize(this::createCodeHelper));
45+
}
46+
47+
@Override
48+
@Nonnull
49+
public ImmutableList<String> listAgents() {
50+
return ImmutableList.copyOf(agentSuppliers.keySet());
51+
}
52+
3153
@Override
32-
public Map<String, BaseAgent> getAgents() {
33-
return Map.of(
34-
"chat_assistant", createChatAssistant(),
35-
"search_agent", createSearchAgent(),
36-
"code_helper", createCodeHelper());
54+
public BaseAgent getAgent(String name) {
55+
Supplier<BaseAgent> supplier = agentSuppliers.get(name);
56+
if (supplier == null) {
57+
throw new NoSuchElementException("Agent not found: " + name);
58+
}
59+
return supplier.get();
3760
}
3861

3962
private BaseAgent createChatAssistant() {

maven_plugin/src/main/java/com/google/adk/maven/AgentProvider.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,43 +15,65 @@
1515
package com.google.adk.maven;
1616

1717
import com.google.adk.agents.BaseAgent;
18-
import java.util.Map;
18+
import com.google.common.collect.ImmutableList;
1919
import javax.annotation.Nonnull;
20+
import javax.annotation.concurrent.ThreadSafe;
2021

2122
/**
2223
* Interface for providing agents to the ADK Web Server.
2324
*
24-
* <p>Users implement this interface to register their custom agents with the web server. The
25-
* implementation should return a map where keys are agent names (used as app names in the UI) and
26-
* values are the corresponding BaseAgent instances.
25+
* <p>Users implement this interface to register their agents with ADK Web Server.
26+
*
27+
* <p><strong>Thread Safety:</strong> Implementation must be thread-safe as it will be used as
28+
* Spring singleton beans and accessed concurrently by multiple HTTP requests.
2729
*
2830
* <p>Example usage:
2931
*
3032
* <pre>{@code
3133
* public class MyAgentProvider implements AgentProvider {
32-
* public static final MyAgentProvider INSTANCE = new MyAgentProvider(); @Override
33-
* public Map<String, BaseAgent> getAgents() {
34-
* return Map.of("chat_bot", createChatBot(), "code_assistant", createCodeAssistant());
34+
* @Override
35+
* public ImmutableList<String> listAgents() {
36+
* return ImmutableList.of("chat_bot", "code_assistant");
37+
* }
38+
*
39+
* @Override
40+
* public BaseAgent getAgent(String name) {
41+
* switch (name) {
42+
* case "chat_bot": return createChatBot();
43+
* case "code_assistant": return createCodeAssistant();
44+
* default: throw new java.util.NoSuchElementException("Agent not found: " + name);
45+
* }
3546
* }
3647
* }
3748
* }</pre>
3849
*
3950
* <p>Then use with Maven plugin:
4051
*
4152
* <pre>{@code
42-
* mvn google-adk:web -Dagents=com.acme.MyAgentProvider.INSTANCE
53+
* mvn google-adk:web -Dagents=com.acme.MyAgentProvider
4354
* }</pre>
4455
*
4556
* TODO: Add config-based agent registration in the future.
4657
*/
58+
@ThreadSafe
4759
public interface AgentProvider {
4860

4961
/**
50-
* Returns a map of agent names to BaseAgent instances.
62+
* Returns a list of available agent names.
5163
*
52-
* @return Map where keys are agent names (app names) and values are BaseAgent instances. Must not
53-
* return null - return an empty map if no agents are available.
64+
* @return ImmutableList of agent names. Must not return null - return an empty list if no agents
65+
* are available.
5466
*/
5567
@Nonnull
56-
Map<String, BaseAgent> getAgents();
68+
ImmutableList<String> listAgents();
69+
70+
/**
71+
* Returns the BaseAgent instance for the specified agent name.
72+
*
73+
* @param name the name of the agent to retrieve
74+
* @return BaseAgent instance for the given name
75+
* @throws java.util.NoSuchElementException if the agent doesn't exist
76+
* @throws IllegalStateException if the agent exists but fails to load
77+
*/
78+
BaseAgent getAgent(String name);
5779
}

maven_plugin/src/main/java/com/google/adk/maven/WebMojo.java

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package com.google.adk.maven;
1818

19-
import com.google.adk.agents.BaseAgent;
2019
import com.google.adk.maven.web.AdkWebServer;
2120
import java.io.IOException;
2221
import java.lang.reflect.Field;
@@ -25,8 +24,6 @@
2524
import java.net.URLClassLoader;
2625
import java.util.ArrayList;
2726
import java.util.List;
28-
import java.util.Map;
29-
import java.util.concurrent.ConcurrentHashMap;
3027
import org.apache.maven.artifact.DependencyResolutionRequiredException;
3128
import org.apache.maven.plugin.AbstractMojo;
3229
import org.apache.maven.plugin.MojoExecutionException;
@@ -43,14 +40,14 @@
4340
/**
4441
* Maven plugin goal that starts the Google ADK Web Server with user-provided agents.
4542
*
46-
* <p>This Mojo provides a convenient way for developers to test and interact with their Google ADK
47-
* agents through a web-based user interface. The plugin dynamically loads user-defined agents and
48-
* makes them available through a browser interface.
43+
* <p>This Mojo provides a convenient way for developers to test and interact with their agents
44+
* through ADK Web UI. The plugin dynamically loads user-defined agents and makes them available
45+
* through a browser interface.
4946
*
5047
* <h3>Basic Usage</h3>
5148
*
5249
* <pre>{@code
53-
* mvn google-adk:web -Dagents=com.example.MyAgentProvider.INSTANCE
50+
* mvn google-adk:web -Dagents=com.example.MyAgentProvider
5451
* }</pre>
5552
*
5653
* <h3>Configuration Parameters</h3>
@@ -59,7 +56,8 @@
5956
* <li><strong>agents</strong> (required) - Full class path to AgentProvider implementation
6057
* <li><strong>port</strong> (optional, default: 8000) - Server port
6158
* <li><strong>host</strong> (optional, default: localhost) - Server host address
62-
* <li><strong>hotReloading</strong> (optional, default: true) - Enable hot reloading
59+
* <li><strong>hotReloading</strong> (optional, default: true) - Enable hot reloading for
60+
* config-based agents
6361
* </ul>
6462
*
6563
* <h3>AgentProvider Implementation</h3>
@@ -74,8 +72,8 @@
7472
*
7573
* <h3>Web Interface</h3>
7674
*
77-
* <p>Once started, the web interface is available at {@code http://host:port} where users can
78-
* interact with available agents.
75+
* <p>Once started, ADK Web UI is available at {@code http://host:port} where users can interact
76+
* with available agents.
7977
*
8078
* @author Google ADK Team
8179
* @since 0.2.1
@@ -103,7 +101,7 @@ public class WebMojo extends AbstractMojo {
103101
* <p>Example:
104102
*
105103
* <pre>{@code
106-
* mvn google-adk:web -Dagents=com.example.MyAgentProvider.INSTANCE
104+
* mvn google-adk:web -Dagents=com.example.MyAgentProvider
107105
* }</pre>
108106
*/
109107
@Parameter(property = "agents")
@@ -168,37 +166,23 @@ public void execute() throws MojoExecutionException, MojoFailureException {
168166
getLog().info("Loading agent provider: " + agents);
169167
AgentProvider provider = loadAgentProvider();
170168

171-
getLog().info("Retrieving agents from provider...");
172-
Map<String, BaseAgent> adkAgents = provider.getAgents();
173-
174-
if (adkAgents == null || adkAgents.isEmpty()) {
175-
throw new MojoExecutionException(
176-
"AgentProvider returned no agents. Please verify your provider implementation.");
177-
}
178-
179-
getLog().info("Successfully loaded " + adkAgents.size() + " agents: " + adkAgents.keySet());
180-
181169
// Set up system properties for Spring Boot
182170
setupSystemProperties();
183171

184-
// Create a registry that will be used by AdkWebServer
185-
Map<String, BaseAgent> agentRegistry = new ConcurrentHashMap<>(adkAgents);
186-
187-
// Start the Spring Boot application with custom agent registry
172+
// Start the Spring Boot application with custom agent provider
188173
SpringApplication app = new SpringApplication(AdkWebServer.class);
189174

190-
// Add the agent registry as a bean
175+
// Add the agent provider as a bean
191176
app.addInitializers(
192177
ctx -> {
193-
ctx.getBeanFactory().registerSingleton("userProvidedAgentRegistry", agentRegistry);
178+
ctx.getBeanFactory().registerSingleton("agentProvider", provider);
194179
});
195180

196181
getLog().info("Starting Spring Boot application...");
197182
applicationContext = app.run(new String[0]);
198183

199184
getLog().info("🎉 ADK Web Server started successfully!");
200185
getLog().info("🌐 Web UI available at: http://" + host + ":" + port);
201-
getLog().info("🛠️ Available agents: " + adkAgents.keySet());
202186
getLog().info("⏹️ Press Ctrl+C to stop the server...");
203187
getLog().info("");
204188

@@ -361,7 +345,7 @@ private AgentProvider tryLoadFromConstructor() throws MojoExecutionException {
361345
}
362346
}
363347

364-
/** Cleans up all resources including application context, classloader, and executor service. */
348+
/** Cleans up all resources including application context, classloader. */
365349
private void cleanupResources() {
366350
getLog().debug("Cleaning up resources...");
367351

0 commit comments

Comments
 (0)