diff --git a/docs/tools-custom/confirmation.md b/docs/tools-custom/confirmation.md index e69b6cf33d..ccd771515e 100644 --- a/docs/tools-custom/confirmation.md +++ b/docs/tools-custom/confirmation.md @@ -1,7 +1,7 @@ # Get action confirmation for ADK Tools
- Supported in ADKPython v1.14.0TypeScript v0.2.0Go v0.3.0Experimental + Supported in ADKPython v1.14.0TypeScript v0.2.0Go v0.3.0Kotlin v0.1.0Experimental
Some agent workflows require confirmation for decision making, verification, @@ -49,9 +49,10 @@ agent pattern. When your tool only requires a simple `yes` or `no` from the user, you can append a confirmation step. In Python, Go, and Java, you can enable this by wrapping the tool with the `FunctionTool` class and setting the -`require_confirmation` parameter (or equivalent) to `True`. In TypeScript, you -implement this logic manually within the `execute` function using the -`ToolContext`. +`require_confirmation` parameter (or equivalent) to `True`. In Kotlin, you set +`requireConfirmation = true` on the tool function's `@Tool` annotation. In +TypeScript, you implement this logic manually within the `execute` function +using the `ToolContext`. The following examples show how to enable boolean confirmation: @@ -118,9 +119,15 @@ The following examples show how to enable boolean confirmation: .build(); ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/tools/confirmation/ToolConfirmationExample.kt:boolean_confirmation" + ``` + ### Require confirmation function -You can modify the behavior of the confirmation requirement by using a function that returns a boolean response based on the tool's input. In TypeScript, this is handled by adding conditional logic to your `execute` function. +You can modify the behavior of the confirmation requirement by using a function that returns a boolean response based on the tool's input. In TypeScript, this is handled by adding conditional logic to your `execute` function. In Kotlin, the `@Tool` annotation's flag is a compile-time constant, so the conditional logic goes inside the tool function. === "Python" @@ -198,6 +205,17 @@ You can modify the behavior of the confirmation requirement by using a function .build(); ``` +=== "Kotlin" + + !!! note + The `@Tool` annotation's `requireConfirmation` flag is a compile-time + constant, so a threshold is evaluated inside the tool using the + `ToolContext`, as in ADK Java. + + ```kotlin + --8<-- "examples/kotlin/snippets/tools/confirmation/dynamic/ReimbursementTools.kt:dynamic_confirmation" + ``` + ## Advanced confirmation {#advanced-confirmation} When a tool confirmation requires more details for the user or a more complex @@ -333,6 +351,12 @@ time off requests for an employee: } ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/tools/confirmation/ToolConfirmationExample.kt:advanced_confirmation" + ``` + ## Remote confirmation with REST API {#remote-response} If there is no active user interface for a human confirmation of an agent diff --git a/examples/kotlin/snippets/tools/confirmation/ToolConfirmationExample.kt b/examples/kotlin/snippets/tools/confirmation/ToolConfirmationExample.kt new file mode 100644 index 0000000000..26395b1841 --- /dev/null +++ b/examples/kotlin/snippets/tools/confirmation/ToolConfirmationExample.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.adk.kt.examples.tools.confirmation + +import com.google.adk.kt.agents.LlmAgent +import com.google.adk.kt.annotations.Param +import com.google.adk.kt.annotations.Tool +import com.google.adk.kt.models.Gemini +import com.google.adk.kt.tools.ToolContext + +// --8<-- [start:boolean_confirmation] +class ReimbursementTools { + /** Reimburse an amount. */ + @Tool(requireConfirmation = true) // Pause for user confirmation before every call. + fun reimburse( + @Param("The amount to reimburse.") amount: Int, + ): Map = mapOf("status" to "ok", "reimbursedAmount" to amount) +} + +val reimbursementAgent = + LlmAgent( + name = "reimbursement_agent", + model = Gemini(name = "gemini-flash-latest"), + tools = ReimbursementTools().generatedTools(), + ) +// --8<-- [end:boolean_confirmation] + +// --8<-- [start:advanced_confirmation] +class TimeOffTools { + /** Request day off for the employee. */ + @Tool + fun requestTimeOff( + context: ToolContext, + @Param("The number of days requested.") days: Int, + ): Map { + val confirmation = context.toolConfirmation + if (confirmation == null) { + context.requestConfirmation( + hint = + "Please approve or reject the tool call requestTimeOff() by responding " + + "with a FunctionResponse with an expected ToolConfirmation payload.", + payload = mapOf("approved_days" to 0), + ) + // Return an intermediate status indicating that the tool is waiting for + // a confirmation response: + return mapOf("status" to "Manager approval is required.") + } + + // The payload comes back decoded from JSON, so the number may arrive as any + // Number subtype. Read it through Number rather than casting straight to Int. + val payload = confirmation.payload as? Map<*, *> + val approvedDays = + minOf((payload?.get("approved_days") as? Number)?.toInt() ?: 0, days) + if (approvedDays == 0) { + return mapOf("status" to "The time off request is rejected.", "approved_days" to 0) + } + return mapOf("status" to "ok", "approved_days" to approvedDays) + } +} +// --8<-- [end:advanced_confirmation] diff --git a/examples/kotlin/snippets/tools/confirmation/dynamic/ReimbursementTools.kt b/examples/kotlin/snippets/tools/confirmation/dynamic/ReimbursementTools.kt new file mode 100644 index 0000000000..62da167685 --- /dev/null +++ b/examples/kotlin/snippets/tools/confirmation/dynamic/ReimbursementTools.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.adk.kt.examples.tools.confirmation.dynamic + +import com.google.adk.kt.annotations.Param +import com.google.adk.kt.annotations.Tool +import com.google.adk.kt.tools.ToolContext + +// --8<-- [start:dynamic_confirmation] +class ReimbursementTools { + /** Reimburse an amount, requiring manager approval above a threshold. */ + @Tool + fun reimburse( + context: ToolContext, + @Param("The amount to reimburse.") amount: Int, + ): Map { + // The @Tool annotation's requireConfirmation flag is a compile-time constant, + // so the threshold is evaluated here using the ToolContext instead. + if (amount > 1000) { + val confirmation = context.toolConfirmation + if (confirmation == null) { + context.requestConfirmation(hint = "Amount > 1000 requires approval.") + // Return an intermediate status while the confirmation is pending. + return mapOf("status" to "Pending manager approval.") + } + if (!confirmation.confirmed) { + return mapOf("status" to "Reimbursement rejected.") + } + } + return mapOf("status" to "ok", "reimbursedAmount" to amount) + } +} +// --8<-- [end:dynamic_confirmation] diff --git a/tools/kotlin-snippets/files_to_test.txt b/tools/kotlin-snippets/files_to_test.txt index b1902c362c..b63293f4d1 100644 --- a/tools/kotlin-snippets/files_to_test.txt +++ b/tools/kotlin-snippets/files_to_test.txt @@ -40,3 +40,5 @@ snippets/tools/overview/UserPreferenceTools.kt snippets/tools/overview/CustomerSupport.kt snippets/tools/overview/DocAnalysisTools.kt snippets/tools/overview/OrderTools.kt +snippets/tools/confirmation/ToolConfirmationExample.kt +snippets/tools/confirmation/dynamic/ReimbursementTools.kt