Ster/Docs

Quick start

Every command below was executed on a checkout of this repository (Ster 0.13.0, macOS arm64, CPU). The walkthrough uses a tiny offline toy checkpoint so it needs no download, no GPU, and no Hugging Face account. The toy model's weights are

Start from an existing pair set#

If you already have Ster contrastive data, import it instead of creating demonstration rows. The file must be the canonical pair-set JSON documented below; import performs the complete loader validation before it changes the workspace.

sh
ster workspace import-pairs ./my-pairs.json
ster workspace show
ster train --model <MODEL> --output direction.ster.json

The third command omits --pairs and uses the active accepted import. You can also do the first step from first use with ster onboarding --import-pairs ./my-pairs.json. Skipping import leaves an empty, usable workspace.

Overview#

Every command below was executed on a checkout of this repository (Ster 0.13.0, macOS arm64, CPU). The walkthrough uses a tiny offline toy checkpoint so it needs no download, no GPU, and no Hugging Face account. The toy model's weights are seeded random numbers — its text is deterministic gibberish, and that is the point: the mechanics (reading, training, gating, steering) are identical to a real model's, just instant. To do the same on a real model, substitute --model meta-llama/Llama-3.2-1B (or any local Safetensors directory of a supported family) everywhere toy-llama appears.

1. Install#

From this checkout:

bash
cargo install --path . --locked

Or from GitHub: cargo install --git https://github.com/wisent-ai/ster --locked. Add --features metal or --features cuda for GPU backends — they are compile-time choices, not flags (configuration).

console
$ ster --version
ster 0.13.0

2. Make an offline toy model#

bash
WORK=$(mktemp -d)
cd "$WORK"
python3 <ster-repo>/docs/examples/make-toy-model.py toy-llama

This writes config.json (model_type: "llama", 4 layers, hidden width 64), a WordLevel tokenizer.json, and model.safetensors — the exact three inputs model resolution requires from a local directory.

3. Define the trait as contrast#

A trait is a contrastive pair set: matched prompts that differ only in the trait. Save as pairs.json (this corpus trains a toy "calm vs stormy" trait; the toy tokenizer only knows its own ~60 words, so pairs stick to that vocabulary):

json
{
  "trait_name": "calm",
  "pairs": [
    {
      "positive": "the sea is calm and quiet tonight .",
      "negative": "the storm waves crash loud against the rocks ."
    },
    {
      "positive": "the water lies still in the harbor .",
      "negative": "the wind howls hard over the dark hills ."
    }
  ]
}

The full executed set has 8 pairs and is checked in as `examples/pairs.json` — enough for ster vector optimize to hold out pairs on both sides of its split.

4. Train#

console
$ ster vector train --model toy-llama --pairs pairs.json --layers 1..3 --method caa --output calm.ster.json
this model publishes no chat template, so prompts and completions are encoded as raw text
reading pair 1/8
...
reading pair 8/8
{
  "artifact": {
    "chat_template": "absent",
    "hidden_size": 64,
    "layers": [
      {
        "layer": 1,
        "norm": 1.0000000062843015,
        "train_accuracy": 0.875,
        "train_margin": 0.5598376393318176,
        "width": 64
      },
      {
        "layer": 2,
        "norm": 1.0000000878582571,
        "train_accuracy": 0.875,
        "train_margin": 0.7613550424575806,
        "width": 64
      }
    ],
    "metadata": {},
    "method": "caa",
    "model": "toy-llama",
    "model_revision": null,
    "precision": "f32",
    "product": "ster",
    "schema_version": 1,
    "trait_name": "calm"
  },
  "chat_template": "absent"
}

Progress goes to stderr; the summary JSON goes to stdout; the full steering artifact (including the vectors) is in calm.ster.json. --layers 1..3 is a half-open range: layers 1 and 2 (layer selection). The first stderr line is what --chat-template auto decided: this toy publishes no template, so the pairs were read as raw text, and the artifact records that as absent so a later evaluate can tell (chat template).

5. Evaluate#

console
$ ster vector evaluate --model toy-llama --pairs pairs.json --vector calm.ster.json
{
  "chat_template": "absent",
  "layers": [
    { "layer": 1, "accuracy": 0.875, "margin": 0.5598376393318176 },
    { "layer": 2, "accuracy": 0.875, "margin": 0.7613550424575806 }
  ],
  "method": "caa",
  "model": "toy-llama",
  "pair_count": 8,
  "trait_name": "calm"
}

Accuracy is the fraction of pairs whose positive prompt projects higher onto the direction than its negative; margin is the mean projection gap (evaluation). Evaluate on held-out pairs, not the training set, when the number matters. --chat-template and --precision should also match the run that trained the artifact: the artifact records both, so a mismatch is warned about on stderr rather than left to be noticed.

6. Generate — baseline, then steered#

console
$ ster generate --model toy-llama --prompt "describe the evening lake ." \
    --max-new-tokens 12 --temperature 0 --seed 42
loud sea hard hard , white , white , white loud loud

$ ster generate --model toy-llama --vector calm.ster.json --strength 1.0 \
    --prompt "describe the evening lake ." --max-new-tokens 12 --temperature 0 --seed 42
white white white howls howls howls drifts , white white white white

These two runs were recorded when temperature 0 and seed 42 were the defaults; Ster now has no default for either, so the commands state the values the recorded runs used. At temperature 0 decoding is argmax and the seed does not change the text.

Same prompt, same seed — the added direction visibly changes the token distribution (on a real model, it changes the trait; on this toy it changes gibberish). Strength is linear and signed; pass negative values as --strength=-2.0 (with =, or the CLI parses -2.0 as a flag). Large strengths dominate the stream entirely — at --strength 2.0 the toy model emits one token forever, and past that it can hit end-of-sequence immediately and return an empty line. Calibrate: intervention.

7. Where to go next#

  • The full command surface, argument by argument: cli.
  • Method-and-layer selection with ster vector optimize, artifact gating, and activation export, all executed: walkthrough-steering.
  • The same engine for desktop apps, one process per operation: desktop request reference.
  • Every refusal sentence you might hit on the way: runbook.