oxicode_ai/dialect/
render.rs1use crate::tools::Tool;
6use serde_json::json;
7
8const PROMPT_TEMPLATE: &str = r#"# Tools
13
14You may call one or more functions to assist with the user query.
15Tool calls are emitted as text using the exact syntax below, not as native provider tool messages.
16
17Available functions are listed inside `<tools></tools>` as one JSON object per line:
18
19<tools>
20{{TOOLS}}
21</tools>
22
23{{DIALECT}}
24"#;
25
26const TOOLS_TOKEN: &str = "{{TOOLS}}";
27const DIALECT_TOKEN: &str = "{{DIALECT}}";
28
29pub fn render_tool_catalog(tools: &[Tool]) -> String {
34 tools
35 .iter()
36 .map(|tool| {
37 let obj = json!({
38 "type": "function",
39 "function": {
40 "name": tool.name,
41 "description": tool.description,
42 "parameters": tool.parameters,
43 },
44 });
45 serde_json::to_string(&obj).unwrap_or_default()
46 })
47 .collect::<Vec<_>>()
48 .join("\n")
49}
50
51pub fn render_inband_tool_prompt(tools: &[Tool], dialect: super::Dialect) -> String {
53 let guide = dialect.prompt();
54 let guide = guide.trim();
55 PROMPT_TEMPLATE
56 .replace(TOOLS_TOKEN, &render_tool_catalog(tools))
57 .replace(DIALECT_TOKEN, guide)
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use crate::dialect::Dialect;
64 use serde_json::json;
65
66 fn echo_tool() -> Tool {
67 Tool {
68 name: "echo".to_string(),
69 description: "Echo a message".to_string(),
70 parameters: json!({
71 "type": "object",
72 "properties": {"msg": {"type": "string"}},
73 "required": ["msg"],
74 }),
75 }
76 }
77
78 #[test]
79 fn catalog_is_one_json_object_per_line() {
80 let tools = vec![echo_tool(), echo_tool()];
81 let catalog = render_tool_catalog(&tools);
82 let lines: Vec<&str> = catalog.lines().collect();
83 assert_eq!(lines.len(), 2);
84 for line in lines {
85 let v: serde_json::Value = serde_json::from_str(line).unwrap();
86 assert_eq!(v["type"], "function");
87 assert_eq!(v["function"]["name"], "echo");
88 }
89 }
90
91 #[test]
92 fn prompt_substitutes_both_tokens() {
93 let prompt = render_inband_tool_prompt(&[echo_tool()], Dialect::Xml);
94 assert!(!prompt.contains(TOOLS_TOKEN));
95 assert!(!prompt.contains(DIALECT_TOKEN));
96 assert!(prompt.contains("<tools>"));
97 assert!(prompt.contains("\"name\":\"echo\""));
98 assert!(prompt.contains("<invoke"));
100 }
101}