adk_ui/tools/
render_form.rs1use 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#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
11pub struct RenderFormParams {
12 pub title: String,
14 #[serde(default)]
16 pub description: Option<String>,
17 pub fields: Vec<FormField>,
19 #[serde(default = "default_submit_action")]
21 pub submit_action: String,
22 #[serde(default = "default_submit_label")]
24 pub submit_label: String,
25 #[serde(default)]
27 pub theme: Option<String>,
28}
29
30fn default_submit_action() -> String {
31 "form_submit".to_string()
32}
33
34fn default_submit_label() -> String {
35 "Submit".to_string()
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
39pub struct FormField {
40 pub name: String,
42 pub label: String,
44 #[serde(rename = "type", default = "default_field_type")]
46 pub field_type: String,
47 #[serde(default)]
49 pub placeholder: Option<String>,
50 #[serde(default)]
52 pub required: bool,
53 #[serde(default)]
55 pub options: Vec<SelectOption>,
56}
57
58fn default_field_type() -> String {
59 "text".to_string()
60}
61
62pub struct RenderFormTool;
91
92impl RenderFormTool {
93 pub fn new() -> Self {
94 Self
95 }
96}
97
98impl Default for RenderFormTool {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104#[async_trait]
105impl Tool for RenderFormTool {
106 fn name(&self) -> &str {
107 "render_form"
108 }
109
110 fn description(&self) -> &str {
111 r#"Render a form to collect user input. Output example:
112┌─────────────────────────┐
113│ Registration Form │
114│ ─────────────────────── │
115│ Name*: [___________] │
116│ Email*: [___________] │
117│ Password*: [___________]│
118│ [Register] │
119└─────────────────────────┘
120Use field types: text, email, password, number, select, textarea. Set required=true for mandatory fields."#
121 }
122
123 fn parameters_schema(&self) -> Option<Value> {
124 Some(super::generate_gemini_schema::<RenderFormParams>())
125 }
126
127 async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
128 let params: RenderFormParams = serde_json::from_value(args)
129 .map_err(|e| adk_core::AdkError::Tool(format!("Invalid parameters: {}", e)))?;
130
131 let mut form_content: Vec<Component> = Vec::new();
133
134 for field in params.fields {
135 let component = match field.field_type.as_str() {
136 "number" => Component::NumberInput(NumberInput {
137 id: None,
138 name: field.name,
139 label: field.label,
140 min: None,
141 max: None,
142 step: None,
143 required: field.required,
144 default_value: None,
145 error: None,
146 }),
147 "select" => Component::Select(Select {
148 id: None,
149 name: field.name,
150 label: field.label,
151 options: field.options,
152 required: field.required,
153 error: None,
154 }),
155 "textarea" => Component::Textarea(Textarea {
156 id: None,
157 name: field.name,
158 label: field.label,
159 placeholder: field.placeholder,
160 rows: 4,
161 required: field.required,
162 default_value: None,
163 error: None,
164 }),
165 _ => Component::TextInput(TextInput {
166 id: None,
167 name: field.name,
168 label: field.label,
169 input_type: field.field_type.clone(),
170 placeholder: field.placeholder,
171 required: field.required,
172 default_value: None,
173 min_length: None,
174 max_length: None,
175 error: None,
176 }),
177 };
178 form_content.push(component);
179 }
180
181 form_content.push(Component::Button(Button {
183 id: None,
184 label: params.submit_label,
185 action_id: params.submit_action,
186 variant: ButtonVariant::Primary,
187 disabled: false,
188 icon: None,
189 }));
190
191 let mut ui = UiResponse::new(vec![Component::Card(Card {
193 id: None,
194 title: Some(params.title),
195 description: params.description,
196 content: form_content,
197 footer: None,
198 })]);
199
200 if let Some(theme_str) = params.theme {
202 let theme = match theme_str.to_lowercase().as_str() {
203 "dark" => Theme::Dark,
204 "system" => Theme::System,
205 _ => Theme::Light,
206 };
207 ui = ui.with_theme(theme);
208 }
209
210 serde_json::to_value(ui)
212 .map_err(|e| adk_core::AdkError::Tool(format!("Failed to serialize UI: {}", e)))
213 }
214}