baml.json.schema
Lowers a BAML type to ordinary, provider-neutral JSON Schema.
Signature
Lowers a BAML type to ordinary, provider-neutral JSON Schema.
The type-level counterpart to the value conversions above: they move a
value between BAML and json, while this describes the shape a value
of t would take.
A field whose type admits null (T?, json) is omitted from
required, as the decoder lets it be missing. Maps use schema-valued
additionalProperties, and unions use anyOf. Enums lower to a string
enum of their variant names. A nullable type becomes a ["T", "null"]
type list where it can, and anyOf otherwise. A nullable enum also lists
null in its enum, and a nullable literal uses anyOf, because enum
and const restrict the value whatever type says.
An int carries its range as minimum and maximum. A bigint is a
decimal string ("-12"), because a JSON number that large loses precision
in most parsers; a bigint literal is that string as a const. A media value
is the tagged object { kind, source, value, mime, name? }, where source
is "url" or "base64". A map's key type must be string, or string
literals, which become propertyNames.
Some types have values but no JSON form: uint8array, functions, futures,
reflect.Type, a class that requires one of those, and a class whose every
value is cyclic (JSON holds only finite values). Others have no values at
all: never, an enum with no variants, and a class that requires one of
those. Both kinds are dropped from a union, so uint8array | int | string
lowers as int | string, and an optional field of either kind is only
null. They differ inside a list or map:
never[] // only []: { "type": "array", "maxItems": 0 }
map<string, never> // only {}
uint8array[] // no JSON form: the list is dropped like its items
map<string, uint8array> // no JSON form
A list of a type with no values is still a list, the empty one. A list of
uint8array is not reduced to []: that would describe only the lists the
type can hold when they happen to be empty.
A type whose values all lack a JSON form, or that has no values, is rejected rather than lowered to a schema nothing can satisfy.
naming picks the names fields and variants go by, as it does for
decode<T>, so a schema and the decoder given the same naming agree on
every key. With Naming.Aliased, a @skip field holds its type's
empty value, so a class with a @skip field whose type has none (media, a
function, a class that contains itself) has no JSON form.
Documentation becomes description, with the text the output format
shows: a class's or a field's @description, then its /// docstring. A
field's description sits beside the $ref when the field is a class. An
enum's description holds its own documentation and one
- <variant>: <documentation> line per documented variant, because JSON
Schema has no description per enum value.
The root type is emitted inline, and every class and named alias beneath it
goes into $defs to be reached by $ref. That is what lets a recursive
graph terminate: a recursive root is emitted both inline and into $defs,
so the $ref back to itself resolves. Definitions are keyed by name; when
two share one, the later gets a _2 (then _3, ...) suffix.
Throws
baml.errors.Unsupportedwhen no value ofthas a JSON form, orthas no values.baml.errors.Unsupportedfor a type no schema describes exactly:unknown, an interface type, media that does not name its kind, a map whose key is notstringor string literals, and a generic class instance.baml.errors.Unsupportedfor a class, enum, or alias the reflection context cannot resolve.
Source:<builtin>/baml/ns_json/json.bamlbytes 21475–21628