#[derive(Signature)], mark fields as #[input] or #[output], and the macro generates everything needed to work with predictors.
Basic syntax
QAInput which you’ll use to call the predictor.
Docstrings add meaning
Doc comments turn into prompt instructions. They add context that types and variable names can’t express:- Struct docstring → The main task instruction
- Field docstrings → What each field means and what makes a good one
/// The question on a field called question adds nothing.
Using Rust’s type system
You get real types, not string parsing:String,booli8,i16,i32,i64,f32,f64Option<T>,Vec<T>,HashMap<String, T>
Custom types
When you have a non-standard type in a field, add#[BamlType] on it:
BamlType reference.
Demos (few-shot examples)
Attach examples for few-shot prompting via the predictor builder:Field attributes
Beyond#[input] and #[output], you can use:
#[alias] - Rename for LLM
#[format] - Input serialization
"json", "yaml", "toon"
#[render] - Custom input rendering (Jinja)
- Only valid on
#[input]fields - Template must be a string literal
- Jinja syntax is validated at compile time
- Cannot be combined with
#[format]on the same field - In
ChatAdapter, built-ins + BAML parity helpers (regex_match,sum) +truncateare available
this- Current field value as JSON-like datainput- Full input object (with top-level alias overlays)field- Metadata object withname,rust_name, andtypevars- Adapter/surface variables (currently empty inChatAdapter)
#[check] - Soft constraint
and, or) in expressions.
#[assert] - Hard constraint
Where it fits
- A
Signaturedoesn’t call the LM by itself - An
Adapterturns the signature into a prompt and parses LM responses back into typed outputs - A
Predictorchestrates this: signature + adapter + LM - Signatures are data - this separation supports reuse, testing, and optimizer integration
