agent_chain_core/tools/
render.rs1use std::sync::Arc;
7
8use super::base::BaseTool;
9
10pub type ToolsRenderer = fn(&[Arc<dyn BaseTool>]) -> String;
12
13pub fn render_text_description(tools: &[Arc<dyn BaseTool>]) -> String {
21 let descriptions: Vec<String> = tools
22 .iter()
23 .map(|tool| format!("{} - {}", tool.name(), tool.description()))
24 .collect();
25
26 descriptions.join("\n")
27}
28
29pub fn render_text_description_and_args(tools: &[Arc<dyn BaseTool>]) -> String {
37 let tool_strings: Vec<String> = tools
38 .iter()
39 .map(|tool| {
40 let args_schema =
41 serde_json::to_string(&tool.args()).unwrap_or_else(|_| "{}".to_string());
42 format!(
43 "{} - {}, args: {}",
44 tool.name(),
45 tool.description(),
46 args_schema
47 )
48 })
49 .collect();
50
51 tool_strings.join("\n")
52}
53
54pub fn render_json(tools: &[Arc<dyn BaseTool>]) -> String {
56 let definitions: Vec<_> = tools.iter().map(|t| t.definition()).collect();
57 serde_json::to_string_pretty(&definitions).unwrap_or_else(|_| "[]".to_string())
58}
59
60pub fn render_json_compact(tools: &[Arc<dyn BaseTool>]) -> String {
62 let definitions: Vec<_> = tools.iter().map(|t| t.definition()).collect();
63 serde_json::to_string(&definitions).unwrap_or_else(|_| "[]".to_string())
64}
65
66pub fn render_tool(tool: &dyn BaseTool) -> String {
68 format!(
69 "Tool: {}\nDescription: {}\nArguments: {}",
70 tool.name(),
71 tool.description(),
72 serde_json::to_string_pretty(&tool.args()).unwrap_or_else(|_| "{}".to_string())
73 )
74}
75
76pub fn render_for_prompt(tools: &[Arc<dyn BaseTool>]) -> String {
78 let mut output = String::from("Available tools:\n\n");
79
80 for (i, tool) in tools.iter().enumerate() {
81 output.push_str(&format!("{}. {}\n", i + 1, tool.name()));
82 output.push_str(&format!(" Description: {}\n", tool.description()));
83
84 let args = tool.args();
85 if !args.is_empty() {
86 output.push_str(" Arguments:\n");
87 for (name, schema) in args {
88 let type_str = schema.get("type").and_then(|t| t.as_str()).unwrap_or("any");
89 let desc = schema
90 .get("description")
91 .and_then(|d| d.as_str())
92 .unwrap_or("");
93 output.push_str(&format!(" - {} ({}): {}\n", name, type_str, desc));
94 }
95 }
96 output.push('\n');
97 }
98
99 output
100}
101
102pub fn render_numbered_list(tools: &[Arc<dyn BaseTool>]) -> String {
104 tools
105 .iter()
106 .enumerate()
107 .map(|(i, tool)| format!("{}. {} - {}", i + 1, tool.name(), tool.description()))
108 .collect::<Vec<_>>()
109 .join("\n")
110}
111
112pub fn render_with_schemas(tools: &[Arc<dyn BaseTool>]) -> String {
114 let mut output = String::new();
115
116 for tool in tools {
117 output.push_str(&format!("## {}\n\n", tool.name()));
118 output.push_str(&format!("{}\n\n", tool.description()));
119 output.push_str("### Schema\n\n");
120 output.push_str("```json\n");
121 output.push_str(&serde_json::to_string_pretty(&tool.definition()).unwrap_or_default());
122 output.push_str("\n```\n\n");
123 }
124
125 output
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::tools::simple::Tool;
132
133 fn create_test_tools() -> Vec<Arc<dyn BaseTool>> {
134 vec![
135 Arc::new(Tool::from_function(
136 |input| Ok(format!("Searched: {}", input)),
137 "search",
138 "Search for information",
139 )) as Arc<dyn BaseTool>,
140 Arc::new(Tool::from_function(
141 |input| Ok(format!("Calculated: {}", input)),
142 "calculator",
143 "Perform calculations",
144 )) as Arc<dyn BaseTool>,
145 ]
146 }
147
148 #[test]
149 fn test_render_text_description() {
150 let tools = create_test_tools();
151 let rendered = render_text_description(&tools);
152
153 assert!(rendered.contains("search - Search for information"));
154 assert!(rendered.contains("calculator - Perform calculations"));
155 }
156
157 #[test]
158 fn test_render_text_description_and_args() {
159 let tools = create_test_tools();
160 let rendered = render_text_description_and_args(&tools);
161
162 assert!(rendered.contains("search -"));
163 assert!(rendered.contains("args:"));
164 }
165
166 #[test]
167 fn test_render_json() {
168 let tools = create_test_tools();
169 let rendered = render_json(&tools);
170
171 assert!(rendered.contains("\"name\": \"search\""));
172 assert!(rendered.contains("\"name\": \"calculator\""));
173 }
174
175 #[test]
176 fn test_render_for_prompt() {
177 let tools = create_test_tools();
178 let rendered = render_for_prompt(&tools);
179
180 assert!(rendered.contains("Available tools:"));
181 assert!(rendered.contains("1. search"));
182 assert!(rendered.contains("2. calculator"));
183 }
184
185 #[test]
186 fn test_render_numbered_list() {
187 let tools = create_test_tools();
188 let rendered = render_numbered_list(&tools);
189
190 assert!(rendered.starts_with("1."));
191 assert!(rendered.contains("2. calculator"));
192 }
193
194 #[test]
195 fn test_render_tool() {
196 let tool = Tool::from_function(Ok, "test", "A test tool");
197
198 let rendered = render_tool(&tool);
199
200 assert!(rendered.contains("Tool: test"));
201 assert!(rendered.contains("Description: A test tool"));
202 }
203}