Skip to content
Merged
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
18 changes: 15 additions & 3 deletions dev/src/main/java/com/google/adk/web/service/RunnerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@
import com.google.adk.agents.BaseAgent;
import com.google.adk.artifacts.BaseArtifactService;
import com.google.adk.memory.BaseMemoryService;
import com.google.adk.plugins.BasePlugin;
import com.google.adk.runner.Runner;
import com.google.adk.sessions.BaseSessionService;
import com.google.adk.web.AgentLoader;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
Expand All @@ -40,18 +44,21 @@ public class RunnerService {
private final BaseArtifactService artifactService;
private final BaseSessionService sessionService;
private final BaseMemoryService memoryService;
private final List<BasePlugin> extraPlugins;
private final Map<String, Runner> runnerCache = new ConcurrentHashMap<>();

@Autowired
public RunnerService(
AgentLoader agentProvider,
BaseArtifactService artifactService,
BaseSessionService sessionService,
BaseMemoryService memoryService) {
BaseMemoryService memoryService,
@Autowired(required = false) @Qualifier("extraPlugins") List<BasePlugin> extraPlugins) {
this.agentProvider = agentProvider;
this.artifactService = artifactService;
this.sessionService = sessionService;
this.memoryService = memoryService;
this.extraPlugins =
extraPlugins != null ? ImmutableList.copyOf(extraPlugins) : ImmutableList.of();
}

/**
Expand All @@ -72,7 +79,12 @@ public Runner getRunner(String appName) {
appName,
agent.name());
return new Runner(
agent, appName, this.artifactService, this.sessionService, this.memoryService);
agent,
appName,
this.artifactService,
this.sessionService,
this.memoryService,
this.extraPlugins);
} catch (java.util.NoSuchElementException e) {
log.error(
"Agent/App named '{}' not found in registry. Available apps: {}",
Expand Down
86 changes: 80 additions & 6 deletions maven_plugin/src/main/java/com/google/adk/maven/WebMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@

package com.google.adk.maven;

import autovalue.shaded.com.google.common.collect.ImmutableList;
import com.google.adk.plugins.BasePlugin;
import com.google.adk.utils.ComponentRegistry;
import com.google.adk.web.AdkWebServer;
import com.google.adk.web.AgentLoader;
import com.google.common.base.Strings;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
Expand Down Expand Up @@ -180,6 +184,30 @@ public class WebMojo extends AbstractMojo {
@Parameter(property = "registry")
private String registry;

/**
* Comma-separated list of additional plugin classes to load.
*
* <p>This parameter allows users to specify extra plugins that extend BasePlugin. Plugins provide
* global callbacks for logging, monitoring, replay, caching, or modifying agent behaviors. These
* are additional to any plugins configured by the user's code.
*
* <p>Each plugin reference can be either:
*
* <ul>
* <li><strong>Static field reference:</strong> {@code com.example.MyPlugin.INSTANCE}
* <li><strong>Class name:</strong> {@code com.example.MyPlugin} (requires default constructor)
* </ul>
*
* <p>Example:
*
* <pre>{@code
* mvn google-adk:web -Dagents=... -DextraPlugins=com.google.adk.plugins.ReplayPlugin
* mvn google-adk:web -Dagents=... -DextraPlugins=com.google.adk.plugins.ReplayPlugin,com.example.CustomPlugin
* }</pre>
*/
@Parameter(property = "extraPlugins")
private String extraPlugins;

private ConfigurableApplicationContext applicationContext;
private URLClassLoader projectClassLoader;

Expand All @@ -205,18 +233,22 @@ public void execute() throws MojoExecutionException, MojoFailureException {

// Load and instantiate the AgentLoader
getLog().info("Loading agent loader: " + agents);
com.google.adk.web.AgentLoader provider = loadAgentProvider();
AgentLoader provider = loadAgentProvider();

// Load extra plugins if specified
final List<BasePlugin> extraPluginInstances = loadExtraPlugins();

// Set up system properties for Spring Boot
setupSystemProperties();

// Start the Spring Boot application with custom agent provider
SpringApplication app = new SpringApplication(AdkWebServer.class);

// Add the agent provider as a bean
// Add the agent provider and extra plugins as beans
app.addInitializers(
ctx -> {
ctx.getBeanFactory().registerSingleton("agentLoader", provider);
ctx.getBeanFactory().registerSingleton("extraPlugins", extraPluginInstances);
});

getLog().info("Starting Spring Boot application...");
Expand Down Expand Up @@ -268,6 +300,7 @@ private void logConfiguration() {
getLog().info(" Server Host: " + host);
getLog().info(" Server Port: " + port);
getLog().info(" Registry: " + (registry != null ? registry : "default"));
getLog().info(" Extra Plugins: " + (extraPlugins != null ? extraPlugins : "none"));
}

private void setupSystemProperties() {
Expand Down Expand Up @@ -401,7 +434,7 @@ private <T> T tryLoadFromConstructor(String className, Class<T> expectedType)
}
}

private com.google.adk.web.AgentLoader loadAgentProvider() throws MojoExecutionException {
private AgentLoader loadAgentProvider() throws MojoExecutionException {
// First, check if agents parameter is a directory path
Path agentsPath = Paths.get(agents);
if (Files.isDirectory(agentsPath)) {
Expand All @@ -411,15 +444,56 @@ private com.google.adk.web.AgentLoader loadAgentProvider() throws MojoExecutionE

// Next, try to interpret as class.field syntax
if (agents.contains(".")) {
com.google.adk.web.AgentLoader provider =
tryLoadFromStaticField(agents, com.google.adk.web.AgentLoader.class);
AgentLoader provider = tryLoadFromStaticField(agents, AgentLoader.class);
if (provider != null) {
return provider;
}
}

// Fallback to trying the entire string as a class name
return tryLoadFromConstructor(agents, com.google.adk.web.AgentLoader.class);
return tryLoadFromConstructor(agents, AgentLoader.class);
}

/**
* Loads extra plugins from the comma-separated extraPlugins parameter.
*
* @return List of loaded plugin instances
* @throws MojoExecutionException if plugins cannot be loaded
*/
private ImmutableList<BasePlugin> loadExtraPlugins() throws MojoExecutionException {
ImmutableList.Builder<BasePlugin> plugins = ImmutableList.builder();
String[] pluginNames = Strings.nullToEmpty(extraPlugins).split(",");

for (String name : pluginNames) {
name = name.trim();
if (name.isEmpty()) {
continue;
}

getLog().info("Loading plugin: " + name);

// Try to load as static field first
BasePlugin plugin = null;
if (name.contains(".")) {
plugin = tryLoadFromStaticField(name, BasePlugin.class);
}

// Fallback to constructor
if (plugin == null) {
plugin = tryLoadFromConstructor(name, BasePlugin.class);
}

if (plugin == null) {
throw new MojoExecutionException("Failed to load plugin: " + name);
}

plugins.add(plugin);
getLog()
.info(
String.format("Successfully loaded plugin: `%s` from `%s`", plugin.getName(), name));
}

return plugins.build();
}

/** Cleans up all resources including application context, classloader. */
Expand Down