adk_ui/tools/
render_progress.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 RenderProgressParams {
12 pub title: String,
14 pub value: u8,
16 #[serde(default)]
18 pub description: Option<String>,
19 #[serde(default)]
21 pub steps: Option<Vec<ProgressStep>>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
25pub struct ProgressStep {
26 pub label: String,
28 #[serde(default)]
30 pub completed: bool,
31 #[serde(default)]
33 pub current: bool,
34}
35
36pub struct RenderProgressTool;
38
39impl RenderProgressTool {
40 pub fn new() -> Self {
41 Self
42 }
43}
44
45impl Default for RenderProgressTool {
46 fn default() -> Self {
47 Self::new()
48 }
49}
50
51#[async_trait]
52impl Tool for RenderProgressTool {
53 fn name(&self) -> &str {
54 "render_progress"
55 }
56
57 fn description(&self) -> &str {
58 "Render a progress indicator to show task completion status. Use this when performing multi-step operations or to show loading progress. Can show a progress bar with optional steps."
59 }
60
61 fn parameters_schema(&self) -> Option<Value> {
62 Some(super::generate_gemini_schema::<RenderProgressParams>())
63 }
64
65 async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
66 let params: RenderProgressParams = serde_json::from_value(args)
67 .map_err(|e| adk_core::AdkError::Tool(format!("Invalid parameters: {}", e)))?;
68
69 let mut components = Vec::new();
70
71 components.push(Component::Text(Text {
73 id: None,
74 content: params.title,
75 variant: TextVariant::H3,
76 }));
77
78 if let Some(desc) = params.description {
80 components.push(Component::Text(Text {
81 id: None,
82 content: desc,
83 variant: TextVariant::Caption,
84 }));
85 }
86
87 components.push(Component::Progress(Progress {
89 id: None,
90 value: params.value,
91 label: Some(format!("{}%", params.value)),
92 }));
93
94 if let Some(steps) = params.steps {
96 for step in steps {
97 let prefix = if step.completed {
98 "✅"
99 } else if step.current {
100 "⏳"
101 } else {
102 "⬜"
103 };
104 components.push(Component::Text(Text {
105 id: None,
106 content: format!("{} {}", prefix, step.label),
107 variant: TextVariant::Body,
108 }));
109 }
110 }
111
112 let ui = UiResponse::new(vec![Component::Card(Card {
113 id: None,
114 title: None,
115 description: None,
116 content: components,
117 footer: None,
118 })]);
119
120 serde_json::to_value(ui)
121 .map_err(|e| adk_core::AdkError::Tool(format!("Failed to serialize UI: {}", e)))
122 }
123}