Predict<S> is the leaf module. One Predict is one prompt template and one LM call: it formats a signature’s fields, instruction, and demos into a prompt, sends it to the configured LM, and parses the response into S::Output. Every other module ultimately delegates to one or more Predict leaves, and optimizers tune a program by rewriting Predict state (instruction override and demos). See How DSRs thinks for the mental model.
Usage
QAInput and QAOutput from the field markers. .call() returns Result<Predicted<QAOutput>, PredictError>, and Predicted<O> implements Deref<Target = O>, so output fields read directly off the result. Everything beyond the defaults, such as demos, an instruction override, tools, or a per-instance LM, goes through the builder.
Construction
PredictBuilder<S>
Demo<S> is the typed input/output pair for few-shot prompting: Demo::new(input, output) with public fields input: S::Input and output: S::Output. Demos render as user/assistant exchanges in the prompt, and the types guarantee a demo matches the signature — a Demo<QA> cannot be attached to a Predict<SummarizeSig>. To seed a demo from a labeled trainset row, project the row through its ToInput/ToOutput impls: Demo::new(row.to_input()?, row.to_output()?), see Data. Tools are settable only at build time. Demos and the instruction override are also writable after construction through the optimizer seam (DynPredictor::apply_update, load_state), see State. The formatted system message and demo turns are cached once per (instruction, demos) configuration; every state mutation invalidates the cache.
Calling
Each
call executes this pipeline:
- Build the chat: cached system + demo prefix, plus the input formatted as the live user message.
- Resolve the LM: the per-instance
.lm(...)if set, otherwise the globalconfigure()LM. - Consult any active replay scope (see below).
- Send via
LM::call_with_toolsetinToolLoopMode::Auto, executing tool calls up tomax_tool_iterations. - Parse the response into
S::Outputthrough the[[ ## field ## ]]protocol, evaluating#[check]and#[assert]constraints. - Record a trace span when inside a
capture()scope.
Replay interception
Before constructing any client,Predict consults the active replay scope. A Serve directive returns the recorded span with zero provider calls and zero tool re-executions; a Refuse directive returns PredictError::Replay; Live (or no scope) proceeds normally. Served predictions carry no per-field parse metadata. See Traces.
Predicted<O>
Every call returns Predicted<O>: the typed output plus runtime bookkeeping. It implements Deref<Target = O>, so output fields read directly (result.answer).
CallMetadata
Each
FieldMeta records raw_text (the text the LM produced for that field), flags (Vec<Flag>, non-fatal coercion observations such as a stripped code fence), and checks (Vec<ConstraintResult> with label, expression, passed). Accessors: field_meta(), field_raw(field), field_flags(field), field_checks(field), field_names(), and has_failed_checks() for a quick scan across all fields. Failed #[check] constraints land here; failed #[assert] constraints become a Parse error instead.
PredictError
PredictError::class() buckets into ErrorClass (BadRequest, NotFound, Forbidden, Temporary, BadResponse, Internal); is_retryable() drives retry logic. Parse errors include the raw response and the token usage: failed parses still consume tokens.
ToolSet
ToolSet is pre-fetched tool definitions plus name-indexed executors, built once and reused across calls.
Predict builds and caches one ToolSet per instance from its builder tools on first use. To put Code Mode on a predictor, add the CodeModeTool (re-exported by dspy_rs under the code-mode feature) as the single tool:
ToolSet::code_mode(...) set drops into LM::call_with_toolset directly. See Code Mode.
