|
30 | 30 | import com.google.adk.events.EventActions; |
31 | 31 | import com.google.adk.tools.BaseTool; |
32 | 32 | import com.google.adk.tools.FunctionTool; |
| 33 | +import com.google.adk.tools.MissingToolResolutionStrategy; |
33 | 34 | import com.google.adk.tools.ToolConfirmation; |
34 | 35 | import com.google.adk.tools.ToolContext; |
35 | | -import com.google.common.base.VerifyException; |
36 | 36 | import com.google.common.collect.ImmutableList; |
37 | 37 | import com.google.common.collect.ImmutableMap; |
38 | 38 | import com.google.genai.types.Content; |
@@ -72,6 +72,84 @@ public static String generateClientFunctionCallId() { |
72 | 72 | return AF_FUNCTION_CALL_ID_PREFIX + UUID.randomUUID(); |
73 | 73 | } |
74 | 74 |
|
| 75 | + /** Container for separated valid and missing tool calls. */ |
| 76 | + private static class ToolCallSeparation { |
| 77 | + private final ImmutableList<FunctionCall> validCalls; |
| 78 | + private final Flowable<Event> missingToolsFlowable; |
| 79 | + |
| 80 | + ToolCallSeparation( |
| 81 | + ImmutableList<FunctionCall> validCalls, Flowable<Event> missingToolsFlowable) { |
| 82 | + this.validCalls = validCalls; |
| 83 | + this.missingToolsFlowable = missingToolsFlowable; |
| 84 | + } |
| 85 | + |
| 86 | + ImmutableList<FunctionCall> validCalls() { |
| 87 | + return validCalls; |
| 88 | + } |
| 89 | + |
| 90 | + Flowable<Event> missingToolsFlowable() { |
| 91 | + return missingToolsFlowable; |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + /** |
| 96 | + * Separates function calls into valid calls and missing tool events. |
| 97 | + * |
| 98 | + * @param invocationContext The invocation context. |
| 99 | + * @param functionCalls The list of function calls to separate. |
| 100 | + * @param tools The available tools. |
| 101 | + * @return A ToolCallSeparation containing valid calls and a flowable for missing tools. |
| 102 | + */ |
| 103 | + private static ToolCallSeparation separateValidAndMissingToolCalls( |
| 104 | + InvocationContext invocationContext, |
| 105 | + ImmutableList<FunctionCall> functionCalls, |
| 106 | + Map<String, BaseTool> tools) { |
| 107 | + MissingToolResolutionStrategy missingToolResolutionStrategy = |
| 108 | + invocationContext.runConfig().missingToolResolutionStrategy(); |
| 109 | + ImmutableList.Builder<Maybe<Event>> missingTools = ImmutableList.builder(); |
| 110 | + ImmutableList.Builder<FunctionCall> validCalls = ImmutableList.builder(); |
| 111 | + |
| 112 | + for (FunctionCall functionCall : functionCalls) { |
| 113 | + if (!tools.containsKey(functionCall.name().get())) { |
| 114 | + missingTools.add( |
| 115 | + missingToolResolutionStrategy.onMissingTool(invocationContext, functionCall)); |
| 116 | + } else { |
| 117 | + validCalls.add(functionCall); |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + Flowable<Event> missingToolsFlowable = |
| 122 | + Flowable.fromIterable(missingTools.build()).concatMapMaybe(maybe -> maybe); |
| 123 | + |
| 124 | + return new ToolCallSeparation(validCalls.build(), missingToolsFlowable); |
| 125 | + } |
| 126 | + |
| 127 | + /** |
| 128 | + * Creates a combined flowable of function response events based on execution mode. |
| 129 | + * |
| 130 | + * @param invocationContext The invocation context. |
| 131 | + * @param validCalls The list of valid function calls. |
| 132 | + * @param missingToolsFlowable The flowable for missing tool events. |
| 133 | + * @param functionCallMapper The mapper to convert function calls to events. |
| 134 | + * @return A combined flowable of all events. |
| 135 | + */ |
| 136 | + private static Flowable<Event> createCombinedFlowable( |
| 137 | + InvocationContext invocationContext, |
| 138 | + ImmutableList<FunctionCall> validCalls, |
| 139 | + Flowable<Event> missingToolsFlowable, |
| 140 | + Function<FunctionCall, Maybe<Event>> functionCallMapper) { |
| 141 | + Flowable<Event> functionResponseEventsFlowable; |
| 142 | + if (invocationContext.runConfig().toolExecutionMode() == ToolExecutionMode.SEQUENTIAL) { |
| 143 | + functionResponseEventsFlowable = |
| 144 | + Flowable.fromIterable(validCalls).concatMapMaybe(functionCallMapper); |
| 145 | + } else { |
| 146 | + functionResponseEventsFlowable = |
| 147 | + Flowable.fromIterable(validCalls).flatMapMaybe(functionCallMapper); |
| 148 | + } |
| 149 | + |
| 150 | + return Flowable.concat(missingToolsFlowable, functionResponseEventsFlowable); |
| 151 | + } |
| 152 | + |
75 | 153 | /** |
76 | 154 | * Populates missing function call IDs in the provided event's content. |
77 | 155 | * |
@@ -137,12 +215,8 @@ public static Maybe<Event> handleFunctionCalls( |
137 | 215 | Map<String, BaseTool> tools, |
138 | 216 | Map<String, ToolConfirmation> toolConfirmations) { |
139 | 217 | ImmutableList<FunctionCall> functionCalls = functionCallEvent.functionCalls(); |
140 | | - |
141 | | - for (FunctionCall functionCall : functionCalls) { |
142 | | - if (!tools.containsKey(functionCall.name().get())) { |
143 | | - throw new VerifyException("Tool not found: " + functionCall.name().get()); |
144 | | - } |
145 | | - } |
| 218 | + ToolCallSeparation separation = |
| 219 | + separateValidAndMissingToolCalls(invocationContext, functionCalls, tools); |
146 | 220 |
|
147 | 221 | Function<FunctionCall, Maybe<Event>> functionCallMapper = |
148 | 222 | functionCall -> { |
@@ -199,15 +273,13 @@ public static Maybe<Event> handleFunctionCalls( |
199 | 273 | }); |
200 | 274 | }; |
201 | 275 |
|
202 | | - Flowable<Event> functionResponseEventsFlowable; |
203 | | - if (invocationContext.runConfig().toolExecutionMode() == ToolExecutionMode.SEQUENTIAL) { |
204 | | - functionResponseEventsFlowable = |
205 | | - Flowable.fromIterable(functionCalls).concatMapMaybe(functionCallMapper); |
206 | | - } else { |
207 | | - functionResponseEventsFlowable = |
208 | | - Flowable.fromIterable(functionCalls).flatMapMaybe(functionCallMapper); |
209 | | - } |
210 | | - return functionResponseEventsFlowable |
| 276 | + Flowable<Event> allEventsFlowable = |
| 277 | + createCombinedFlowable( |
| 278 | + invocationContext, |
| 279 | + separation.validCalls(), |
| 280 | + separation.missingToolsFlowable(), |
| 281 | + functionCallMapper); |
| 282 | + return allEventsFlowable |
211 | 283 | .toList() |
212 | 284 | .flatMapMaybe( |
213 | 285 | events -> { |
@@ -242,12 +314,8 @@ public static Maybe<Event> handleFunctionCalls( |
242 | 314 | public static Maybe<Event> handleFunctionCallsLive( |
243 | 315 | InvocationContext invocationContext, Event functionCallEvent, Map<String, BaseTool> tools) { |
244 | 316 | ImmutableList<FunctionCall> functionCalls = functionCallEvent.functionCalls(); |
245 | | - |
246 | | - for (FunctionCall functionCall : functionCalls) { |
247 | | - if (!tools.containsKey(functionCall.name().get())) { |
248 | | - throw new VerifyException("Tool not found: " + functionCall.name().get()); |
249 | | - } |
250 | | - } |
| 317 | + ToolCallSeparation separation = |
| 318 | + separateValidAndMissingToolCalls(invocationContext, functionCalls, tools); |
251 | 319 |
|
252 | 320 | Function<FunctionCall, Maybe<Event>> functionCallMapper = |
253 | 321 | functionCall -> { |
@@ -310,18 +378,14 @@ public static Maybe<Event> handleFunctionCallsLive( |
310 | 378 | }); |
311 | 379 | }; |
312 | 380 |
|
313 | | - Flowable<Event> responseEventsFlowable; |
314 | | - |
315 | | - if (invocationContext.runConfig().toolExecutionMode() == ToolExecutionMode.SEQUENTIAL) { |
316 | | - responseEventsFlowable = |
317 | | - Flowable.fromIterable(functionCalls).concatMapMaybe(functionCallMapper); |
318 | | - |
319 | | - } else { |
320 | | - responseEventsFlowable = |
321 | | - Flowable.fromIterable(functionCalls).flatMapMaybe(functionCallMapper); |
322 | | - } |
| 381 | + Flowable<Event> allEventsFlowable = |
| 382 | + createCombinedFlowable( |
| 383 | + invocationContext, |
| 384 | + separation.validCalls(), |
| 385 | + separation.missingToolsFlowable(), |
| 386 | + functionCallMapper); |
323 | 387 |
|
324 | | - return responseEventsFlowable |
| 388 | + return allEventsFlowable |
325 | 389 | .toList() |
326 | 390 | .flatMapMaybe( |
327 | 391 | events -> { |
|
0 commit comments