use crate::compat::{Result, Tool, ToolContext};
use crate::schema::*;
use crate::tools::{LegacyProtocolOptions, render_ui_response_with_protocol};
use async_trait::async_trait;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct RenderProgressParams {
pub title: String,
pub value: u8,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub steps: Option<Vec<ProgressStep>>,
#[serde(flatten)]
pub protocol: LegacyProtocolOptions,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ProgressStep {
pub label: String,
#[serde(default)]
pub completed: bool,
#[serde(default)]
pub current: bool,
}
pub struct RenderProgressTool;
impl RenderProgressTool {
pub fn new() -> Self {
Self
}
}
impl Default for RenderProgressTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Tool for RenderProgressTool {
fn name(&self) -> &str {
"render_progress"
}
fn description(&self) -> &str {
"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."
}
fn parameters_schema(&self) -> Option<Value> {
Some(super::generate_gemini_schema::<RenderProgressParams>())
}
async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
let params: RenderProgressParams = serde_json::from_value(args)
.map_err(|e| crate::compat::AdkError::tool(format!("Invalid parameters: {}", e)))?;
let protocol_options = params.protocol.clone();
let mut components = Vec::new();
components.push(Component::Text(Text {
id: None,
content: params.title,
variant: TextVariant::H3,
}));
if let Some(desc) = params.description {
components.push(Component::Text(Text {
id: None,
content: desc,
variant: TextVariant::Caption,
}));
}
components.push(Component::Progress(Progress {
id: None,
value: params.value,
label: Some(format!("{}%", params.value)),
}));
if let Some(steps) = params.steps {
for step in steps {
let prefix = if step.completed {
"✅"
} else if step.current {
"⏳"
} else {
"⬜"
};
components.push(Component::Text(Text {
id: None,
content: format!("{} {}", prefix, step.label),
variant: TextVariant::Body,
}));
}
}
let ui = UiResponse::new(vec![Component::Card(Card {
id: None,
title: None,
description: None,
content: components,
footer: None,
})]);
render_ui_response_with_protocol(ui, &protocol_options, "progress")
}
}