Skip to main content

artifactize_tools/
schema.rs

1use jsonschema::Validator;
2use serde_json::Value;
3
4/// Bound model-generated arguments before JSON Schema validation.
5pub const ARGUMENT_LIMIT: usize = 64 * 1024;
6
7/// Bound owner schemas before compilation and provider prompt construction.
8const MAX_SCHEMA_BYTES: usize = 8 * 1024 * 1024;
9/// Show a handful of actionable failures, rather than every error in an untrusted argument.
10const MAX_DIAGNOSTICS: usize = 5;
11/// Leave room for a truncation marker in a short diagnostic line.
12const MAX_QUOTED_BYTES: usize = 320;
13/// Reserve the rest of MAX_QUOTED_BYTES for the explicit truncated-value marker.
14const QUOTED_PREFIX_BYTES: usize = 300;
15
16pub fn compile(schema: &Value) -> Result<Validator, String> {
17    if !schema.is_object() || schema["type"] != "object" {
18        return Err("Tool inputSchema must declare type object.".into());
19    }
20    if serde_json::to_vec(schema).map_err(|e| e.to_string())?.len() > MAX_SCHEMA_BYTES {
21        return Err("Tool inputSchema exceeds 8 MiB.".into());
22    }
23    jsonschema::validator_for(schema).map_err(|error| {
24        format!(
25            "Invalid tool inputSchema at {}.",
26            quoted(&error.schema_path().to_string())
27        )
28    })
29}
30
31pub fn validate(validator: &Validator, args: &Value) -> Result<(), String> {
32    if !args.is_object()
33        || serde_json::to_vec(args).map_err(|e| e.to_string())?.len() > ARGUMENT_LIMIT
34    {
35        return Err("Tool arguments must be an object of at most 64 KiB.".into());
36    }
37    let mut errors = validator.iter_errors(args).take(MAX_DIAGNOSTICS).peekable();
38    if errors.peek().is_none() {
39        return Ok(());
40    }
41    let mut message = "Tool arguments do not match the registered input schema.".to_owned();
42    for error in errors {
43        message.push_str(&format!(
44            "\n- instancePath {}: constraint {} failed.",
45            quoted(&error.instance_path().to_string()),
46            quoted(&error.schema_path().to_string()),
47        ));
48    }
49    Err(message)
50}
51
52pub fn quoted(value: &str) -> String {
53    let mut value = serde_json::to_string(value).expect("string is JSON");
54    if value.len() > MAX_QUOTED_BYTES {
55        let mut end = QUOTED_PREFIX_BYTES;
56        while !value.is_char_boundary(end) {
57            end -= 1;
58        }
59        value.truncate(end);
60        value.push_str("… (truncated)");
61    }
62    value
63}