Sharing pipelines (.spipe)
A steered pipeline normally exists only as in-memory Python objects. The .spipe format writes a pipeline down as a
portable bundle that can be saved, version-controlled, and handed to another person or machine.
One format contains two layers of information:
- The recipe: the model reference plus the controls exactly as constructed. Every
.spipecontains the recipe. Loading a recipe-only bundle and callingsteer()re-runs any fits and training. Results may differ across machines because of GPU nondeterminism. - The frozen resolution: what the steer step actually produced, i.e., fitted steering vectors, probes, LoRA
adapters, and optimized prompts, stored content-addressed alongside the recipe. A lock section pins fingerprints of
the producing model and per-fit digests of the recipe fields each artifact came from. Loading a frozen bundle
yields controls in precomputed form, where
steer()still runs but is cheap and model-free.
Freezing is a rewrite of the recipe rather than a second format. A frozen entry is an ordinary control constructed
with precomputed arguments: a CAA fitted from data freezes as a CAA constructed with the fitted vector, and a
fine-tune freezes as a LoadLoRA or
LoadCheckpoint pointing at the trained product.
Loading therefore takes the same construction and steer() path as any hand-built pipeline.
Saving and loading
pipeline.steer()
spipe = pipeline.to_spipe() # frozen by default once steered
spipe.save("formal_tone.spipe") # a .spipe path writes a zip (any other path writes a directory)
from steerability.spipe import SPipe
spipe = SPipe.load("formal_tone.spipe")
pipeline = spipe.pipeline() # backend, device, and dtype stay the caller's choice
pipeline.steer() # installs the frozen artifacts and fits nothing
response = pipeline.generate(...)
to_spipe(freeze=False) forces a recipe-only bundle from a steered pipeline, and spipe.thaw() turns a frozen bundle
back into its recipe. save(..., artifacts="thin") writes the manifest without the artifact payloads. A thin bundle
loads against an external store via SPipe.load(path, artifact_store=...).
Staleness
The lock records, per frozen artifact, a digest of the recipe fields a re-fit would consume. Editing an inert
application parameter in the manifest (say, a CAA multiplier) leaves the pinned vector valid. Editing the training
data does not, and loading then fails with a staleness error that identifies the control and the fix (thaw() and re-steer,
or allow_stale=True).
Verification
spipe.verify() reports on a bundle without loading a model: format validity, artifact integrity, staleness, version
compatibility, and whether the bundle references code. At steer() time, frozen steering artifacts are checked
against the model they are being installed on, under a policy chosen at pipeline(verify=...):
"strict"(default): a wrong architecture or width is an error. A calibrated artifact (a probe, a gate threshold) on a model with a different weight fingerprint is an error. A direction artifact on different weights of the same architecture is a warning, since a direction can be transferred across fine-tunes deliberately."warn": every mismatch is a warning."off": no checks.
Trust and allow_code
A .spipe from someone else is untrusted input. Loading never unpickles by default. Tensors are stored only as
safetensors, archives are extracted behind zip-safety guards, and every artifact is verified against its content
hash. Two things require an explicit allow_code=True at load, similar to trust_remote_code:
- References to Python callables, e.g., a scorer function a prompt optimizer was configured with. The manifest's
code_dependentflag says up front whether a bundle needs this, and the referenced modules must be on the import path. - Pickle-backed memory payloads (CPO's trained scorer memory), since unpickling executes code.
Frozen prompt-optimization bundles keep their search-only arguments (scorers, budgets) for provenance. A bundle whose optimizer used a custom scorer is therefore code-dependent even though the frozen memory never calls it.
Identity
Every bundle contains two digests. config_id is the same configuration identity that SteeringEval records, which
ties a .spipe to evaluation results. recipe_id additionally includes the model reference, since a steering
artifact is meaningless without its model.