ModuleState snapshots the mutable state of every Predict leaf in a module into a serializable value, so an optimized program can be saved to disk and reloaded in production without re-running optimization.
ModuleState
A ModuleState holds one PredictState per predictor, keyed by the dotted path the optimizer walker discovers (predictors: BTreeMap<String, PredictState>). The BTreeMap keeps JSON output stable across runs. Paths follow the module structure: struct fields join with dots (inner.predictor), list elements append an index (steps[0]), and map entries append an escaped key (stages['draft']).
PredictState and the JSON shape
PredictState is the serializable snapshot of a single predictor’s mutable state.
A saved file therefore looks like:
The mutation seam
Internally, both state loading and optimizers reach predictors through one type-erased trait,DynPredictor (crate-private). Its apply_update method is the single mutation seam: every write to a predictor’s optimizable state flows through it, including optimizer candidate set and restore, ModuleState::apply, and the fx::Params overlay. An update is partial: None fields are left untouched, instruction: Some(None) clears the override back to the signature default, and Some(demos) replaces the demo set. load_state (used by apply) delegates to apply_update with both fields set. This is also the single place where prompt caches are invalidated: a candidate is data applied through the seam, never ad hoc field mutation.
