use schemars::{JsonSchema, schema_for};
use serde_json::Value;
#[allow(
clippy::expect_used,
reason = "schemars-generated schema always serializes to valid JSON"
)]
pub fn generate_json_schema<T: JsonSchema>() -> Value {
let schema = schema_for!(T);
serde_json::to_value(schema).expect("Failed to serialize schema")
}
#[allow(
clippy::expect_used,
reason = "schemars-generated schema always serializes to valid JSON"
)]
pub fn generate_json_schema_string<T: JsonSchema>(pretty: bool) -> String {
let schema = generate_json_schema::<T>();
if pretty {
serde_json::to_string_pretty(&schema).expect("Failed to serialize schema")
} else {
serde_json::to_string(&schema).expect("Failed to serialize schema")
}
}
pub fn graph_model_to_schema<T: JsonSchema>() -> Value {
generate_json_schema::<T>()
}
pub fn graph_model_to_schema_string<T: JsonSchema>(pretty: bool) -> String {
generate_json_schema_string::<T>(pretty)
}
pub fn build_schema_prompt<T: JsonSchema>(instructions: &str) -> String {
let schema = generate_json_schema_string::<T>(true);
format!(
r#"{instructions}
Your response MUST be a valid JSON object that conforms to the schema below. Do not include any explanatory text, markdown formatting, or code blocks outside of the JSON.
Schema:
{schema}
IMPORTANT: Return ONLY the JSON object. No additional text before or after."#
)
}
#[cfg(test)]
mod tests {
#![allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test code — panics are acceptable"
)]
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, JsonSchema)]
struct TestPerson {
name: String,
age: u32,
email: Option<String>,
}
#[test]
fn test_generate_json_schema() {
let schema = generate_json_schema::<TestPerson>();
assert!(schema.is_object());
let schema_obj = schema.as_object().unwrap();
assert!(schema_obj.contains_key("$schema") || schema_obj.contains_key("properties"));
}
#[test]
fn test_generate_json_schema_string() {
let schema_str = generate_json_schema_string::<TestPerson>(false);
assert!(!schema_str.is_empty());
assert!(schema_str.contains("name"));
assert!(schema_str.contains("age"));
let pretty_str = generate_json_schema_string::<TestPerson>(true);
assert!(pretty_str.contains('\n')); }
#[test]
fn test_build_schema_prompt() {
let prompt = build_schema_prompt::<TestPerson>("Extract person information.");
assert!(prompt.contains("Extract person information"));
assert!(prompt.contains("valid JSON"));
assert!(prompt.contains("schema"));
assert!(prompt.contains("name"));
}
#[test]
fn test_graph_model_to_schema() {
let schema = graph_model_to_schema::<TestPerson>();
assert!(schema.is_object());
let expected = generate_json_schema::<TestPerson>();
assert_eq!(schema, expected);
}
#[test]
fn test_graph_model_to_schema_string() {
let schema_str = graph_model_to_schema_string::<TestPerson>(false);
let expected = generate_json_schema_string::<TestPerson>(false);
assert_eq!(schema_str, expected);
let pretty_str = graph_model_to_schema_string::<TestPerson>(true);
assert!(pretty_str.contains('\n'));
}
}