DomainSteer
Contrastive activation addition for scientific domains. DomainSteer builds one steering vector per field and adds it to a causal language model's residual stream while the model generates.
Pairs for 144 scientific domains ship with the package, so building a vector needs no API key. The 2,160-item polysemy-trap benchmark ships too.
pip install domainsteer
from domainsteer import DomainSteerer
steerer = DomainSteerer(
model_name="meta-llama/Llama-3.1-8B-Instruct",
domain="Industrial biotechnology",
)
steerer.build(layer=15)
steerer.generate("What is a strain?", alpha=0.25)
steerer.compare(
"If the assembly falls apart, do you get back the original pieces?",
alpha=0.25,
)
Steered, "strain" is a microbial variant. Unsteered, it is a pulled muscle. Each bundled pair is that contrast: the same unnamed question, answered once in the domain sense and once in the everyday sense.
build(layer=15) extracts the vector at decoder block 15 and caches it. Later calls reuse the cache. generate takes a raw strength alpha (typical range 0.10–0.40). The shift at each token is h ← h + α‖h‖v.
Directions are written under ~/.cache/domainsteer/<domain>/<model>/directions/. Pass cache_dir= to put them somewhere else.
Bundled pairs
| Model | Domains | Pairs |
|---|---|---|
meta-llama/Llama-3.1-8B-Instruct |
144 | 4,320 |
meta-llama/Llama-3.2-3B-Instruct |
144 | 4,320 |
from domainsteer import list_bundled_models, bundled_manifest, load_bundled_pairs
list_bundled_models()
load_bundled_pairs("meta-llama/Llama-3.1-8B-Instruct", "3106")
load_bundled_pairs("meta-llama/Llama-3.1-8B-Instruct", "Industrial biotechnology")
Pairs are model-specific. Both shipped models share the question stems and do not share the answers. A vector built from 8B pairs is not applied to 3B. If the model you name has no bundled set, load_bundled_pairs returns None.
DomainSteerer looks for pairs in this order:
- A cache file under
cache_dir/<domain-slug>/pairs.jsonl - The bundled pairs for that model and domain
- API generation through
PairGenerator(pip install "domainsteer[generation]"and an API key)
Turn the bundle off with use_bundled_pairs=False. Regenerate with build(force_pairs=True).
Benchmark
The polysemy-trap exam is 144 domains × 15 items = 2,160 questions. The question does not name the field. The overloaded term has to be read in the domain sense.
from domainsteer import load_benchmark, iter_benchmark
item = load_benchmark("3106")["items"][0]
item["question"] # 'What can a fingerprint distinguish?'
item["gold"] # 'product identity'
item["baseline_trap"] # "a person's identity"
item["gold_responses"] # one frozen reference sentence
sum(len(d["items"]) for d in iter_benchmark()) # 2160
gold_responses holds exactly one reference sentence per item.
What build does
- Resolve the domain against the registry of 144 ANZSRC groups, ten concepts each.
- Load the 30 contrastive pairs for that model and domain.
- Replay both answers under the helper system prompt. The domain prompt is not in this forward pass.
- Mean-pool the assistant-token hidden states at the layer you passed.
- Save
v = unit(mean expert − mean default).
Pass estimator="rfm" to use a Recursive Feature Machine instead of the difference of means. Those vectors are cached separately.
Calling build() with no layer asks an NLI judge to pick the layer and to map an expertise dial onto alpha. That judge is not installed by pip install domainsteer. Use build(layer=…) and a raw alpha.
Optional installs
| Extra | Install | What it adds |
|---|---|---|
generation |
pip install "domainsteer[generation]" |
API pair generation (anthropic, openai) |
eval |
pip install "domainsteer[eval]" |
Cosine scoring with sentence-transformers |
dev |
pip install "domainsteer[dev]" |
pytest, ruff |
License
MIT.
Release files for domainsteer 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| domainsteer-1.0.0.tar.gz | 2.0 MB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| domainsteer-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
| domainsteer-1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 7.0 MB
Release files / domainsteer-1.0.0.tar.gz
| Download URL | domainsteer-1.0.0.tar.gz |
|---|---|
| Size | 2.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0489156978cf566ee959debd0538417eb1832430116eda9bf91ba601ebdd1fd3
|
|
BLAKE2b-256 checksum How to use checksums |
ec381e247a033647ee9756fbc87d60fe6f7c7e45e07843a12d096934bcf960d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Release files / domainsteer-1.0.0-py3-none-any.whl
| Download URL | domainsteer-1.0.0-py3-none-any.whl |
|---|---|
| Size | 2.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
97773c52429ae2a45714b847401881f636cbcdf13663f81a2c275eaf9cb48629
|
|
BLAKE2b-256 checksum How to use checksums |
a8ddb772a638ae8f057245e4f5767f2702d36e524599a99af54bcb3ea3a68748
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Release files / domainsteer-1-py3-none-any.whl
| Download URL | domainsteer-1-py3-none-any.whl |
|---|---|
| Size | 2.5 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
11b2c9011bb7d5f9556e9424926e991c5b21249d0e430a8711581f54ac966835
|
|
BLAKE2b-256 checksum How to use checksums |
99c5632317e8733fac7cafdd238a8793d58f674268899f3830d426af9d0a0815
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|