gemini_adk_rs/tools/
transfer_to_agent.rs1use async_trait::async_trait;
7
8use crate::error::ToolError;
9use crate::tool::ToolFunction;
10
11#[derive(Debug, Clone)]
16pub struct TransferToAgentTool {
17 agent_names: Vec<String>,
19}
20
21impl TransferToAgentTool {
22 pub fn new(agent_names: Vec<String>) -> Self {
24 Self { agent_names }
25 }
26
27 pub fn agent_names(&self) -> &[String] {
29 &self.agent_names
30 }
31}
32
33#[async_trait]
34impl ToolFunction for TransferToAgentTool {
35 fn name(&self) -> &str {
36 "transfer_to_agent"
37 }
38
39 fn description(&self) -> &str {
40 "Transfer the question to another agent. Use this tool to hand off control \
41 to a more suitable agent based on their description."
42 }
43
44 fn parameters(&self) -> Option<serde_json::Value> {
45 Some(serde_json::json!({
46 "type": "object",
47 "properties": {
48 "agent_name": {
49 "type": "string",
50 "description": "The name of the agent to transfer to.",
51 "enum": self.agent_names
52 }
53 },
54 "required": ["agent_name"]
55 }))
56 }
57
58 async fn call(&self, args: serde_json::Value) -> Result<serde_json::Value, ToolError> {
59 let agent_name = args
60 .get("agent_name")
61 .and_then(|v| v.as_str())
62 .ok_or_else(|| ToolError::InvalidArgs("Missing agent_name".into()))?;
63
64 if !self.agent_names.iter().any(|n| n == agent_name) {
65 return Err(ToolError::InvalidArgs(format!(
66 "Invalid agent name '{}'. Valid agents: {:?}",
67 agent_name, self.agent_names
68 )));
69 }
70
71 Ok(serde_json::json!({
74 "status": "transferred",
75 "agent_name": agent_name
76 }))
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use serde_json::json;
84
85 #[test]
86 fn parameters_include_enum() {
87 let tool = TransferToAgentTool::new(vec!["agent_a".into(), "agent_b".into()]);
88 let params = tool.parameters().unwrap();
89 let enums = params["properties"]["agent_name"]["enum"]
90 .as_array()
91 .unwrap();
92 assert_eq!(enums.len(), 2);
93 assert_eq!(enums[0], "agent_a");
94 assert_eq!(enums[1], "agent_b");
95 }
96
97 #[tokio::test]
98 async fn valid_transfer() {
99 let tool = TransferToAgentTool::new(vec!["support".into(), "billing".into()]);
100 let result = tool.call(json!({"agent_name": "support"})).await.unwrap();
101 assert_eq!(result["status"], "transferred");
102 assert_eq!(result["agent_name"], "support");
103 }
104
105 #[tokio::test]
106 async fn invalid_agent_name() {
107 let tool = TransferToAgentTool::new(vec!["support".into()]);
108 let result = tool.call(json!({"agent_name": "hacker"})).await;
109 assert!(result.is_err());
110 }
111
112 #[tokio::test]
113 async fn missing_agent_name() {
114 let tool = TransferToAgentTool::new(vec!["support".into()]);
115 let result = tool.call(json!({})).await;
116 assert!(result.is_err());
117 }
118
119 #[test]
120 fn agent_names_accessor() {
121 let tool = TransferToAgentTool::new(vec!["a".into(), "b".into()]);
122 assert_eq!(tool.agent_names(), &["a", "b"]);
123 }
124}