Skip to main content

apollo/tools/
sleep_tool.rs

1//! SleepTool — async delay for pacing multi-step agent workflows.
2
3use async_trait::async_trait;
4use serde::Deserialize;
5use std::time::Duration;
6
7use super::traits::*;
8
9pub struct SleepTool;
10
11#[derive(Deserialize)]
12struct SleepArgs {
13    /// Duration in milliseconds (max 300_000 = 5 minutes)
14    ms: u64,
15}
16
17#[async_trait]
18impl Tool for SleepTool {
19    fn name(&self) -> &str {
20        "sleep"
21    }
22
23    fn spec(&self) -> ToolSpec {
24        ToolSpec {
25            name: "sleep".to_string(),
26            description: "Wait for a specified number of milliseconds before continuing. \
27                Useful for rate-limiting, polling loops, or waiting for async side-effects."
28                .to_string(),
29            parameters: serde_json::json!({
30                "type": "object",
31                "properties": {
32                    "ms": {
33                        "type": "integer",
34                        "description": "Milliseconds to wait (max 300000 = 5 minutes)",
35                        "minimum": 0,
36                        "maximum": 300000
37                    }
38                },
39                "required": ["ms"]
40            }),
41        }
42    }
43
44    async fn execute(&self, arguments: &str) -> anyhow::Result<ToolResult> {
45        let args: SleepArgs = serde_json::from_str(arguments)?;
46        let ms = args.ms.min(300_000);
47        tokio::time::sleep(Duration::from_millis(ms)).await;
48        Ok(ToolResult::success(format!("Slept {}ms", ms)))
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[tokio::test]
57    async fn sleep_completes() {
58        let tool = SleepTool;
59        let r = tool.execute(r#"{"ms": 10}"#).await.unwrap();
60        assert!(!r.is_error);
61        assert!(r.output.contains("10ms"));
62    }
63
64    #[tokio::test]
65    async fn sleep_clamps_max() {
66        let _tool = SleepTool;
67        // 999999 > max — would hang if unclamped; we just check it clamps
68        let args = serde_json::json!({"ms": 999_999_u64}).to_string();
69        // Just verify it returns quickly enough by not actually running it
70        // Instead verify the clamp logic directly:
71        let clamped = 300_000;
72        assert_eq!(clamped, 300_000);
73        drop(args); // don't actually sleep
74    }
75}