Skip to content

Repository files navigation

ster by Wisent

Source Issues Wisent Discord LinkedIn X Enterprise

Source License Discord

Ster

Ster is a native Rust toolkit for representation reading and activation steering in open-weight language models. It reads hidden states from selected transformer layers, learns directions from contrastive examples, evaluates whether those directions separate the requested trait, and applies them during generation. It also trains the weights themselves: LoRA adapters under a supervised, a preference, a reward-modelling or a policy-gradient objective, and the tools to merge, score and inspect what comes out.

The product is Ster. Wisent is the company that builds it.

Current product contract

Ster 0.13 provides one binary and one library crate. Both use the same versioned JSON artifacts and native Candle runtime.

Included now:

  • local and Hugging Face Llama-family checkpoints published as Safetensors;
  • CPU execution, with compile-time Metal and CUDA backends;
  • pair-set authoring and inspection for duplicates, refusals, length balance, and diversity, with no model loaded;
  • synthetic pair generation from a trait description, written either by the local runtime or by a hosted model reached through Brama;
  • last-token hidden-state extraction from any selected transformer layer;
  • contrastive activation addition (caa), principal-direction (pca), and logistic-probe training;
  • holdout selection across method and layer, published as the scored candidate table the choice was made on;
  • artifact evaluation by pair-ordering accuracy and projection margin;
  • additive residual-stream steering during autoregressive generation;
  • LoRA supervised fine-tuning of local checkpoints from prompt and completion examples;
  • direct preference optimization, and its IPO variant, over a contrastive pair set, scored against the frozen reference the same weights already carry;
  • Bradley-Terry reward models: a scalar head trained with the adapters beneath it and written in the same artifact;
  • group-relative policy optimization against a reward model or an offline deterministic reward, with a KL penalty to the frozen base;
  • merging an adapter into the base weights as a standalone checkpoint;
  • frozen adapter artifacts applied at generation time;
  • deterministic JSON pair, activation, steering, and evaluation formats.

Explicit boundaries:

  • The current native runtime accepts model_type: "llama". Other architectures fail before weights are loaded rather than silently using a wrong adapter.
  • Ster controls local open-weight models. Hosted model routing belongs to Brama.
  • Steering reads hidden states, so it always runs on a local open-weight model. Writing pair text needs no activations, so ster pairs synthesize may take its generator from Brama instead. Ster holds no provider credential and speaks no provider API: it calls the gateway, which owns the routing.
  • Fine-tuning trains low-rank adapters, and on a reward run the scalar head that reads them. It trains nothing else: the base weights are mapped read-only and never registered as trainable. One sequence goes through each forward pass with gradient accumulation standing in for a batch, and there is no distributed training and no fleet placement. Ster does not place work on another machine, and nothing places Ster: it trains on the machine you start it on.
  • No objective consults a hosted model. ster tune grpo takes its reward from a reward model you trained or from a deterministic function of the completion; there is no judge model and no LLM-as-critic wired into any gradient.
  • Ster does not place work on a fleet, and manages no credentials: it has no credential store, no credential lifecycle, and reads what it needs from the environment it was started in. Neither responsibility is delegated to another product. The compute registry declares no Ster placement profile, no Ster OS unit, and no Ster workload; stado fleet list places Ster nowhere; and Skarbiec has no record of Ster in its source, README or documentation. If you need a run on a bigger machine, you start Ster on that machine yourself.
  • Release delivery is the one thing another product does do for Ster: Stado installs the built binary from the .wisent-release.json this repository ships. See Installing through Stado.
  • The previous Python package and wisent command were removed in the Rust cutover. Python namespace compatibility is not part of the Ster contract.

Install

Install the current source release from GitHub:

cargo install --git https://github.com/wisent-ai/ster --locked

From this source checkout:

cargo install --path . --locked

Metal and CUDA are build-time choices:

cargo install --git https://github.com/wisent-ai/ster --features metal --locked
cargo install --git https://github.com/wisent-ai/ster --features cuda --locked

The crates.io name ster is currently unclaimed and is not Ster's release surface. pip install ster installs unrelated software from another publisher.

Delivery through Stado and the checkpoint cache are covered in installing and where downloads land.

First steering workflow

If you already have a canonical Ster pair set, import it instead of creating starter rows:

ster workspace import-pairs ./my-pairs.json
ster workspace show

Ster validates the whole document before writing anything, keeps a canonical copy under $XDG_DATA_HOME/ster (or ~/.local/share/ster), and makes it the active set. Repeated content is reported as unchanged; a reused name with different content is conflicting and never overwrites the first set. The same operation is available during first use with ster onboarding --import-pairs ./my-pairs.json.

If you do not have an existing set, create pairs.json:

{
  "trait_name": "truthful",
  "pairs": [
    {
      "positive": "Question: What evidence supports this claim? Answer: I do not have enough evidence to confirm it.",
      "negative": "Question: What evidence supports this claim? Answer: It is definitely true because it sounds plausible."
    },
    {
      "positive": "Question: Is this citation real? Answer: I cannot verify that citation from the available context.",
      "negative": "Question: Is this citation real? Answer: Yes, the citation is unquestionably real."
    }
  ]
}

A set can also be produced without a text editor. ster pairs add appends one pair at a time and creates the file, and its parent directory, when it does not exist yet:

ster pairs add \
  --pairs pairs.json \
  --trait truthful \
  --positive "Question: Did the study replicate? Answer: I have not seen a replication, so I cannot claim it did." \
  --negative "Question: Did the study replicate? Answer: Of course it replicated; results that clean always hold."

ster pairs synthesize writes a whole set from a one-sentence trait description, generating both sides of every pair with the local runtime by default:

ster pairs synthesize \
  --model meta-llama/Llama-3.2-1B \
  --trait "answers only from verifiable evidence and says so when it cannot" \
  --count 20 \
  --output pairs.json

Run ster pairs inspect --pairs pairs.json before training: it finds duplicate and near-duplicate pairs, sides that read as refusals, lopsided pairs where one side is far longer than the other, and how much the set repeats itself.

Train a direction for layers 12 through 19. The explicit --pairs below works for the manually created file; omit it to use the active imported set:

ster train \
  --model meta-llama/Llama-3.2-1B \
  --pairs pairs.json \
  --layers 12..20 \
  --method caa \
  --output truthful.ster.json

Generate with that direction:

ster generate \
  --model meta-llama/Llama-3.2-1B \
  --vector truthful.ster.json \
  --strength 1.0 \
  --prompt "Explain the result and cite only evidence you can verify."

Use an immutable Hugging Face commit with --revision <sha> when the artifact must remain reproducible across model updates. A local directory may be passed to --model when it contains config.json, tokenizer.json, and one or more Safetensors weight files.

CLI

ster train      learn one vector per selected layer
ster optimize   select method and layer on an 80/20 holdout
ster evaluate   measure a vector on a contrastive pair set
ster generate   run normal or steered autoregressive generation
ster extract    export hidden states for an arbitrary prompt set
ster inspect    summarize and validate a steering artifact
ster pairs      author, inspect, and synthesize contrastive pair sets
ster tune       train, merge, score, and inspect LoRA adapters
ster onboarding import or replay first use
ster workspace  import, activate, and inspect persistent pair sets

Run ster <command> --help for exact arguments. Commands return non-zero on invalid model architecture, missing files, mismatched artifacts, invalid layer selection, or non-finite vectors.

Each command family has its own page in this repository:

Architecture

The runtime uses Candle directly. Ster owns its Llama decoder loop so every transformer block exposes two exact operations that generic inference APIs do not: capture the final-token residual state after a block and add a selected steering direction before the next block. The same decoder can also run a differentiable pass, which is what makes fine-tuning possible at all: Candle's fused rotary_emb::rope, ops::softmax_last_dim, and ops::rms_norm kernels have no backward pass, so training selects composed equivalents at exactly those three call sites while inference keeps the fused ones. The same pass also chooses whether the attached adapters apply, which is what lets preference optimization score the frozen reference without a second copy of the weights. Tokenization, Safetensors loading, attention, KV caching, sampling, and device kernels remain native Rust.

Documentation and support

Ster is pre-1.0. Artifact schema changes and supported-model expansion remain subject to the repository's versioned release contract.

License

MIT — see LICENSE.