adk_ui/tools/
render_confirm.rs

1use crate::schema::*;
2use adk_core::{Result, Tool, ToolContext};
3use async_trait::async_trait;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::sync::Arc;
8
9/// Parameters for the render_confirm tool
10#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
11pub struct RenderConfirmParams {
12    /// Confirmation title
13    pub title: String,
14    /// Message explaining what the user is confirming
15    pub message: String,
16    /// Action ID triggered when user confirms
17    pub confirm_action: String,
18    /// Optional cancel action ID (defaults to dismissing)
19    #[serde(default)]
20    pub cancel_action: Option<String>,
21    /// Confirm button label
22    #[serde(default = "default_confirm_label")]
23    pub confirm_label: String,
24    /// Cancel button label
25    #[serde(default = "default_cancel_label")]
26    pub cancel_label: String,
27    /// Whether this is a destructive action (shows danger button)
28    #[serde(default)]
29    pub destructive: bool,
30}
31
32fn default_confirm_label() -> String {
33    "Confirm".to_string()
34}
35
36fn default_cancel_label() -> String {
37    "Cancel".to_string()
38}
39
40/// Tool for rendering confirmation dialogs
41pub struct RenderConfirmTool;
42
43impl RenderConfirmTool {
44    pub fn new() -> Self {
45        Self
46    }
47}
48
49impl Default for RenderConfirmTool {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55#[async_trait]
56impl Tool for RenderConfirmTool {
57    fn name(&self) -> &str {
58        "render_confirm"
59    }
60
61    fn description(&self) -> &str {
62        "Render a confirmation dialog to get user approval before proceeding. Use this for destructive actions, important decisions, or when you need explicit user consent."
63    }
64
65    fn parameters_schema(&self) -> Option<Value> {
66        Some(super::generate_gemini_schema::<RenderConfirmParams>())
67    }
68
69    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
70        let params: RenderConfirmParams = serde_json::from_value(args)
71            .map_err(|e| adk_core::AdkError::Tool(format!("Invalid parameters: {}", e)))?;
72
73        let confirm_variant =
74            if params.destructive { ButtonVariant::Danger } else { ButtonVariant::Primary };
75
76        let footer = vec![
77            Component::Button(Button {
78                id: None,
79                label: params.cancel_label,
80                action_id: params.cancel_action.unwrap_or_else(|| "cancel".to_string()),
81                variant: ButtonVariant::Ghost,
82                disabled: false,
83                icon: None,
84            }),
85            Component::Button(Button {
86                id: None,
87                label: params.confirm_label,
88                action_id: params.confirm_action,
89                variant: confirm_variant,
90                disabled: false,
91                icon: None,
92            }),
93        ];
94
95        let ui = UiResponse::new(vec![Component::Card(Card {
96            id: None,
97            title: Some(params.title),
98            description: None,
99            content: vec![Component::Text(Text {
100                id: None,
101                content: params.message,
102                variant: TextVariant::Body,
103            })],
104            footer: Some(footer),
105        })]);
106
107        serde_json::to_value(ui)
108            .map_err(|e| adk_core::AdkError::Tool(format!("Failed to serialize UI: {}", e)))
109    }
110}