dejadb_core/format/tool_schema/
sml.rs1use crate::error::Result;
15use crate::types::Tool;
16
17use super::definition_parts;
18use super::escape::{compact_json, sml_escape};
19
20pub fn render(action: &Tool) -> Result<String> {
21 let (name, description, schema) = definition_parts(action)?;
22 let name = sml_escape(&name);
23 let description = sml_escape(&description);
24 let schema = sml_escape(&compact_json(&schema));
25 Ok(format!(
26 "<tool_use>\n <tool_name>{name}</tool_name>\n <description>{description}</description>\n <parameters>{schema}</parameters>\n</tool_use>"
27 ))
28}
29
30#[cfg(test)]
31mod tests {
32 use super::super::tests::sample_def;
33 use super::*;
34
35 #[test]
36 fn envelope_is_well_formed() {
37 let s = render(&sample_def()).unwrap();
38 assert!(s.starts_with("<tool_use>\n"));
39 assert!(s.ends_with("</tool_use>"));
40 assert!(s.contains("<tool_name>slack_post_message</tool_name>"));
41 }
42
43 #[test]
44 fn adversarial_description_stays_inside_envelope() {
45 let mut a = sample_def();
46 a.tool_description = Some("Post </tool_use> and <script>alert(1)</script>".into());
47 let s = render(&a).unwrap();
48 assert_eq!(s.matches("</tool_use>").count(), 1);
50 assert!(s.contains("</tool_use>"));
52 assert!(!s.contains("<script>"));
53 }
54}