spec_ai_core/tools/builtin/
echo.rs1use crate::tools::{Tool, ToolResult};
2use anyhow::{Context, Result};
3use async_trait::async_trait;
4use serde::Deserialize;
5use serde_json::Value;
6
7pub struct EchoTool;
9
10#[derive(Debug, Deserialize)]
11struct EchoArgs {
12 message: String,
13}
14
15impl EchoTool {
16 pub fn new() -> Self {
17 Self
18 }
19}
20
21impl Default for EchoTool {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27#[async_trait]
28impl Tool for EchoTool {
29 fn name(&self) -> &str {
30 "echo"
31 }
32
33 fn description(&self) -> &str {
34 "Echoes back the provided message"
35 }
36
37 fn parameters(&self) -> Value {
38 serde_json::json!({
39 "type": "object",
40 "properties": {
41 "message": {
42 "type": "string",
43 "description": "The message to echo back"
44 }
45 },
46 "required": ["message"]
47 })
48 }
49
50 async fn execute(&self, args: Value) -> Result<ToolResult> {
51 let echo_args: EchoArgs =
52 serde_json::from_value(args).context("Failed to parse echo arguments")?;
53
54 Ok(ToolResult::success(echo_args.message))
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[tokio::test]
63 async fn test_echo_tool_basic() {
64 let tool = EchoTool::new();
65
66 assert_eq!(tool.name(), "echo");
67 assert!(!tool.description().is_empty());
68 }
69
70 #[tokio::test]
71 async fn test_echo_tool_parameters() {
72 let tool = EchoTool::new();
73 let params = tool.parameters();
74
75 assert!(params.is_object());
76 assert!(params["properties"]["message"].is_object());
77 assert_eq!(params["required"][0], "message");
78 }
79
80 #[tokio::test]
81 async fn test_echo_tool_execute() {
82 let tool = EchoTool::new();
83 let args = serde_json::json!({
84 "message": "Hello, world!"
85 });
86
87 let result = tool.execute(args).await.unwrap();
88
89 assert!(result.success);
90 assert_eq!(result.output, "Hello, world!");
91 assert!(result.error.is_none());
92 }
93
94 #[tokio::test]
95 async fn test_echo_tool_execute_empty_message() {
96 let tool = EchoTool::new();
97 let args = serde_json::json!({
98 "message": ""
99 });
100
101 let result = tool.execute(args).await.unwrap();
102
103 assert!(result.success);
104 assert_eq!(result.output, "");
105 }
106
107 #[tokio::test]
108 async fn test_echo_tool_execute_missing_argument() {
109 let tool = EchoTool::new();
110 let args = serde_json::json!({});
111
112 let result = tool.execute(args).await;
113
114 assert!(result.is_err());
115 }
116
117 #[tokio::test]
118 async fn test_echo_tool_execute_invalid_type() {
119 let tool = EchoTool::new();
120 let args = serde_json::json!({
121 "message": 123
122 });
123
124 let result = tool.execute(args).await;
125
126 assert!(result.is_err());
127 }
128}