Skip to content

Commit c498d91

Browse files
Doris26copybara-github
authored andcommitted
feat: Add support for programmatic sub-agent resolution using 'code' key
This change introduces the ability to configure sub-agents in YAML by referencing a 'code' key. This key is used to look up an agent instance from the ComponentRegistry, allowing for programmatic registration and resolution of agents, similar to the Python ADK. This enhances flexibility in agent composition, enabling a mix of file-based and code-based sub-agent definitions. The ComponentRegistry and ConfigAgentUtils have been updated to support this new resolution method, and new examples and tests are included. PiperOrigin-RevId: 805053064
1 parent 2162f89 commit c498d91

5 files changed

Lines changed: 353 additions & 78 deletions

File tree

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

Lines changed: 7 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -31,48 +31,23 @@ public class BaseAgentConfig {
3131

3232
/**
3333
* Configuration for referencing other agents (subagents). Supports both config-based references
34-
* (YAML files) and programmatic references (Java classes).
34+
* (YAML files) and programmatic references (via code registry).
3535
*/
3636
public static class AgentRefConfig {
37-
private String name;
3837
private String configPath;
39-
private String className;
40-
private String staticField;
38+
private String code;
4139

4240
public AgentRefConfig() {}
4341

4442
/**
4543
* Constructor for config-based agent reference.
4644
*
47-
* @param name The name of the subagent
4845
* @param configPath The path to the subagent's config file
4946
*/
50-
public AgentRefConfig(String name, String configPath) {
51-
this.name = name;
47+
public AgentRefConfig(String configPath) {
5248
this.configPath = configPath;
5349
}
5450

55-
/**
56-
* Constructor for programmatic agent reference.
57-
*
58-
* @param name The name of the subagent
59-
* @param className The Java class name
60-
* @param staticField Optional static field name
61-
*/
62-
public AgentRefConfig(String name, String className, String staticField) {
63-
this.name = name;
64-
this.className = className;
65-
this.staticField = staticField;
66-
}
67-
68-
public String name() {
69-
return name;
70-
}
71-
72-
public void setName(String name) {
73-
this.name = name;
74-
}
75-
7651
public String configPath() {
7752
return configPath;
7853
}
@@ -81,35 +56,12 @@ public void setConfigPath(String configPath) {
8156
this.configPath = configPath;
8257
}
8358

84-
public String className() {
85-
return className;
86-
}
87-
88-
public void setClassName(String className) {
89-
this.className = className;
90-
}
91-
92-
public String staticField() {
93-
return staticField;
94-
}
95-
96-
public void setStaticField(String staticField) {
97-
this.staticField = staticField;
59+
public String code() {
60+
return code;
9861
}
9962

100-
@Override
101-
public String toString() {
102-
if (configPath != null) {
103-
return "AgentRefConfig{name='" + name + "', configPath='" + configPath + "'}";
104-
} else {
105-
return "AgentRefConfig{name='"
106-
+ name
107-
+ "', className='"
108-
+ className
109-
+ "', staticField='"
110-
+ staticField
111-
+ "'}";
112-
}
63+
public void setCode(String code) {
64+
this.code = code;
11365
}
11466
}
11567

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

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,7 @@ public static ImmutableList<BaseAgent> resolveSubAgents(
111111
resolvedSubAgents.add(subAgent);
112112
logger.debug("Successfully resolved subagent: {}", subAgent.name());
113113
} catch (Exception e) {
114-
String errorMsg =
115-
"Failed to resolve subagent: "
116-
+ (subAgentConfig.name() != null ? subAgentConfig.name() : "unnamed");
114+
String errorMsg = "Failed to resolve subagent";
117115
logger.error(errorMsg, e);
118116
throw new ConfigurationException(errorMsg, e);
119117
}
@@ -137,19 +135,18 @@ private static BaseAgent resolveSubAgent(
137135
return resolveSubAgentFromConfigPath(subAgentConfig, configDir);
138136
}
139137

140-
// TODO: Add support for programmatic subagent resolution (className/staticField).
141-
if (subAgentConfig.className() != null || subAgentConfig.staticField() != null) {
142-
throw new ConfigurationException(
143-
"Programmatic subagent resolution (className/staticField) is not yet supported for"
144-
+ " subagent: "
145-
+ subAgentConfig.name());
138+
// Check for programmatic references (only 'code' is supported)
139+
if (subAgentConfig.code() != null && !subAgentConfig.code().trim().isEmpty()) {
140+
String registryKey = subAgentConfig.code().trim();
141+
return ComponentRegistry.resolveAgentInstance(registryKey)
142+
.orElseThrow(
143+
() ->
144+
new ConfigurationException(
145+
"Failed to resolve subagent from registry with code key: " + registryKey));
146146
}
147147

148148
throw new ConfigurationException(
149-
"Subagent configuration for '"
150-
+ subAgentConfig.name()
151-
+ "' must specify 'configPath'."
152-
+ " Programmatic references (className/staticField) are not yet supported.");
149+
"Subagent configuration must specify either 'configPath' or 'code'.");
153150
}
154151

155152
/** Resolves a subagent from a configuration file path. */

core/src/main/java/com/google/adk/utils/ComponentRegistry.java

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,33 @@ public static synchronized void setInstance(ComponentRegistry newInstance) {
240240
logger.info("ComponentRegistry singleton instance updated");
241241
}
242242

243+
/**
244+
* Resolves an agent instance from the registry.
245+
*
246+
* <p>This method looks up an agent in the ComponentRegistry by the given key. The registry should
247+
* have been pre-populated with all available agents during initialization.
248+
*
249+
* <p>The key can be any string that was used to register the agent, such as:
250+
*
251+
* <ul>
252+
* <li>A class name: "com.example.LifeAgent"
253+
* <li>A static field reference: "com.example.LifeAgent.INSTANCE"
254+
* <li>A simple name: "life_agent"
255+
* <li>Any custom key: "sub_agents_config.life_agent.agent"
256+
* </ul>
257+
*
258+
* @param name the registry key to look up
259+
* @return an Optional containing the BaseAgent if found, or empty if not found
260+
*/
261+
public static Optional<BaseAgent> resolveAgentInstance(String name) {
262+
if (isNullOrEmpty(name)) {
263+
return Optional.empty();
264+
}
265+
266+
ComponentRegistry registry = getInstance();
267+
return registry.get(name, BaseAgent.class);
268+
}
269+
243270
/**
244271
* Resolves the agent class based on the agent class name from the configuration.
245272
*
@@ -274,6 +301,12 @@ public static Class<? extends BaseAgent> resolveAgentClass(String agentClassName
274301
if (agentClass.isPresent() && BaseAgent.class.isAssignableFrom(agentClass.get())) {
275302
return (Class<? extends BaseAgent>) agentClass.get();
276303
}
304+
305+
// For Python compatibility, also try with google.adk.agents prefix
306+
agentClass = registry.get("google.adk.agents." + agentClassName, Class.class);
307+
if (agentClass.isPresent() && BaseAgent.class.isAssignableFrom(agentClass.get())) {
308+
return (Class<? extends BaseAgent>) agentClass.get();
309+
}
277310
}
278311

279312
throw new IllegalArgumentException(
@@ -322,14 +355,11 @@ public static Optional<BaseTool> resolveToolInstance(String name) {
322355
// If name contains '.', use it directly
323356
return registry.get(name, BaseTool.class);
324357
} else {
325-
// First try the simple name
326-
Optional<BaseTool> toolInstance = registry.get(name, BaseTool.class);
327-
if (toolInstance.isPresent()) {
328-
return toolInstance;
329-
}
330-
331-
// If not found, try with google.adk.tools prefix
332-
return registry.get("google.adk.tools." + name, BaseTool.class);
358+
// Try simple name, then common prefixes (com/google)
359+
return registry
360+
.get(name, BaseTool.class)
361+
.or(() -> registry.get("com.google.adk.tools." + name, BaseTool.class))
362+
.or(() -> registry.get("google.adk.tools." + name, BaseTool.class));
333363
}
334364
}
335365

@@ -360,7 +390,12 @@ public static Optional<Class<? extends BaseTool>> resolveToolClass(String toolCl
360390
return Optional.of((Class<? extends BaseTool>) toolClass.get());
361391
}
362392

363-
// If not found, try with google.adk.tools prefix
393+
// If not found, try with common prefixes (com/google)
394+
toolClass = registry.get("com.google.adk.tools." + toolClassName, Class.class);
395+
if (toolClass.isPresent() && BaseTool.class.isAssignableFrom(toolClass.get())) {
396+
return Optional.of((Class<? extends BaseTool>) toolClass.get());
397+
}
398+
364399
toolClass = registry.get("google.adk.tools." + toolClassName, Class.class);
365400
if (toolClass.isPresent() && BaseTool.class.isAssignableFrom(toolClass.get())) {
366401
return Optional.of((Class<? extends BaseTool>) toolClass.get());

0 commit comments

Comments
 (0)