Declaring a signature
- The struct doc comment becomes the instruction the model receives.
- Field doc comments become field descriptions in the prompt. Write one only when it adds something the field name does not;
/// The questionon a field namedquestionadds nothing. - The
#[output]field’s type is part of the prompt. Declaringanswer: Stringversusanswer: Vec<String>versus a custom enum changes both what the model is told to produce and what the parser will accept.
QAInput and QAOutput structs (each with a new constructor) and implements the Signature trait: instruction(), input_shape(), output_shape(), and per-field metadata. Calling the signature is the predictor’s job:
Richer output types
The type system is the main lever for controlling model output. An enum output constrains the model to its variants; anOption makes absence a legal answer instead of an invitation to guess; a nested struct is filled field by field; a constraint attaches a rule the value must satisfy:
Sentiment, the call fails with a typed error carrying the raw response; see Predict for error handling and Adapters for how the types are rendered into the prompt and parsed back.
Field attributes
Every field requires exactly one of
#[input] or #[output], and the signature requires at least one of each.
#[render(jinja = ...)] templates see this (the field value), input (the full input object with alias overlays), field (name, rust_name, type), and vars (currently empty). Available filters: minijinja builtins plus regex_match, sum, and truncate.
Supported field types
Rejected shapes (compile errors)
Custom types with #[Schema]
#[Schema] marks a struct or enum as usable inside signature fields. It accepts no arguments. It expands to #[derive(facet::Facet, serde::Serialize, serde::Deserialize)] with crate-path attributes; enums additionally receive #[repr(u8)] when no explicit repr is present.
#[BamlType] survives as a backwards-compatible alias with identical expansion. The old vendored BAML stack, including its #[baml(...)] attribute grammar, was removed. The schema layer now reads facet metadata instead.#[Schema] types:
Enums must be unit-only. Variant matching at parse time is case-insensitive and strips quotes. Data-carrying enums are rejected: schema construction panics with
data-carrying enums are not supported; use a struct. Model union-shaped data as a struct with optional fields.
The typesys pipeline
The in-housetypesys module implements the type system in four parts: schema (the FieldType/OutputSchema model, derived from facet Shape metadata), render (the schema text the model sees), coerce (tolerant parsing of model output), and constraint (check/assert evaluation). Order per call: render the schema into the prompt, coerce the response text per field, evaluate constraints, then deserialize into the output struct via serde.
render
typesys::type_name produces the inline label shown in field descriptions and the should be of type: line (unions render as A or B, literals as "value"). typesys::schema_block produces the expanded block for structured types: classes render as { field: type, ... } with doc comments, enums as a one of: value list, lists of classes as a bracketed block. Primitives return their label so the adapter skips a redundant block.
coerce
typesys::coerce(raw, field_type, types) returns a Coerced { value, flags }. Non-fatal observations are recorded as Flag values: StrippedCodeFence, ParsedListFromText, CoercedFromString, ExtraTextIgnored.
Per-field results land in
CallMetadata::field_meta, an IndexMap<String, FieldMeta> where FieldMeta carries raw_text, flags, and checks.
Constraints
Constraints attach rules to output fields. Use#[assert] for rules whose violation should fail the call, and #[check] for rules you want recorded without failing.
Expression language
Expressions are minijinja expressions evaluated with the parsed field value bound asthis. Derive-emitted expressions compile once per process.
Inspecting results
Compile-time enforcement
A#[check] without a label is a compile error:
See also
- Predict for calling signatures and handling
Predictedoutput - Adapters for prompt formatting and the parse pipeline
- The module macro for declaring signatures as bodyless functions
- Example: 01-simple.rs
- Example: 16-insurance-claim-prompt.rs
