artifactize_tools/
schema.rs1use jsonschema::Validator;
2use serde_json::Value;
3
4pub const ARGUMENT_LIMIT: usize = 64 * 1024;
6
7const MAX_SCHEMA_BYTES: usize = 8 * 1024 * 1024;
9const MAX_DIAGNOSTICS: usize = 5;
11const MAX_QUOTED_BYTES: usize = 320;
13const 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}