v0.4.0
Adds a backend layer between SteeringPipeline and the runtime so steering, fitting, scoring, and evaluation run either in process (Hugging Face transformers) or on vLLM (offline engine or server). Additionally rewrites the residual-stream state controls on an intervention representation that lowers to a given backend.
Backends
A new aisteer360/backends/ package with a common backend interface (aisteer360/algorithms/core/execution/) and three backends behind it:
huggingface(default): the in-process path, byte-identical to the 0.3.0 generation path apart from the stop-string / finish-reason change noted belowvllm: an offline vLLM enginevllm-serve: a running vLLM server, with server-side steering via the vLLM-Hook plugin
Both vLLM backends are enabled with the new vllm extra (uv pip install ".[vllm]").
- One backend per pipeline, selected with
SteeringPipeline(backend=...)(aBackendSpecor"huggingface"/"vllm"/"vllm-serve"), plus afit=venue policy:"auto"fits through the backend's capture surface where it serves the fit,"in_process"forces every fit onto a staged in-process model - Controls declare what they need via
BaseControl.requirements()andsteer_access();pipeline.check()reports which enabled controls the backend supports, plus the steer plan, before any model or engine work - Deterministic lifecycle:
Backend.release(),SteeringPipeline.release_backends(), and context-manager support;VLLMBackend.release()tears down the engine and distributed state idempotently, and a failedsteer()releases what it constructed - Activation methods fit through the backend's capture session: data-fitted
CAA,DirectionalAblation, andAngularSteeringsteer on any hidden-capture backend (ActAddandITIfitting stay in process). Steering vectors and probes carry provenance metadata (model, config, and chat-template fingerprints) and entry selection warns on a served-model mismatch - Residual-stream state controls are rewritten declaratively (
Intervention,TokenScope,Condition,WireForm), lowering to in-process hooks or to the served plugin. Gating is factored intoGate/Evidence/Rulewith affine, cosine, projected-cosine, and callable readouts; cosine and projected-cosine gates (including CAST) lower to the plugin's structured gate spec Benchmark,LLMJudgeMetric, andPerplexitytake abackend=; the benchmark runs a pre-flightcheck()over every sweep point and releases pipeline and metric backends when a run finishes or fails
New methods
constrained_decoding: Constrained decoding from one declarative source (json_schema,regex,grammar, orchoice); compiles to a client-side xgrammar automaton in process (newguidedextra) and to the engine's native structured-output parameters on vLLM
Other changes
- Reasoning models: per-call
chat_template_kwargspassthrough, a shared thinking splitter (aisteer360.utils.thinking.split_thinking,DEFAULT_THINK_TAGS),return_thinking/think_tagsonbatch_retry_generate(), and thecommonsense_mcqa,instruction_following, andtruthful_qause cases keep a thinking column and score the answer alone - Evaluation: every benchmark generation routes through
SteeringPipeline.generate(), so exactly one prompt path exists and message-level input controls fire;Benchmark(seed=...)derives one seed per (config, trial) for reproducible trials; checkpoints are a versioned envelope with trial-granular resume; newon_unsupported="raise" | "skip"andcheckpoint_every="trial" | "config" SteeringPipelineconstruction performs no I/O (model and tokenizer are acquired insteer()), preloaded objects are injectable viamodel=/tokenizer=, andgenerate_text(),generate_messages(), andgenerate_tokens()mirrorgenerate()'s keyword sources with per-source return types- RAD reworked around
reward_model_idandbetawith newtop_k,invert,score_index,score_transform,include_in_scoring, andefficientoptions; SASA converges onto the shared core probes substrate, with fisher probe fitting on unpaired data and chunked feature extraction - ActAdd: the single-pair estimator now reads the layer-input boundary where ActAdd injects (previously
layer_output) and no longer fabricates a BOS row, so fitted ActAdd vectors change from 0.3.0 aisteer360.utils.verbosity.set_verbosity()/get_verbosity()for toolkit logging- Docs, README, and notebooks updated for the backend layer (vLLM serving sections added to the CAA, TRL, and activation-adapter notebooks); all notebooks rerun
Breaking changes
- Component libraries renamed from
_common/tocommon/ininput_control,state_control, andoutput_control(e.g.aisteer360.algorithms.state_control._common→aisteer360.algorithms.state_control.common) thinking_interventionremoved; the capability survives as aPhasedDecodingconfiguration (see the driver docstrings)- State-control taxonomy:
NoStateControlremoved;StateControlsplits intoInterventionControl(declares an intervention template) andHookControl(ownsget_hooks()for mechanisms other than the residual stream). The previous gate, scorer, and condition classes (BaseGate,AlwaysOpenGate,CacheOnceGate,MultiKeyThresholdGate,ProbeSumGate,ConditionScorer,CosineDirectionScorer,ProjectedCosineScorer,ProbeContributionScorer) are deleted in favour ofstate_control.common.gating;DirectionalAblationTransformrenamedProjectionTransform;ScopeKindLiteralrenamedScopeKind, with fit-time vocabulary moved tocommon/fit_specs.pyand the wire compiler tocommon/lowering.py - Routing moved from
core/internals/probes/rules.pytooutput_control/routed_decoding/routing.pywith renamed types (ProbePredicate→Predicate,Rule→Route,RoutingRules→Router,Readout→ProbeReadings); probes are measurement-only Output.finish_reasongains"stop"(precedence stop, then eos, then length), a per-rowfinish_reasonstuple is added, and stop strings are no longer removed fromoutput_ids- Decoded
text=/messages=returns enforce one candidate per prompt;num_return_sequences/ngreater than one raisesValueErrorpointing toreturn_output=Trueorinput_ids= lazy_initis deprecated and inert;pipeline.model/pipeline.tokenizerareNoneuntilsteer()unless preloaded objects were passed- Evaluation helpers
chat_generate_model(),chat_generate_pipeline(), andrender_inference_prompts()removed (usegenerate_on_pipeline()orbatch_retry_generate()). Benchmark results foradapt_messagescontrols such asFewShotdiffer from 0.3.0 because the message phase now fires LLMJudgeMetricandPerplexityare constructed from a model reference plus keyword-onlybackend=(model_or_id,tokenizer, anddeviceremoved); judges are authored with class-levelprompt_template,scale,system_prompt, andstructured_output, and samplinggen_kwargsdefault to non-greedyUseCasesubclasses declare extra constructor parameters as class-level annotations (bare annotation is required, class attribute supplies the default); unknown kwargs and missing required parameters raiseTypeError, schema-invalid rows raiseValueErrorBenchmarkconfig identity is a canonical digest over the materialized pipeline with the baseline unified on"baseline"; checkpoints written by 0.3.0 are not resumed- RAD:
reward_pathremoved;reward_model_idandbetaare required - Packaging: new
guided(xgrammar) andvllm(vllm,vllm-hook-plugins) extras;devnow pulls invllm-hook-plugins; shared isort configuration inpyproject.toml
What's Changed
- Refactor pipeline execution functionality to allow for multiple backends by @emiehling in #22 (closes #17)
Full Changelog: v0.3.0...v0.4.0