Skip to main content

dejadb_core/format/tool_schema/
markdown.rs

1//! Markdown-with-fenced-JSON adapter.
2//!
3//! Shape:
4//!   ## {name}
5//!
6//!   {description}
7//!
8//!   **Input schema:**
9//!   ```json
10//!   {schema}
11//!   ```
12//!
13//! Fenced JSON is the most LLM-obedient catalog format per the Phase-1
14//! research. Triple-backtick collision falls back to `~~~` fences.
15
16use crate::error::Result;
17use crate::types::Tool;
18
19use super::definition_parts;
20use super::escape::{compact_json, markdown_fence};
21
22pub fn render(action: &Tool) -> Result<String> {
23    let (name, description, schema) = definition_parts(action)?;
24    let schema_str = compact_json(&schema);
25    // Pick a fence that doesn't collide with either the description or
26    // the schema string.
27    let combined = format!("{description}\n{schema_str}");
28    let fence = markdown_fence(&combined);
29    Ok(format!(
30        "## {name}\n\n{description}\n\n**Input schema:**\n{fence}json\n{schema_str}\n{fence}"
31    ))
32}
33
34#[cfg(test)]
35mod tests {
36    use super::super::tests::sample_def;
37    use super::*;
38
39    #[test]
40    fn envelope_starts_with_heading() {
41        let s = render(&sample_def()).unwrap();
42        assert!(s.starts_with("## slack_post_message\n"));
43        assert!(s.contains("**Input schema:**"));
44    }
45
46    #[test]
47    fn fence_flips_on_collision() {
48        let mut a = sample_def();
49        a.tool_description = Some("docs ``` ref".into());
50        let s = render(&a).unwrap();
51        assert!(s.contains("~~~json"), "fence should be ~~~:\n{s}");
52    }
53
54    #[test]
55    fn description_preserved_verbatim() {
56        let mut a = sample_def();
57        a.tool_description = Some("Post to Slack **bold**".into());
58        let s = render(&a).unwrap();
59        assert!(s.contains("**bold**"));
60    }
61}