Local Explorer Setup
This is the operational guide for starting the SafeLens Explorer from a fresh
checkout. The Explorer is a local FastAPI server that serves the compiled React
workbench, the artifact API, and the local job queue from one port. The normal
address is http://127.0.0.1:7860.
1. Install SafeLens
Use Python 3.10 or newer. A virtual environment keeps the Explorer dependencies separate from other research environments:
git clone https://github.com/PKU-PILLAR-Group/SafeLens.git
cd SafeLens
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[explorer,models,modelscope,sae,attribution,nla,jlens,viz]"
python -m pip install "jlens @ https://codeload.github.com/anthropics/jacobian-lens/tar.gz/581d398613e5602a5af361e1c34d3a92ea82ba8e"
The extras have deliberately separate responsibilities:
| Extra | Required for |
|---|---|
explorer |
FastAPI server, static web app, artifact API, and job queue |
models |
Real Hugging Face/Transformers model jobs |
sae |
SAELens, Hugging Face Hub, and Gemma Scope SAE loading/downloads |
attribution |
Captum Integrated Gradients |
nla |
NLA artifact loading and reconstruction jobs |
jlens |
J-Lens runtime dependencies (install the pinned jlens package separately) |
modelscope |
Optional ModelScope provider for supported Gemma 3 models |
viz |
CircuitsVis HTML bridges for notebooks and demos |
The jlens package is not published on PyPI. The jlens extra installs only its
runtime dependencies, and the pinned Anthropic build above provides the package
itself; install both lines before starting the server.
For a viewer-only install, python -m pip install -e ".[explorer]" is enough.
For the complete real-model workbench, install the command above and restart the
server after adding any missing extra. A CPU-only installation is valid; CUDA is
optional.
Check the installation before starting:
python -c "import torch; print('torch', torch.__version__); print('cuda', torch.cuda.is_available())"
safelens explorer --help
2. Start The Packaged Explorer
From the repository root, run:
safelens explorer \
--artifact-root outputs/local-explorer \
--host 127.0.0.1 \
--port 7860
The server creates the artifact directory if it does not exist and opens a
browser. On a headless machine use --no-browser:
safelens explorer --artifact-root outputs/local-explorer --no-browser
safelens-explorer and python -m SafeLens.explorer_api are equivalent entry
points. The packaged frontend does not require Node.js at runtime. Open the URL
printed by the server and confirm the API is alive:
curl -fsS http://127.0.0.1:7860/api/health
The response should contain "status":"ok". The bundled tiny-GPT-2 run is
available immediately, even when no real model has been downloaded. Files named
*.explorer.json under outputs/local-explorer are indexed in the Run Library.
Generated prompt and analysis results are written under the same artifact root.
For a background process on a local machine:
mkdir -p outputs/local-explorer
nohup safelens explorer --artifact-root outputs/local-explorer --no-browser \
> outputs/local-explorer/explorer.log 2>&1 &
echo $! > outputs/local-explorer/explorer.pid
Stop it with kill "$(cat outputs/local-explorer/explorer.pid)" after checking
that the PID belongs to the Explorer process.
3. GPU And Model Placement
Explorer jobs use SAFELENS_EXPLORER_JOB_DEVICE=auto by default. auto selects
cuda:0 when torch.cuda.is_available() is true and otherwise selects cpu.
The dtype defaults to bfloat16 on CUDA and float32 on CPU. Override these
only when the machine or model requires it:
export SAFELENS_EXPLORER_JOB_DEVICE=auto # or cpu, cuda:0, cuda:1
export SAFELENS_EXPLORER_JOB_DTYPE=bfloat16 # or float32, float16
The process must be restarted after changing these variables. A CUDA-enabled
PyTorch build and a working NVIDIA driver are required for GPU execution; a
GPU visible to the host is not enough if torch.cuda.is_available() is false.
Explorer resolves real models in this order:
- An explicit local directory from
SAFELENS_GEMMA_2_9B_IT_MODEL_PATHorSAFELENS_EXPLORER_MODEL_PATHS. - A complete local Hugging Face cache snapshot.
- The configured provider (
huggingfaceby default, or ModelScope for the supported Gemma 3 models whenmodelscopeis installed).
For an explicit model directory, set a path containing config.json, tokenizer
metadata, and all model weight shards:
export SAFELENS_GEMMA_2_9B_IT_MODEL_PATH=/data/models/gemma-2-9b-it
Alternatively leave the variable unset and select gemma-2-9b-it in the chat
model picker. With Hugging Face access configured, the first real prompt job
downloads the model into .cache/safelens/local-explorer-real-flow. Gemma
checkpoints require accepting Google's model terms and running hf auth login
when the Hub requests authentication.
4. Gemma SAE Workbench
The SAE controls are inside a conversation turn: choose the gemma-2-9b-it
chat model, run a prompt, click SAE, and then choose either Find active
features or a Neuronpedia mode. The preset selector is enabled only for a
Gemma-2-9B-it run. It contains the public Cats, Chinese, Pirate, Shakespeare,
Poetry, San Francisco, Positivity, Negativity, Music, and British English
modes. There is no separate /sae-steer page.
The Gemma-2-9B-it presets use public canonical Gemma Scope residual-stream checkpoints at L9, L20, and L31. L9 is loaded first; L20 and L31 are downloaded lazily when a preset needs them. To make the service fully offline, put all three files under one cache root:
/data/safelens-cache/gemma-scope-9b-it-res/
layer_9/width_131k/average_l0_121/params.npz
layer_20/width_131k/average_l0_81/params.npz
layer_31/width_131k/average_l0_109/params.npz
Then set:
export SAFELENS_GEMMA_SAE_CACHE=/data/safelens-cache
The L9 helper is:
python scripts/download_gemma_scope_9b_it_sae.py \
--output /data/safelens-cache/gemma-scope-9b-it-res/layer_9/width_131k/average_l0_121/params.npz
On a machine with the local Gemma model and L9 checkpoint already in the
standard paths, scripts/run_gemma_sae_demo.sh is a convenience wrapper around
the same Explorer server. It defaults to the same automatic device/dtype policy
as the main command and accepts the normal Explorer flags:
scripts/run_gemma_sae_demo.sh --port 7860
For L20 and L31, use the Hub CLI while online:
hf download google/gemma-scope-9b-it-res \
layer_20/width_131k/average_l0_81/params.npz \
--local-dir /data/safelens-cache/gemma-scope-9b-it-res
hf download google/gemma-scope-9b-it-res \
layer_31/width_131k/average_l0_109/params.npz \
--local-dir /data/safelens-cache/gemma-scope-9b-it-res
The default SAFELENS_GEMMA_SAE_DEVICE is also auto; it follows the same
CUDA-first policy. Set it explicitly only to force the standalone SAE runtime:
export SAFELENS_GEMMA_SAE_DEVICE=auto
# Leave SAFELENS_GEMMA_SAE_DTYPE unset for the automatic CPU/GPU dtype.
The official sae extra is recommended for checkpoint downloads and SAELens
compatibility. SafeLens also has a local JumpReLU loader for the official NPZ
files, so an old ModuleNotFoundError: sae_lens message usually means a stale
worker or bundle is running. Rebuild/stage the distribution and restart the
server as described in the next section.
5. Rebuild After Frontend Changes
A normal checkout already contains a packaged web bundle. Node.js is needed only when changing the React app or regenerating the bundle:
cd apps/local_explorer
npm ci
npm run build
cd ../..
python scripts/prepare_explorer_distribution.py --skip-web-build
The staging command copies apps/local_explorer/dist and all Explorer workers
into src/SafeLens/explorer_web and src/SafeLens/explorer_workers. Restart
the running safelens explorer process after staging. To build and stage in one
step, run python scripts/prepare_explorer_distribution.py from the repository
root; this invokes npm run build itself.
For frontend development, use two ports:
# terminal 1
cd apps/local_explorer
npm ci
npm run dev # http://127.0.0.1:7860
# terminal 2, from the repository root
python -m SafeLens.explorer_api \
--artifact-root outputs/local-explorer \
--web-root apps/local_explorer/dist \
--host 127.0.0.1 \
--port 7861 \
--no-browser
Vite proxies /api from port 7860 to the FastAPI process on port 7861. Do not
start the production server on 7860 at the same time as Vite.
6. Remote Or Container Use
The default bind is localhost and is intentional. Explorer has no built-in authentication or TLS. Do not expose it directly to an untrusted network. If a reverse proxy provides authentication and HTTPS, explicitly opt into a remote bind:
safelens explorer \
--artifact-root /data/safelens \
--host 0.0.0.0 \
--port 7860 \
--allow-remote \
--no-browser
The repository Dockerfile builds the frontend and starts this same packaged server. A minimal container run is:
docker build -t safelens-explorer .
docker volume create safelens-data
docker run --rm -p 127.0.0.1:7860:7860 \
-v safelens-data:/data safelens-explorer
The image is a viewer/API image. Add the real-model extras in the image build if container-side prompt, SAE, attribution, NLA, or J-Lens jobs are required.
7. Troubleshooting
| Symptom | Check/fix |
|---|---|
Built Explorer frontend not found |
Run python scripts/prepare_explorer_distribution.py or reinstall the package. |
| Page loads but no real model appears | Install .[models], check /api/prompt/options, and verify the model snapshot or Hub access. |
| Send shows no answer immediately | The first model job loads weights in a subprocess. Watch the in-page progress and server log; do not start a second server on the same port. |
Run/Find active features is disabled |
Wait for preflight to finish, select a compatible model/profile and feature, and keep the token range inside the prompt. |
SAE Lens is unavailable or No module named sae_lens |
Install .[sae] for the official loader, then rebuild/stage workers and restart. The official local NPZ fallback is also supported. |
| SAE preset is disabled | The Neuronpedia presets require google/gemma-2-9b-it; switch the chat model from Qwen, tiny-GPT-2, or Gemma 3. |
| SAE checkpoint not found | Set SAFELENS_GEMMA_SAE_CACHE to the parent containing gemma-scope-9b-it-res/, or set the explicit L9 path. Checkpoint downloads require huggingface-hub. |
| Service uses CPU unexpectedly | Run python -c "import torch; print(torch.cuda.is_available())"; install a CUDA PyTorch build, check the driver, unset a forced SAFELENS_EXPLORER_JOB_DEVICE=cpu, and restart. |
| Port 7860 is occupied | Use --port 7861 (and a matching proxy/URL), or stop the process that owns 7860 after verifying it is safe to stop. |
| Remote bind is rejected | Add --allow-remote, and put authentication/TLS in front of the service. |
Useful final checks:
curl -fsS http://127.0.0.1:7860/api/health
curl -fsS http://127.0.0.1:7860/api/prompt/options
curl -fsS 'http://127.0.0.1:7860/api/intervention/sae-profiles?modelName=google%2Fgemma-2-9b-it'
curl -fsS http://127.0.0.1:7860/api/sae-steering/config
The last two requests should show the three 9B profiles (L9/L20/L31) and the ten Neuronpedia modes when the current code and packaged bundle are loaded.
8. Verification Before Handoff
Run these checks after changing the startup path or frontend bundle:
ruff check src/SafeLens tests
python -m py_compile src/SafeLens/explorer_api.py src/SafeLens/explorer_sae.py
pytest -q
cd apps/local_explorer && npm run build
For a full package check, stage the bundle and run the wheel verification script:
cd ../..
python scripts/prepare_explorer_distribution.py --skip-web-build
python -m build
python scripts/verify_explorer_wheel.py