Skip to main content

cairn_runtime/
output.rs

1//! Helpers for reading structured output out of model text.
2
3use serde_json::Value;
4
5/// Extract a JSON value from model text: the whole text, a fenced
6/// ```json block, or the outermost `{...}` / `[...]` span.
7pub fn extract_json(text: &str) -> Option<Value> {
8    let t = text.trim();
9    if let Ok(v) = serde_json::from_str(t) {
10        return Some(v);
11    }
12    if let Some(start) = t.find("```") {
13        let after = &t[start + 3..];
14        let body = after.strip_prefix("json").or_else(|| after.strip_prefix("JSON")).unwrap_or(after);
15        if let Some(end) = body.find("```") {
16            if let Ok(v) = serde_json::from_str(body[..end].trim()) {
17                return Some(v);
18            }
19        }
20    }
21    for (open, close) in [('{', '}'), ('[', ']')] {
22        if let (Some(s), Some(e)) = (t.find(open), t.rfind(close)) {
23            if s < e {
24                if let Ok(v) = serde_json::from_str(&t[s..=e]) {
25                    return Some(v);
26                }
27            }
28        }
29    }
30    None
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use serde_json::json;
37
38    #[test]
39    fn extracts_json_in_common_shapes() {
40        assert_eq!(extract_json(r#"{"a":1}"#), Some(json!({"a": 1})));
41        assert_eq!(extract_json("Here you go:\n```json\n{\"a\": 2}\n```\nDone."), Some(json!({"a": 2})));
42        assert_eq!(extract_json("Result: {\"a\": {\"b\": 3}} hope that helps"), Some(json!({"a": {"b": 3}})));
43        assert_eq!(extract_json("no json here"), None);
44        assert_eq!(extract_json("{\"a\": 1"), None);
45    }
46}