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::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct RenderTableParams {
#[serde(default)]
pub title: Option<String>,
pub columns: Vec<ColumnDef>,
pub data: Vec<HashMap<String, Value>>,
#[serde(flatten)]
pub protocol: LegacyProtocolOptions,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ColumnDef {
pub header: String,
pub accessor_key: String,
}
pub struct RenderTableTool;
impl RenderTableTool {
pub fn new() -> Self {
Self
}
}
impl Default for RenderTableTool {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Tool for RenderTableTool {
fn name(&self) -> &str {
"render_table"
}
fn description(&self) -> &str {
r#"Render a data table. Output example:
┌───────┬─────────────────────┬───────┐
│ Name │ Email │ Role │
├───────┼─────────────────────┼───────┤
│ Alice │ alice@example.com │ Admin │
│ Bob │ bob@example.com │ User │
└───────┴─────────────────────┴───────┘
Set sortable=true for clickable column headers. Set page_size for pagination. Set striped=true for alternating row colors."#
}
fn parameters_schema(&self) -> Option<Value> {
Some(super::generate_gemini_schema::<RenderTableParams>())
}
async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
let params: RenderTableParams = serde_json::from_value(args)
.map_err(|e| crate::compat::AdkError::tool(format!("Invalid parameters: {}", e)))?;
let protocol_options = params.protocol.clone();
let columns: Vec<TableColumn> = params
.columns
.into_iter()
.map(|c| TableColumn {
header: c.header,
accessor_key: c.accessor_key,
sortable: true,
})
.collect();
let mut components = Vec::new();
if let Some(title) = params.title {
components.push(Component::Text(Text {
id: None,
content: title,
variant: TextVariant::H3,
}));
}
components.push(Component::Table(Table {
id: None,
columns,
data: params.data,
sortable: false,
page_size: None,
striped: false,
}));
let ui = UiResponse::new(components);
render_ui_response_with_protocol(ui, &protocol_options, "table")
}
}