39
39
import com .google .adk .agents .Callbacks .BeforeToolCallback ;
40
40
import com .google .adk .agents .Callbacks .BeforeToolCallbackBase ;
41
41
import com .google .adk .agents .Callbacks .BeforeToolCallbackSync ;
42
+ import com .google .adk .agents .ConfigAgentUtils .ConfigurationException ;
42
43
import com .google .adk .events .Event ;
43
44
import com .google .adk .examples .BaseExampleProvider ;
44
45
import com .google .adk .examples .Example ;
49
50
import com .google .adk .models .LlmRegistry ;
50
51
import com .google .adk .models .Model ;
51
52
import com .google .adk .tools .BaseTool ;
53
+ import com .google .adk .tools .BaseTool .ToolConfig ;
52
54
import com .google .adk .tools .BaseToolset ;
55
+ import com .google .common .base .CaseFormat ;
53
56
import com .google .common .base .Preconditions ;
54
57
import com .google .common .collect .ImmutableList ;
55
58
import com .google .errorprone .annotations .CanIgnoreReturnValue ;
59
62
import io .reactivex .rxjava3 .core .Flowable ;
60
63
import io .reactivex .rxjava3 .core .Maybe ;
61
64
import io .reactivex .rxjava3 .core .Single ;
65
+ import java .lang .reflect .Constructor ;
62
66
import java .util .ArrayList ;
63
67
import java .util .List ;
64
68
import java .util .Map ;
@@ -864,20 +868,20 @@ private Model resolveModelInternal() {
864
868
* @param configAbsPath The absolute path to the agent config file. This is needed for resolving
865
869
* relative paths for e.g. tools.
866
870
* @return the configured LlmAgent
867
- * @throws ConfigAgentUtils. ConfigurationException if the configuration is invalid
871
+ * @throws ConfigurationException if the configuration is invalid
868
872
* <p>TODO: Config agent features are not yet ready for public use.
869
873
*/
870
874
public static LlmAgent fromConfig (LlmAgentConfig config , String configAbsPath )
871
- throws ConfigAgentUtils . ConfigurationException {
875
+ throws ConfigurationException {
872
876
logger .debug ("Creating LlmAgent from config: {}" , config .name ());
873
877
874
878
// Validate required fields
875
879
if (config .name () == null || config .name ().trim ().isEmpty ()) {
876
- throw new ConfigAgentUtils . ConfigurationException ("Agent name is required" );
880
+ throw new ConfigurationException ("Agent name is required" );
877
881
}
878
882
879
883
if (config .instruction () == null || config .instruction ().trim ().isEmpty ()) {
880
- throw new ConfigAgentUtils . ConfigurationException ("Agent instruction is required" );
884
+ throw new ConfigurationException ("Agent instruction is required" );
881
885
}
882
886
883
887
// Create builder with required fields
@@ -891,6 +895,14 @@ public static LlmAgent fromConfig(LlmAgentConfig config, String configAbsPath)
891
895
builder .model (config .model ());
892
896
}
893
897
898
+ try {
899
+ if (config .tools () != null ) {
900
+ builder .tools (resolveTools (config .tools (), configAbsPath ));
901
+ }
902
+ } catch (ConfigurationException e ) {
903
+ throw new ConfigurationException ("Error resolving tools for agent " + config .name (), e );
904
+ }
905
+
894
906
// Set optional transfer configuration
895
907
if (config .disallowTransferToParent () != null ) {
896
908
builder .disallowTransferToParent (config .disallowTransferToParent ());
@@ -911,4 +923,116 @@ public static LlmAgent fromConfig(LlmAgentConfig config, String configAbsPath)
911
923
912
924
return agent ;
913
925
}
926
+
927
+ private static ImmutableList <BaseTool > resolveTools (
928
+ List <ToolConfig > toolConfigs , String configAbsPath ) throws ConfigurationException {
929
+
930
+ if (toolConfigs == null || toolConfigs .isEmpty ()) {
931
+ return ImmutableList .of ();
932
+ }
933
+
934
+ List <BaseTool > resolvedTools = new ArrayList <>();
935
+
936
+ for (ToolConfig toolConfig : toolConfigs ) {
937
+ try {
938
+ String toolName = toolConfig .name ();
939
+ if (toolName == null || toolName .trim ().isEmpty ()) {
940
+ throw new ConfigurationException ("Tool name cannot be empty" );
941
+ }
942
+
943
+ toolName = toolName .trim ();
944
+ BaseTool tool ;
945
+
946
+ if (!toolName .contains ("." )) {
947
+ tool = resolveBuiltInTool (toolName , toolConfig );
948
+ } else {
949
+ // TODO: Support user-defined tools
950
+ logger .debug ("configAbsPath is: {}" , configAbsPath );
951
+ throw new ConfigurationException ("User-defined tools are not yet supported" );
952
+ }
953
+
954
+ resolvedTools .add (tool );
955
+ logger .debug ("Successfully resolved tool: {}" , toolConfig .name ());
956
+ } catch (Exception e ) {
957
+ String errorMsg = "Failed to resolve tool: " + toolConfig .name ();
958
+ logger .error (errorMsg , e );
959
+ throw new ConfigurationException (errorMsg , e );
960
+ }
961
+ }
962
+
963
+ return ImmutableList .copyOf (resolvedTools );
964
+ }
965
+
966
+ private static BaseTool resolveBuiltInTool (String toolName , ToolConfig toolConfig )
967
+ throws ConfigurationException {
968
+ try {
969
+ logger .debug ("Resolving built-in tool: {}" , toolName );
970
+ // TODO: Handle built-in tool name end with Tool while config yaml file does not.
971
+ // e.g.google_search in config yaml file and GoogleSearchTool in tool class name.
972
+ String pascalCaseToolName = CaseFormat .LOWER_UNDERSCORE .to (CaseFormat .UPPER_CAMEL , toolName );
973
+ String className = "com.google.adk.tools." + pascalCaseToolName ;
974
+ Class <?> toolClass ;
975
+ try {
976
+ toolClass = Class .forName (className );
977
+ logger .debug ("Successfully loaded tool class: {}" , className );
978
+ } catch (ClassNotFoundException e ) {
979
+ String fallbackClassName = "com.google.adk.tools." + toolName ;
980
+ try {
981
+ toolClass = Class .forName (fallbackClassName );
982
+ } catch (ClassNotFoundException e2 ) {
983
+ throw new ConfigurationException (
984
+ "Built-in tool not found: "
985
+ + toolName
986
+ + ". Expected class: "
987
+ + className
988
+ + " or "
989
+ + fallbackClassName ,
990
+ e2 );
991
+ }
992
+ }
993
+
994
+ if (!BaseTool .class .isAssignableFrom (toolClass )) {
995
+ throw new ConfigurationException (
996
+ "Built-in tool class " + toolClass .getName () + " does not extend BaseTool" );
997
+ }
998
+
999
+ @ SuppressWarnings ("unchecked" )
1000
+ Class <? extends BaseTool > baseToolClass = (Class <? extends BaseTool >) toolClass ;
1001
+
1002
+ BaseTool tool = createToolInstance (baseToolClass , toolConfig );
1003
+ logger .info (
1004
+ "Successfully created built-in tool: {} (class: {})" , toolName , toolClass .getName ());
1005
+
1006
+ return tool ;
1007
+
1008
+ } catch (Exception e ) {
1009
+ logger .error ("Failed to create built-in tool: {}" , toolName , e );
1010
+ throw new ConfigurationException ("Failed to create built-in tool: " + toolName , e );
1011
+ }
1012
+ }
1013
+
1014
+ private static BaseTool createToolInstance (
1015
+ Class <? extends BaseTool > toolClass , ToolConfig toolConfig )
1016
+ throws ConfigAgentUtils .ConfigurationException {
1017
+
1018
+ try {
1019
+ // TODO:implement constructor with ToolArgsConfig
1020
+ logger .debug ("ToolConfig is: {}" , toolConfig );
1021
+
1022
+ // Try default constructor
1023
+ try {
1024
+ Constructor <? extends BaseTool > constructor = toolClass .getConstructor ();
1025
+ return constructor .newInstance ();
1026
+ } catch (NoSuchMethodException e ) {
1027
+ // Continue
1028
+ }
1029
+
1030
+ throw new ConfigAgentUtils .ConfigurationException (
1031
+ "No suitable constructor found for tool class: " + toolClass .getName ());
1032
+
1033
+ } catch (Exception e ) {
1034
+ throw new ConfigAgentUtils .ConfigurationException (
1035
+ "Failed to instantiate tool class: " + toolClass .getName (), e );
1036
+ }
1037
+ }
914
1038
}
0 commit comments