Skip to main content

llama_cpp_bindings/
json_schema_to_grammar.rs

1use std::ffi::{CStr, CString, c_char};
2
3use crate::error::{LlamaCppError, Result};
4use crate::ffi_status_is_ok::status_is_ok;
5
6/// Convert a JSON schema string into a llama.cpp grammar string.
7///
8/// # Errors
9/// Returns an error if the schema contains null bytes or the conversion fails.
10pub fn json_schema_to_grammar(schema_json: &str) -> Result<String> {
11    let schema_cstr = CString::new(schema_json)
12        .map_err(|err| LlamaCppError::JsonSchemaToGrammarError(err.to_string()))?;
13    let mut out: *mut c_char = std::ptr::null_mut();
14    let mut error_ptr: *mut c_char = std::ptr::null_mut();
15
16    let status = unsafe {
17        llama_cpp_bindings_sys::llama_rs_json_schema_to_grammar(
18            schema_cstr.as_ptr(),
19            false,
20            &raw mut out,
21            &raw mut error_ptr,
22        )
23    };
24
25    if !status_is_ok(status) || out.is_null() {
26        let message = if error_ptr.is_null() {
27            "unknown error".to_owned()
28        } else {
29            let message = unsafe { CStr::from_ptr(error_ptr) }
30                .to_string_lossy()
31                .into_owned();
32
33            unsafe { llama_cpp_bindings_sys::llama_rs_string_free(error_ptr) };
34
35            message
36        };
37
38        return Err(LlamaCppError::JsonSchemaToGrammarError(message));
39    }
40
41    let grammar_bytes = unsafe { CStr::from_ptr(out) }.to_bytes().to_vec();
42
43    unsafe { llama_cpp_bindings_sys::llama_rs_string_free(out) };
44
45    String::from_utf8(grammar_bytes)
46        .map_err(|err| LlamaCppError::JsonSchemaToGrammarError(err.to_string()))
47}
48
49#[cfg(test)]
50mod tests {
51    use super::json_schema_to_grammar;
52
53    #[test]
54    fn simple_object() {
55        let schema = r#"{"type": "object", "properties": {"name": {"type": "string"}}}"#;
56        let grammar = json_schema_to_grammar(schema).unwrap();
57
58        assert!(!grammar.is_empty());
59    }
60
61    #[test]
62    fn null_byte_returns_error() {
63        let schema = "{\x00}";
64        let result = json_schema_to_grammar(schema);
65
66        assert!(result.is_err());
67    }
68
69    #[test]
70    fn simple_string() {
71        let schema = r#"{"type": "string"}"#;
72        let grammar = json_schema_to_grammar(schema).unwrap();
73
74        assert!(!grammar.is_empty());
75    }
76
77    #[test]
78    fn invalid_json_returns_ffi_error() {
79        let schema = "not valid json at all";
80        let result = json_schema_to_grammar(schema);
81
82        assert!(result.is_err());
83    }
84}