Skip to main content

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:

  1. A cache file under cache_dir/<domain-slug>/pairs.jsonl
  2. The bundled pairs for that model and domain
  3. This model answering the 30 shared questions in data/questions/ itself, with the hook off. No API call. Used when the model is not one of the two shipped sets (for example Gemma).
  4. API generation through PairGenerator (pip install "domainsteer[generation]" and an API key), only when the domain has no shipped questions

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

  1. Resolve the domain against the registry of 144 ANZSRC groups, ten concepts each.
  2. Load the 30 contrastive pairs for that model and domain.
  3. Replay both answers under the helper system prompt. The domain prompt is not in this forward pass.
  4. Mean-pool the assistant-token hidden states at the layer you passed.
  5. 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.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for domainsteer 1.0.1
File Size Uploaded
domainsteer-1.0.1.tar.gz 2.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for domainsteer 1.0.1
File Interpreter ABI Platform
domainsteer-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 4.6 MB

Release files / domainsteer-1.0.1.tar.gz

Download URL domainsteer-1.0.1.tar.gz
Size 2.1 MB
Tags Source
SHA-256 checksum
How to use checksums
b162c91d90f317bf8bbd2159821ca9d1edad8036b42a973082410aeb77410392
BLAKE2b-256 checksum
How to use checksums
c8ea5791f462f8638bced58d810b1795e4d9758bd9fa958920a09aa8696aace7
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.1-py3-none-any.whl

Download URL domainsteer-1.0.1-py3-none-any.whl
Size 2.5 MB
Tags Python 3
SHA-256 checksum
How to use checksums
1c992d124ace3ff38006f8505cf5c06d6619fa092c2284c635330328d9814997
BLAKE2b-256 checksum
How to use checksums
9606b7ac1767245db40fd2c79eab1f2640c747c9067a06d5a80f947ed84b5c78
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

3 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page