3434import com .google .genai .types .LiveConnectConfig ;
3535import com .google .genai .types .Tool ;
3636import io .reactivex .rxjava3 .core .Completable ;
37+ import io .reactivex .rxjava3 .core .Maybe ;
3738import io .reactivex .rxjava3 .core .Single ;
3839import java .util .HashMap ;
3940import java .util .Map ;
@@ -48,6 +49,8 @@ public abstract class BaseTool {
4849 private final String name ;
4950 private final String description ;
5051 private final boolean isLongRunning ;
52+ private final boolean overridesRunAsync ;
53+ private final boolean overridesRunMaybeAsync ;
5154 private final HashMap <String , Object > customMetadata ;
5255
5356 protected BaseTool (@ Nonnull String name , @ Nonnull String description ) {
@@ -58,6 +61,8 @@ protected BaseTool(@Nonnull String name, @Nonnull String description, boolean is
5861 this .name = name ;
5962 this .description = description ;
6063 this .isLongRunning = isLongRunning ;
64+ overridesRunAsync = overridesMethod ("runAsync" );
65+ overridesRunMaybeAsync = overridesMethod ("runMaybeAsync" );
6166 customMetadata = new HashMap <>();
6267 }
6368
@@ -90,6 +95,24 @@ public void setCustomMetadata(String key, Object value) {
9095
9196 /** Calls a tool. */
9297 public Single <Map <String , Object >> runAsync (Map <String , Object > args , ToolContext toolContext ) {
98+ if (overridesRunMaybeAsync ) {
99+ return runMaybeAsync (args , toolContext ).defaultIfEmpty (ImmutableMap .<String , Object >of ());
100+ }
101+ throw new UnsupportedOperationException ("This method is not implemented." );
102+ }
103+
104+ /**
105+ * Calls a tool and optionally returns a function response.
106+ *
107+ * <p>Override this method for long-running tools that may end the current invocation without
108+ * emitting a function response event. This default implementation delegates to {@link
109+ * #runAsync(Map, ToolContext)} for backwards compatibility.
110+ */
111+ public Maybe <Map <String , Object >> runMaybeAsync (
112+ Map <String , Object > args , ToolContext toolContext ) {
113+ if (overridesRunAsync ) {
114+ return runAsync (args , toolContext ).toMaybe ();
115+ }
93116 throw new UnsupportedOperationException ("This method is not implemented." );
94117 }
95118
@@ -180,6 +203,15 @@ private static ImmutableList<Tool> findToolsWithoutFunctionDeclarations(LlmReque
180203 .orElse (ImmutableList .of ());
181204 }
182205
206+ private boolean overridesMethod (String methodName ) {
207+ try {
208+ return getClass ().getMethod (methodName , Map .class , ToolContext .class ).getDeclaringClass ()
209+ != BaseTool .class ;
210+ } catch (NoSuchMethodException e ) {
211+ throw new IllegalStateException ("Missing tool method: " + methodName , e );
212+ }
213+ }
214+
183215 /**
184216 * Creates a tool instance from a config.
185217 *
0 commit comments