Local MLX steering-vector workbench for chatting with a model while mixing activation-space controls. It ships with a small set of precomputed steering vectors and an app-backed path for creating your own contrastive vectors.
The repo is configured around
mlx-community/gemma-4-text-26b-a4b-it-8bit.
The bundled vectors were computed for that exact model.
It also includes an officially supported economy profile for
mlx-community/gemma-4-e4b-it-4bit-MAD,
with a separate matching vector bundle.
This is experimental software. Steering vectors are model-specific, sometimes messy, and should be treated as exploratory controls rather than reliable personality or safety edits.
- Runs a local FastAPI web UI for MLX-LM chat generation.
- Applies a weighted sum of steering vectors across selected transformer layers.
- Lets you adjust generation sampling settings from the UI.
- Lets you create new contrastive steering vectors from persona pairs.
- Hot-loads newly created custom vectors into the slider list.
- macOS with Apple Silicon.
- Python 3.10 or newer.
- 32GB or more of unified memory recommended for the default full profile.
- Less memory is needed for the economy profile, which uses the smaller E4B MAD model.
The default full model is mlx-community/gemma-4-text-26b-a4b-it-8bit.
The economy model is mlx-community/gemma-4-e4b-it-4bit-MAD. MLX-LM will use
the Hugging Face cache for either one, downloading the selected model when
needed. You can still pass --model /path/to/model if you have already
downloaded the same model locally.
Two model profiles are supported:
full- default, best quality, usesmlx-community/gemma-4-text-26b-a4b-it-8bit.economy- lower-memory option, usesmlx-community/gemma-4-e4b-it-4bit-MAD.
Each profile has its own steering vectors. The vectors are not expected to transfer cleanly to arbitrary model architectures or unrelated Gemma checkpoints.
First launch of a profile may spend a while downloading model weights. Later launches should reuse the Hugging Face cache.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtIf you already manage MLX in Conda, installing the same requirements into that environment is fine too.
python scripts/steering_webui.pyThis starts the default full profile, using
mlx-community/gemma-4-text-26b-a4b-it-8bit through the Hugging Face cache.
Then open:
http://127.0.0.1:7860
To use an explicit local copy of the same model:
python scripts/steering_webui.py --model /path/to/gemma-4-text-26b-a4b-it-8bitTo use the economy profile:
python scripts/steering_webui.py --model-profile economyOr point at a local economy model elsewhere:
python scripts/steering_webui.py \
--model-profile economy \
--model /path/to/gemma-4-e4b-it-4bit-MADThe --model override only changes where the model is loaded from. The
selected profile still controls which bundled steering-vector directory is
used.
The left panel contains steering sliders. Positive values move toward the
right-hand trait label, negative values move toward the left-hand trait label.
Built-in sliders default to the range -10 to 10. Use Zero sliders to
reset all active steering.
The Sampling button in the chat header opens a right-side panel for:
- max tokens
- temperature
- top p
- top k
- repetition penalty
The Create vector button opens the vector builder. A vector is created from
contrastive persona pairs, for example:
cryptic oracle :: practical engineer
old-fashioned speaker :: slang-heavy speaker
tsundere :: #baseline
Each line is one contrastive pair. The special negative or positive persona
#baseline means "do not ask for a persona"; the generation prompt becomes
Write some text.
New vectors are written to:
vectors/custom/<profile>/<name>_steering_vector.safetensorsvectors/custom/<profile>/<name>_steering_vector.jsondatasets/custom/<profile>/<name>.dataset.jsonl
If Add to sliders is checked, the app also updates data/custom_axes.json
and hot-loads the vector into the running UI.
Custom vectors are profile-specific. A custom vector name cannot collide with a built-in axis. Reusing an existing custom vector name replaces that custom vector's files and slider entry.
You can override the custom artifact locations with:
python scripts/steering_webui.py \
--custom-vector-dir /path/to/custom-vectors \
--custom-dataset-dir /path/to/custom-datasetsThe same canonical vector builder can be run directly:
python scripts/canonical_steering.py \
--positive-label mystical \
--negative-label mundane \
--persona-pair "cryptic oracle::practical engineer" \
--name mystical_mundaneTo build against the economy profile:
python scripts/canonical_steering.py \
--model-profile economy \
--positive-label mystical \
--negative-label mundane \
--persona-pair "cryptic oracle::practical engineer" \
--name mystical_mundaneTo rebuild all bundled vectors for a supported profile:
python scripts/build_profile_vectors.py --model-profile economyUse --help on any script for the full option list.
scripts/steering_webui.py- local web app.scripts/canonical_steering.py- canonical vector creation machinery.scripts/build_profile_vectors.py- rebuild bundled vectors for a profile.scripts/model_profiles.py- supported model/profile definitions.scripts/evaluate_steering.py- small evaluation/probing helper.static/index.html- web UI markup served by FastAPI.static/styles.css- web UI styles.static/app.js- browser-side chat, slider, sampling, and vector-builder logic.data/core_emotion_axes.json- bundled emotion controls.data/fun_control_axes.json- bundled style controls.data/custom_axes.json- starts empty; the app writes custom slider entries.seed_suffixes/- suffix prompts used during vector creation.vectors/gemma-4-text-26b-a4b-it-8bit/- default full-profile vectors.vectors/gemma-4-e4b-it-4bit-MAD/- economy-profile vectors.vectors/custom/<profile>/- app-created custom vectors, ignored by Git.datasets/<profile>/- generated datasets used to compute bundled vectors.datasets/custom/<profile>/- app-created custom-vector datasets, ignored by Git.
CC0 1.0 Universal. See LICENSE.