What adapters do
ChatAdapter, which handles both formatting and parsing.
ChatAdapter
Prompt structure
The system message has four parts (in this order):- Field descriptions - Lists input/output fields with types and descriptions
- Field structure - Shows the marker format + type schemas
- Response instructions - “Respond with
[[ ## field ## ]]…” - Task description - Your instruction from the signature docstring (at the end)
Marker protocol
Fields are delimited by markers:Type schemas
For complex types, the adapter renders full schemas using BAML’s schema format:- Module paths stripped:
my_crate::Sentiment→Sentiment class/enumprefixes removed" | "→" or "(more natural)
Robust parsing with jsonish
Response parsing uses BAML’sjsonish parser, which handles:
- Malformed JSON - Missing quotes, trailing commas
- Markdown fences - Extracts JSON from ```json blocks
- Type coercion -
"42"→42,"true"→true - Partial recovery - Returns what it can parse + all errors
Constraint evaluation
After parsing, the adapter runs constraints:#[check]results → stored in metadata#[assert]failures → returnsParseError::AssertFailed
Error handling
Parse errors aggregate - you get all problems, not just the first:Using the adapter directly
Usually you don’t touch the adapter -Predict handles it. But for debugging:
Input formatting options
Input fields support two rendering paths:#[format("json" | "yaml" | "toon")]: serialize a field using that format.#[render(jinja = "...")]: render a field with a MiniJinja template.#[format]and#[render]are mutually exclusive on the same field.
#[render] context in ChatAdapter:
this: current field valueinput: full input object (plus top-level alias overlays)field:{ name, rust_name, type }vars: adapter/surface vars (currently{})
ChatAdapter configures MiniJinja with strict undefined behavior.
Available filters include:
- MiniJinja built-ins
- BAML parity helpers (
regex_match,sum) truncatefor length-limited string rendering
ChatAdapter panics.
Compiled templates are cached process-wide by template string.
Real example: Insurance claim extraction
Here’s what a prompt looks like for a complex nested type (fromexamples/16-insurance-claim-prompt.rs):
Full type definitions (InsuranceClaim and nested types)
Full type definitions (InsuranceClaim and nested types)
- Enums with many variants become “Definitions” at the top
- Doc comments become inline
//comments in the schema Option<T>renders asT or null- Nested objects show their full structure
- The instruction comes from the signature docstring
Design notes
- DSPy inspiration: Marker protocol from DSPy’s ChatAdapter
- BAML inspiration: Schema rendering and jsonish parsing from BAML
- Separation of concerns: Adapter doesn’t know about LMs - just formatting/parsing
