adk_ui/tools/
render_progress.rs1use crate::schema::*;
2use crate::tools::{LegacyProtocolOptions, render_ui_response_with_protocol};
3use adk_core::{Result, Tool, ToolContext};
4use async_trait::async_trait;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
12pub struct RenderProgressParams {
13 pub title: String,
15 pub value: u8,
17 #[serde(default)]
19 pub description: Option<String>,
20 #[serde(default)]
22 pub steps: Option<Vec<ProgressStep>>,
23 #[serde(flatten)]
25 pub protocol: LegacyProtocolOptions,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
29pub struct ProgressStep {
30 pub label: String,
32 #[serde(default)]
34 pub completed: bool,
35 #[serde(default)]
37 pub current: bool,
38}
39
40pub struct RenderProgressTool;
42
43impl RenderProgressTool {
44 pub fn new() -> Self {
45 Self
46 }
47}
48
49impl Default for RenderProgressTool {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55#[async_trait]
56impl Tool for RenderProgressTool {
57 fn name(&self) -> &str {
58 "render_progress"
59 }
60
61 fn description(&self) -> &str {
62 "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."
63 }
64
65 fn parameters_schema(&self) -> Option<Value> {
66 Some(super::generate_gemini_schema::<RenderProgressParams>())
67 }
68
69 async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
70 let params: RenderProgressParams = serde_json::from_value(args)
71 .map_err(|e| adk_core::AdkError::Tool(format!("Invalid parameters: {}", e)))?;
72 let protocol_options = params.protocol.clone();
73
74 let mut components = Vec::new();
75
76 components.push(Component::Text(Text {
78 id: None,
79 content: params.title,
80 variant: TextVariant::H3,
81 }));
82
83 if let Some(desc) = params.description {
85 components.push(Component::Text(Text {
86 id: None,
87 content: desc,
88 variant: TextVariant::Caption,
89 }));
90 }
91
92 components.push(Component::Progress(Progress {
94 id: None,
95 value: params.value,
96 label: Some(format!("{}%", params.value)),
97 }));
98
99 if let Some(steps) = params.steps {
101 for step in steps {
102 let prefix = if step.completed {
103 "✅"
104 } else if step.current {
105 "⏳"
106 } else {
107 "⬜"
108 };
109 components.push(Component::Text(Text {
110 id: None,
111 content: format!("{} {}", prefix, step.label),
112 variant: TextVariant::Body,
113 }));
114 }
115 }
116
117 let ui = UiResponse::new(vec![Component::Card(Card {
118 id: None,
119 title: None,
120 description: None,
121 content: components,
122 footer: None,
123 })]);
124
125 render_ui_response_with_protocol(ui, &protocol_options, "progress")
126 }
127}