#json-schema #llm #structured-output #json #coerce

schema-coerce

Coerce LLM JSON values to a simple field-schema: string->int, bool, float; strip wrapper objects; fill defaults. Forgiving structured-output recovery.

1 unstable release

0.1.0 May 16, 2026

#13 in Encoding

MIT/Apache

9KB
79 lines

schema-coerce

Coerce an LLM-emitted JSON value to a small field-schema.

LLMs return "42" instead of 42, "true" instead of true, or wrap the answer in { "result": {...} }. This crate gives you a lossy coerce(value, &schema) that applies the obvious fixes:

  • String containing an integer → integer
  • String containing a float → float
  • String "true" / "false" / "yes" / "no" → bool
  • Object containing a single key whose value matches the schema → unwrap (handles wrappers like { "result": {...} })
  • Missing field → fill from default

Example

use schema_coerce::{coerce, Field, Type};
use serde_json::json;

let schema = vec![
    Field { name: "count", ty: Type::Int, default: None },
    Field { name: "ok", ty: Type::Bool, default: Some(json!(false)) },
];
let raw = json!({ "count": "42", "ok": "yes" });
let fixed = coerce(raw, &schema);
assert_eq!(fixed["count"], json!(42));
assert_eq!(fixed["ok"], json!(true));

schema-coerce

crates.io

Coerce LLM JSON values to a simple field-schema. String→int, "yes"true, unwrap {"result": {...}} wrappers, fill missing fields from defaults.

use schema_coerce::{coerce, Field, Type};
use serde_json::json;

let schema = vec![
    Field { name: "count", ty: Type::Int, default: None },
    Field { name: "ok", ty: Type::Bool, default: Some(json!(false)) },
];
let raw = json!({ "count": "42", "ok": "yes" });
let fixed = coerce(raw, &schema);
assert_eq!(fixed["count"], json!(42));

Pair with llm-json-repair (parse) and json-pluck (extract). MIT or Apache-2.0.

Dependencies

~255–730KB
~14K SLoC