Skip to content

Commit 18dc8ad

Browse files
fix confilct
1 parent 867433f commit 18dc8ad

File tree

1 file changed

+3
-311
lines changed

1 file changed

+3
-311
lines changed

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

Lines changed: 3 additions & 311 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@
5454
import com.google.adk.tools.BaseTool.ToolArgsConfig;
5555
import com.google.adk.tools.BaseTool.ToolConfig;
5656
import com.google.adk.tools.BaseToolset;
57-
import com.google.adk.utils.ComponentRegistry;
5857
import com.google.adk.tools.internal.ToolConstraints;
58+
import com.google.adk.utils.ComponentRegistry;
5959
import com.google.common.base.Preconditions;
6060
import com.google.common.collect.ImmutableList;
6161
import com.google.errorprone.annotations.CanIgnoreReturnValue;
@@ -613,14 +613,9 @@ protected void validate() {
613613
}
614614

615615
public LlmAgent build() {
616+
validate();
616617
LlmAgent built = new LlmAgent(this);
617-
// New Guardrail Check
618-
if (Boolean.getBoolean("adk.enableToolConstraints")) {
619-
try {
620-
ToolConstraints.validateAgentTree(built);
621-
} catch (NullPointerException ignored) {
622-
}
623-
}
618+
ToolConstraints.validateAgentTree(built);
624619
return built;
625620
}
626621
}
@@ -1190,307 +1185,4 @@ static BaseTool resolveInstanceViaReflection(String toolName) throws Exception {
11901185
}
11911186
return null;
11921187
}
1193-
1194-
/**
1195-
* Creates an LlmAgent from configuration.
1196-
*
1197-
* @param config the agent configuration
1198-
* @param configAbsPath The absolute path to the agent config file. This is needed for resolving
1199-
* relative paths for e.g. tools.
1200-
* @return the configured LlmAgent
1201-
* @throws ConfigurationException if the configuration is invalid
1202-
* <p>TODO: Config agent features are not yet ready for public use.
1203-
*/
1204-
public static LlmAgent fromConfig(LlmAgentConfig config, String configAbsPath)
1205-
throws ConfigurationException {
1206-
logger.debug("Creating LlmAgent from config: {}", config.name());
1207-
1208-
// Validate required fields
1209-
if (config.name() == null || config.name().trim().isEmpty()) {
1210-
throw new ConfigurationException("Agent name is required");
1211-
}
1212-
1213-
if (config.instruction() == null || config.instruction().trim().isEmpty()) {
1214-
throw new ConfigurationException("Agent instruction is required");
1215-
}
1216-
1217-
// Create builder with required fields
1218-
Builder builder =
1219-
LlmAgent.builder()
1220-
.name(config.name())
1221-
.description(nullToEmpty(config.description()))
1222-
.instruction(config.instruction());
1223-
1224-
if (config.model() != null && !config.model().trim().isEmpty()) {
1225-
builder.model(config.model());
1226-
}
1227-
1228-
try {
1229-
if (config.tools() != null) {
1230-
builder.tools(resolveTools(config.tools(), configAbsPath));
1231-
}
1232-
} catch (ConfigurationException e) {
1233-
throw new ConfigurationException("Error resolving tools for agent " + config.name(), e);
1234-
}
1235-
1236-
// Set optional transfer configuration
1237-
if (config.disallowTransferToParent() != null) {
1238-
builder.disallowTransferToParent(config.disallowTransferToParent());
1239-
}
1240-
1241-
if (config.disallowTransferToPeers() != null) {
1242-
builder.disallowTransferToPeers(config.disallowTransferToPeers());
1243-
}
1244-
1245-
// Set optional output key
1246-
if (config.outputKey() != null && !config.outputKey().trim().isEmpty()) {
1247-
builder.outputKey(config.outputKey());
1248-
}
1249-
1250-
// Build and return the agent
1251-
LlmAgent agent = builder.build();
1252-
logger.info("Successfully created LlmAgent: {}", agent.name());
1253-
1254-
return agent;
1255-
}
1256-
1257-
/**
1258-
* Resolves a list of tool configurations into {@link BaseTool} instances.
1259-
*
1260-
* <p>This method is only for use by Agent Development Kit.
1261-
*
1262-
* @param toolConfigs The list of tool configurations to resolve.
1263-
* @param configAbsPath The absolute path to the agent config file currently being processed. This
1264-
* path can be used to resolve relative paths for tool configurations, if necessary.
1265-
* @return An immutable list of resolved {@link BaseTool} instances.
1266-
* @throws ConfigurationException if any tool configuration is invalid (e.g., missing name), if a
1267-
* tool cannot be found by its name or class, or if tool instantiation fails.
1268-
*/
1269-
static ImmutableList<BaseTool> resolveTools(List<ToolConfig> toolConfigs, String configAbsPath)
1270-
throws ConfigurationException {
1271-
1272-
if (toolConfigs == null || toolConfigs.isEmpty()) {
1273-
return ImmutableList.of();
1274-
}
1275-
1276-
ImmutableList.Builder<BaseTool> resolvedTools = ImmutableList.builder();
1277-
1278-
for (ToolConfig toolConfig : toolConfigs) {
1279-
try {
1280-
if (isNullOrEmpty(toolConfig.name())) {
1281-
throw new ConfigurationException("Tool name cannot be empty");
1282-
}
1283-
1284-
String toolName = toolConfig.name().trim();
1285-
1286-
// Option 1: Try to resolve as a tool instance
1287-
BaseTool tool = resolveToolInstance(toolName);
1288-
if (tool != null) {
1289-
resolvedTools.add(tool);
1290-
logger.debug("Successfully resolved tool instance: {}", toolName);
1291-
continue;
1292-
}
1293-
1294-
// Option 2: Try to resolve as a tool class (with or without args)
1295-
BaseTool toolFromClass = resolveToolFromClass(toolName, toolConfig.args());
1296-
if (toolFromClass != null) {
1297-
resolvedTools.add(toolFromClass);
1298-
logger.debug("Successfully resolved tool from class: {}", toolName);
1299-
continue;
1300-
}
1301-
1302-
throw new ConfigurationException("Tool not found: " + toolName);
1303-
1304-
} catch (Exception e) {
1305-
String errorMsg = "Failed to resolve tool: " + toolConfig.name();
1306-
logger.error(errorMsg, e);
1307-
throw new ConfigurationException(errorMsg, e);
1308-
}
1309-
}
1310-
1311-
return resolvedTools.build();
1312-
}
1313-
1314-
/**
1315-
* Resolves a tool instance by its unique name or its static field reference.
1316-
*
1317-
* <p>It first checks the {@link ComponentRegistry} for a registered tool instance. If not found,
1318-
* and the name looks like a fully qualified Java name referencing a static field (e.g.,
1319-
* "com.google.mytools.MyToolClass.INSTANCE"), it attempts to resolve it via reflection using
1320-
* {@link #resolveInstanceViaReflection(String)}.
1321-
*
1322-
* @param toolName The name of the tool or a static field reference (e.g., "myTool",
1323-
* "com.google.mytools.MyToolClass.INSTANCE").
1324-
* @return The resolved tool instance, or {@code null} if the tool is not found in the registry
1325-
* and cannot be resolved via reflection.
1326-
*/
1327-
@Nullable
1328-
static BaseTool resolveToolInstance(String toolName) {
1329-
ComponentRegistry registry = ComponentRegistry.getInstance();
1330-
1331-
// First try registry
1332-
Optional<BaseTool> toolOpt = ComponentRegistry.resolveToolInstance(toolName);
1333-
if (toolOpt.isPresent()) {
1334-
return toolOpt.get();
1335-
}
1336-
1337-
// If not in registry and looks like Java qualified name, try reflection
1338-
if (isJavaQualifiedName(toolName)) {
1339-
try {
1340-
BaseTool tool = resolveInstanceViaReflection(toolName);
1341-
if (tool != null) {
1342-
registry.register(toolName, tool);
1343-
logger.debug("Resolved and registered tool instance via reflection: {}", toolName);
1344-
return tool;
1345-
}
1346-
} catch (Exception e) {
1347-
logger.debug("Failed to resolve instance via reflection: {}", toolName, e);
1348-
}
1349-
}
1350-
logger.debug("Could not resolve tool instance: {}", toolName);
1351-
return null;
1352-
}
1353-
1354-
/**
1355-
* Resolves a tool from a class name and optional arguments.
1356-
*
1357-
* <p>It attempts to load the class specified by {@code className}. If {@code args} are provided
1358-
* and non-empty, it looks for a static factory method {@code fromConfig(ToolArgsConfig)} on the
1359-
* class to instantiate the tool. If {@code args} are null or empty, it looks for a default
1360-
* constructor.
1361-
*
1362-
* @param className The fully qualified name of the tool class to instantiate.
1363-
* @param args Optional configuration arguments for tool creation. If provided, the class must
1364-
* implement a static {@code fromConfig(ToolArgsConfig)} factory method. If null or empty, the
1365-
* class must have a default constructor.
1366-
* @return The instantiated tool instance, or {@code null} if the class cannot be found or loaded.
1367-
* @throws ConfigurationException if {@code args} are provided but no {@code fromConfig} method
1368-
* exists, if {@code args} are not provided but no default constructor exists, or if
1369-
* instantiation via the factory method or constructor fails.
1370-
*/
1371-
@Nullable
1372-
static BaseTool resolveToolFromClass(String className, ToolArgsConfig args)
1373-
throws ConfigurationException {
1374-
ComponentRegistry registry = ComponentRegistry.getInstance();
1375-
1376-
// First try registry for class
1377-
Optional<Class<? extends BaseTool>> classOpt = ComponentRegistry.resolveToolClass(className);
1378-
Class<? extends BaseTool> toolClass = null;
1379-
1380-
if (classOpt.isPresent()) {
1381-
toolClass = classOpt.get();
1382-
} else if (isJavaQualifiedName(className)) {
1383-
// Try reflection to get class
1384-
try {
1385-
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
1386-
if (BaseTool.class.isAssignableFrom(clazz)) {
1387-
toolClass = clazz.asSubclass(BaseTool.class);
1388-
// Optimization: register for reuse
1389-
registry.register(className, toolClass);
1390-
logger.debug("Resolved and registered tool class via reflection: {}", className);
1391-
}
1392-
} catch (ClassNotFoundException e) {
1393-
logger.debug("Failed to resolve class via reflection: {}", className, e);
1394-
return null;
1395-
}
1396-
}
1397-
1398-
if (toolClass == null) {
1399-
return null;
1400-
}
1401-
1402-
// If args provided and not empty, try fromConfig method first
1403-
if (args != null && !args.isEmpty()) {
1404-
try {
1405-
Method fromConfigMethod = toolClass.getMethod("fromConfig", ToolArgsConfig.class);
1406-
Object instance = fromConfigMethod.invoke(null, args);
1407-
if (instance instanceof BaseTool baseTool) {
1408-
return baseTool;
1409-
}
1410-
} catch (NoSuchMethodException e) {
1411-
throw new ConfigurationException(
1412-
"Class " + className + " does not have fromConfig method but args were provided.", e);
1413-
} catch (Exception e) {
1414-
logger.error("Error calling fromConfig on class {}", className, e);
1415-
throw new ConfigurationException("Error creating tool from class " + className, e);
1416-
}
1417-
}
1418-
1419-
// No args provided or empty args, try default constructor
1420-
try {
1421-
Constructor<? extends BaseTool> constructor = toolClass.getDeclaredConstructor();
1422-
constructor.setAccessible(true);
1423-
return constructor.newInstance();
1424-
} catch (NoSuchMethodException e) {
1425-
throw new ConfigurationException(
1426-
"Class " + className + " does not have a default constructor and no args were provided.",
1427-
e);
1428-
} catch (Exception e) {
1429-
logger.error("Error calling default constructor on class {}", className, e);
1430-
throw new ConfigurationException(
1431-
"Error creating tool from class " + className + " using default constructor", e);
1432-
}
1433-
}
1434-
1435-
/**
1436-
* Checks if a string appears to be a Java fully qualified name, such as "com.google.adk.MyClass"
1437-
* or "com.google.adk.MyClass.MY_FIELD".
1438-
*
1439-
* <p>It verifies that the name contains at least one dot ('.') and consists of characters valid
1440-
* for Java identifiers and package names.
1441-
*
1442-
* @param name The string to check.
1443-
* @return {@code true} if the string matches the pattern of a Java qualified name, {@code false}
1444-
* otherwise.
1445-
*/
1446-
static boolean isJavaQualifiedName(String name) {
1447-
if (name == null || name.trim().isEmpty()) {
1448-
return false;
1449-
}
1450-
return name.contains(".") && name.matches("^[a-zA-Z_$][a-zA-Z0-9_.$]*$");
1451-
}
1452-
1453-
/**
1454-
* Resolves a {@link BaseTool} instance by attempting to access a public static field via
1455-
* reflection.
1456-
*
1457-
* <p>This method expects {@code toolName} to be in the format
1458-
* "com.google.package.ClassName.STATIC_FIELD_NAME", where "STATIC_FIELD_NAME" is the name of a
1459-
* public static field in "com.google.package.ClassName" that holds a {@link BaseTool} instance.
1460-
*
1461-
* @param toolName The fully qualified name of a static field holding a tool instance.
1462-
* @return The {@link BaseTool} instance, or {@code null} if {@code toolName} is not in the
1463-
* expected format, or if the field is not found, not static, or not of type {@link BaseTool}.
1464-
* @throws Exception if the class specified in {@code toolName} cannot be loaded, or if there is a
1465-
* security manager preventing reflection, or if accessing the field causes an exception.
1466-
*/
1467-
@Nullable
1468-
static BaseTool resolveInstanceViaReflection(String toolName) throws Exception {
1469-
int lastDotIndex = toolName.lastIndexOf('.');
1470-
if (lastDotIndex == -1) {
1471-
return null;
1472-
}
1473-
1474-
String className = toolName.substring(0, lastDotIndex);
1475-
String fieldName = toolName.substring(lastDotIndex + 1);
1476-
1477-
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
1478-
1479-
try {
1480-
Field field = clazz.getField(fieldName);
1481-
if (!Modifier.isStatic(field.getModifiers())) {
1482-
logger.debug("Field {} in class {} is not static", fieldName, className);
1483-
return null;
1484-
}
1485-
Object instance = field.get(null);
1486-
if (instance instanceof BaseTool baseTool) {
1487-
return baseTool;
1488-
} else {
1489-
logger.debug("Field {} in class {} is not a BaseTool instance", fieldName, className);
1490-
}
1491-
} catch (NoSuchFieldException e) {
1492-
logger.debug("Field {} not found in class {}", fieldName, className);
1493-
}
1494-
return null;
1495-
}
14961188
}

0 commit comments

Comments
 (0)