validate_schema
validate_schema checks a value against a JSON Schema before you use it. It never coerces types, reports every violation in a fixed order, and its messages are part of beacon’s compatibility contract, so they’re safe to snapshot and to feed back to the model.
validate_schema(instance, schema) -> None # raises SchemaValidationErrorschema_errors(instance, schema) -> list[dict] # [] when valid>>> validate_schema({"intent": "explode", "confidence": 1.5}, INTENT)SchemaValidationError: 2 schema violations: - /intent: "explode" is not one of ["cancel", "refund", "question"] - /confidence: 1.5 is greater than the maximum of 1>>> err.errors[{'path': '/intent', 'keyword': 'enum', 'message': '"explode" is not one of [...]'}, {'path': '/confidence', 'keyword': 'maximum', 'message': '1.5 is greater than the maximum of 1'}]Try it
Section titled “Try it”Strict JSON Schema. No coercion, every violation, in a fixed order, with messages that never drift.
No coercion
Section titled “No coercion”| Value | {"type": ...} |
Valid? |
|---|---|---|
"42" |
number |
no |
True |
integer |
no (booleans are not numbers) |
1.0 |
integer |
yes (JSON Schema rule) |
10**30 |
integer |
yes (exact) |
(1, 2) |
array |
yes (tuples are arrays) |
Supported keywords
Section titled “Supported keywords”beacon implements the JSON Schema 2020-12 subset that LLM apps use, including everything Pydantic’s model_json_schema() emits:
$ref (local #… pointers), $defs, definitions, type, enum, const, minLength, maxLength, pattern, minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, minItems, maxItems, uniqueItems, prefixItems, items, contains, minContains, maxContains, required, dependentRequired, minProperties, maxProperties, propertyNames, properties, patternProperties, additionalProperties, allOf, anyOf, oneOf, not, if/then/else, and boolean schemas.
Annotations are ignored: title, description, default, examples, format, deprecated, readOnly, writeOnly, $schema, $id, $comment, $anchor, contentEncoding, contentMediaType, contentSchema, discriminator, $vocabulary.
Error order
Section titled “Error order”At each node: $ref → type (on failure, nothing else is checked there) → enum, const → string/number/array/object keywords → allOf, anyOf, oneOf, not, if/then/else.
Object keywords run as required → dependentRequired → min/maxProperties → propertyNames, then members in the instance’s key order. Array items go in index order.
Unions point at the right branch
Section titled “Unions point at the right branch”When anyOf/oneOf fails and every branch but one failed only on type at the value itself, the remaining branch’s errors are reported. That’s the branch the value was evidently meant for, as with Optional[Model]:
>>> schema_errors({"user": {"age": "x"}}, OPTIONAL_USER)[{'path': '/user/age', 'keyword': 'type', 'message': 'expected type "integer", got "string"'}]Otherwise you get one summary error: value does not match any of the 2 subschemas in anyOf.
Details
Section titled “Details”- String lengths count code points.
patternuses Rust regex syntax (no look-around or backreferences) and is unanchored, as JSON Schema specifies.multipleOfis exact for decimals:0.3is a multiple of0.1, and19.99of0.01.enum,constanduniqueItemsuse JSON equality:1 == 1.0, key order is ignored,True != 1.- Remote
$refs are not supported (no I/O, ever). Cyclic refs raiseSchemaErrorat the depth limit.
Errors
Section titled “Errors”| Exception | Code | When |
|---|---|---|
SchemaValidationError |
schema.validation_failed |
the instance is invalid; .errors lists every violation |
SchemaError |
schema.invalid |
the schema is invalid or uses an unsupported keyword |
TypeError |
the instance contains non-JSON types (set, datetime, non-str keys…) |