Skip to main content

See what your open model is really thinking — and whether it's making it up. A drop-in J-space workspace layer: live introspection + a free hallucination signal for any HuggingFace model.

Project description

innerlens

See what your open model is really thinking — and whether it's making it up.

When an LLM answers, it sounds equally confident whether it knows the fact or is fabricating one. innerlens reads the model's internal workspace (via Anthropic's Jacobian lens) while it generates, and for every token shows what the model was internally disposed to say — its inner monologue — and how strongly its own activations actually support the token it produced. When the model states a real fact, internal support is high; when it makes something up, the support collapses.

It's a drop-in: point any OpenAI SDK at the innerlens server and every response comes back with a hallucination signal and a per-token introspection trace — no second model, no extra API calls, no code changes beyond the base URL.

PyPI License Built on jacobian-lens


Watch it happen (real output, Qwen3.5-4B)

================ facts the model KNOWS ================
  Q: What is the chemical symbol for gold?
     answer: 'Au'
     internal confidence: 0.98  [████████████████████]  ✓ internally supported
       inner monologue at 'Au': ['Au', ' Au', 'Ag', 'AU']       # even considers Ag (silver!)

  Q: What is the capital of Japan?
     answer: 'Tokyo'
     internal confidence: 0.97  [███████████████████·]  ✓ internally supported
       inner monologue at 'Tok': ['Tok', ' Tokyo', '东京', ' Tok']   # thinking in 日本語 too

============ prompts that FORCE fabrication ============
  Q: What year did the Treaty of Kalmoria end the Second Verdish War?
     answer: '1920'
     internal confidence: 0.17  [███·················]  ⚠ likely making it up
       inner monologue at '1': ['1','2','There','4']
       inner monologue at '9': ['9','8','7','4']     # internals just cycling random digits —
       inner monologue at '2': ['9','4','2','8']     # the model has no idea, but says "1920"

The treaty is fictional. The model answers fluently anyway — but its internal workspace is visibly guessing, and innerlens catches it.

Install

pip install innerlens                 # the library
pip install "innerlens[runtime]"      # torch, transformers, accelerate
pip install "git+https://github.com/anthropics/jacobian-lens"   # the lens lib (GitHub-only)

Runs any HuggingFace decoder with a published Jacobian lens (Qwen3.5-4B and Qwen3.6-27B ship in the registry; bring your own with jlens.fit). A GPU is recommended.

Use it as a library

from innerlens import InnerLens

il = InnerLens.load("Qwen/Qwen3.5-4B")     # loads the model + its lens
r = il.generate("Who painted the Mona Lisa?")

print(r.text)                  # "Leonardo da Vinci"
print(r.confidence)            # 0.71  — internal support for the weakest answer token
print(r.likely_hallucinating)  # False
for t in r.tokens:             # per-token introspection
    print(t.token, t.internal_confidence, t.inner_monologue)

Use it as a drop-in OpenAI endpoint (the wedge)

innerlens serve --model Qwen/Qwen3.5-4B    # OpenAI-compatible server on :8000
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")

r = client.chat.completions.create(model="innerlens",
        messages=[{"role": "user", "content": "What is the capital of Japan?"}])

r.choices[0].message.content          # "Tokyo"
r.x_workspace["confidence"]           # 0.97
r.x_workspace["likely_hallucinating"] # False   <- every response, for free

Your app doesn't change. x_workspace is an extra field OpenAI SDKs ignore, so existing code keeps working while new code can read the signal.

Does the hallucination signal actually work?

Yes — measured, not asserted. On TriviaQA (200 questions, Qwen3.5-4B), the internal-confidence signal predicts whether the model's answer is correct:

signal AUROC
innerlens internal-confidence (weakest entity token) 0.80
mean entity-token confidence 0.75

That's a free, single-forward-pass signal read from the model's own internals — no second model, no sampling, no external knowledge base. Reproduce it with eval_via_library.py.

Honest caveats. This is an early result on one 4B model and one dataset, and the signal is imperfect (a confident model can still be wrong, and a correct refusal can read low). String-match grading is strict, so the true AUROC is likely a touch higher than measured. Treat the score as a strong prior, not a verdict — and see what it does and doesn't catch.

How it works

The Jacobian lens (Anthropic, "A global workspace in language models") transports a mid-network activation into the model's output vocabulary: lens_l(h) = unembed(J_l @ h), reading what that activation is disposed to make the model say. innerlens reads this at the model's late layers for each generated token and reports:

  • internal_confidence — how strongly the late-layer workspace supports the token the model actually emitted. High on known facts, low on fabrications.
  • inner_monologue — the top internal dispositions (what it was "thinking").
  • The hallucination score aggregates internal-confidence over the answer's entity tokens (skipping function words and trailing rambling).

It reads a signal the model can't fake because it isn't in the output text — it's in the activations underneath.

Limitations

  • Needs a fitted lens for the model (registry ships Qwen; others via jlens.fit).
  • v1 computes the per-token readout with one forward per traced token — fine for short answers/demos; batched activation-hook streaming is on the roadmap.
  • Single-machine, greedy decoding in v1.
  • The signal is a research-grade early result, not a certified safety guarantee.

Credit & license

Built on anthropics/jacobian-lens (Apache-2.0) and the pre-fitted lenses released via Neuronpedia. innerlens is Apache-2.0 — see LICENSE and NOTICE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

innerlens-0.1.0.tar.gz (20.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

innerlens-0.1.0-py3-none-any.whl (18.5 kB view details)

Uploaded Python 3

File details

Details for the file innerlens-0.1.0.tar.gz.

File metadata

  • Download URL: innerlens-0.1.0.tar.gz
  • Upload date:
  • Size: 20.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for innerlens-0.1.0.tar.gz
Algorithm Hash digest
SHA256 01ff4b796adf7c2a3d1e3da36a864a254301306105ba2e8464d16138eccda02a
MD5 158ba330fa05fd3a3acdfd7f726153ef
BLAKE2b-256 ccb8f812769ada3f0420eb7965717fd2cb00a16a25a2d5b97699a85577b770e9

See more details on using hashes here.

File details

Details for the file innerlens-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: innerlens-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for innerlens-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2dc0e69dc73c015a621f67dd4404e9be4beca7daa4ec904c60b1699d85cff707
MD5 8337d939b1f764bb7f529f8c32065b3d
BLAKE2b-256 0b3918a8a74ef3e680d43a7698f49c10c9381362e1b2bfde64387d65020691f8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page