adk_ui/tools/
render_card.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_card tool
10#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
11pub struct RenderCardParams {
12    /// Title of the card
13    pub title: String,
14    /// Optional description/subtitle
15    #[serde(default)]
16    pub description: Option<String>,
17    /// Main content text (supports markdown-like formatting)
18    pub content: String,
19    /// Optional action buttons
20    #[serde(default)]
21    pub actions: Vec<CardAction>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
25pub struct CardAction {
26    /// Button label
27    pub label: String,
28    /// Action ID triggered when clicked
29    pub action_id: String,
30    /// Button variant: primary, secondary, danger, ghost
31    #[serde(default = "default_variant")]
32    pub variant: String,
33}
34
35fn default_variant() -> String {
36    "primary".to_string()
37}
38
39/// Tool for rendering information cards.
40///
41/// Creates styled card components to display content with optional action buttons.
42/// Cards are ideal for status updates, summaries, or any structured information.
43///
44/// # Example JSON Parameters
45///
46/// ```json
47/// {
48///   "title": "Welcome",
49///   "description": "Getting started with your account",
50///   "content": "Your account has been created successfully. Click below to continue.",
51///   "actions": [
52///     { "label": "Get Started", "action_id": "start", "variant": "primary" }
53///   ]
54/// }
55/// ```
56pub struct RenderCardTool;
57
58impl RenderCardTool {
59    pub fn new() -> Self {
60        Self
61    }
62}
63
64impl Default for RenderCardTool {
65    fn default() -> Self {
66        Self::new()
67    }
68}
69
70#[async_trait]
71impl Tool for RenderCardTool {
72    fn name(&self) -> &str {
73        "render_card"
74    }
75
76    fn description(&self) -> &str {
77        r#"Render an information card. Output example:
78┌─────────────────────────────┐
79│ Welcome                     │
80│ Your account is ready       │
81│ ─────────────────────────── │
82│ Click below to get started. │
83│      [Get Started]          │
84└─────────────────────────────┘
85Use for status updates, summaries, or any structured info with optional action buttons."#
86    }
87
88    fn parameters_schema(&self) -> Option<Value> {
89        Some(super::generate_gemini_schema::<RenderCardParams>())
90    }
91
92    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
93        let params: RenderCardParams = serde_json::from_value(args)
94            .map_err(|e| adk_core::AdkError::Tool(format!("Invalid parameters: {}", e)))?;
95
96        // Build card content
97        let content = vec![Component::Text(Text {
98            id: None,
99            content: params.content,
100            variant: TextVariant::Body,
101        })];
102
103        // Build footer with action buttons
104        let footer = if params.actions.is_empty() {
105            None
106        } else {
107            Some(
108                params
109                    .actions
110                    .into_iter()
111                    .map(|action| {
112                        let variant = match action.variant.as_str() {
113                            "secondary" => ButtonVariant::Secondary,
114                            "danger" => ButtonVariant::Danger,
115                            "ghost" => ButtonVariant::Ghost,
116                            "outline" => ButtonVariant::Outline,
117                            _ => ButtonVariant::Primary,
118                        };
119                        Component::Button(Button {
120                            id: None,
121                            label: action.label,
122                            action_id: action.action_id,
123                            variant,
124                            disabled: false,
125                            icon: None,
126                        })
127                    })
128                    .collect(),
129            )
130        };
131
132        let ui = UiResponse::new(vec![Component::Card(Card {
133            id: None,
134            title: Some(params.title),
135            description: params.description,
136            content,
137            footer,
138        })]);
139
140        serde_json::to_value(ui)
141            .map_err(|e| adk_core::AdkError::Tool(format!("Failed to serialize UI: {}", e)))
142    }
143}