gemini_adk_rs/tools/
get_user_choice.rs

1//! Get user choice tool — presents options to the user for selection.
2//!
3//! Mirrors ADK-Python's `get_user_choice_tool`. Wraps around the
4//! long-running tool mechanism to pause execution until the user responds.
5
6use async_trait::async_trait;
7
8use crate::error::ToolError;
9use crate::tool::ToolFunction;
10
11/// Tool that presents a list of options to the user and waits for selection.
12///
13/// This is a long-running tool — the model calls it to present choices,
14/// and execution pauses until the user makes a selection.
15#[derive(Debug, Clone, Default)]
16pub struct GetUserChoiceTool;
17
18impl GetUserChoiceTool {
19    /// Create a new get user choice tool.
20    pub fn new() -> Self {
21        Self
22    }
23}
24
25#[async_trait]
26impl ToolFunction for GetUserChoiceTool {
27    fn name(&self) -> &str {
28        "get_user_choice"
29    }
30
31    fn description(&self) -> &str {
32        "Provides a list of options to the user and asks them to choose one. \
33         Use this when you need user input to decide between multiple options."
34    }
35
36    fn parameters(&self) -> Option<serde_json::Value> {
37        Some(serde_json::json!({
38            "type": "object",
39            "properties": {
40                "options": {
41                    "type": "array",
42                    "items": { "type": "string" },
43                    "description": "The list of options to present to the user."
44                }
45            },
46            "required": ["options"]
47        }))
48    }
49
50    async fn call(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
51        let options = args
52            .get("options")
53            .and_then(|v| v.as_array())
54            .ok_or_else(|| ToolError::InvalidArgs("Missing options array".into()))?;
55
56        // In a real integration, the runtime would intercept this and
57        // present the options to the user via the UI.
58        Ok(serde_json::json!({
59            "status": "awaiting_user_choice",
60            "options": options,
61            "message": "Waiting for user to select an option."
62        }))
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use serde_json::json;
70
71    #[test]
72    fn tool_metadata() {
73        let tool = GetUserChoiceTool::new();
74        assert_eq!(tool.name(), "get_user_choice");
75        assert!(tool.parameters().is_some());
76    }
77
78    #[tokio::test]
79    async fn call_with_options() {
80        let tool = GetUserChoiceTool::new();
81        let result = tool
82            .call(json!({"options": ["Option A", "Option B", "Option C"]}))
83            .await
84            .unwrap();
85        assert_eq!(result["status"], "awaiting_user_choice");
86        assert_eq!(result["options"].as_array().unwrap().len(), 3);
87    }
88
89    #[tokio::test]
90    async fn missing_options_error() {
91        let tool = GetUserChoiceTool::new();
92        let result = tool.call(json!({})).await;
93        assert!(result.is_err());
94    }
95}