PII round-trip · Apache 2.0 · runs on your device
Anonymise
PII before it leaves
your device.
gheim detects names, addresses, phones, IBANs and more across the four official Swiss languages plus English, swaps them for stable sentinels before you call any LLM, and restores the originals when the response comes back — even mid-stream.
§ 01 — Live demo
Type something. Watch the model
redact it in real time.
Inference runs entirely on your device via @huggingface/transformers on WebGPU (or WASM). No text leaves this page. The model is fetched once from Hugging Face and cached.
Model not loaded
First run downloads ~280 MB from Hugging Face
Cached in IndexedDB after that. Roughly 30–60 s on broadband.
§ 02 — How it works
Four moves between you
and any remote model.
gheim is framework-agnostic. It plugs into the OpenAI Python and Node
SDKs as a drop-in client, and it works against any HTTP LLM you call
yourself. The detection model is yours to swap — the Swiss-tuned
gheim-ch-560m is the
default, and any openai/privacy-filter
-compatible token-classification model drops in.
- 01
Detect
A token-classification model scans the prompt for names, addresses, phones, IBANs, dates, URLs, emails, and secrets across five languages.
- 02
Substitute
Each span is replaced with a stable sentinel — <PERSON_1>, <EMAIL_1>, <ACCOUNT_1> — that the LLM treats as opaque tokens.
- 03
Send
The redacted prompt goes to whichever LLM you use. Streaming, function calls, multi-turn chat — all transparent.
- 04
Restore
On the way back, every sentinel in the response is rewritten to the original value. Works mid-stream so the user sees Joel, never <PERSON_1>.
Round-trip in one paragraph
Please draft a polite reminder to Joel Barmettler (jbarmettler@proton.me) that invoice CH9300762011623852957 is overdue since 15 May 2026.
Please draft a polite reminder to <PERSON_1> (<EMAIL_1>) that invoice <ACCOUNT_1> is overdue since <DATE_1>.
Subject: Friendly reminder. Dear <PERSON_1>, this is a friendly reminder that invoice <ACCOUNT_1> has been outstanding since <DATE_1>. We will follow up by email at <EMAIL_1>.
Subject: Friendly reminder. Dear Joel Barmettler, this is a friendly reminder that invoice CH9300762011623852957 has been outstanding since 15 May 2026. We will follow up by email at jbarmettler@proton.me.
§ 03 — Install
Three minutes from
pip install to redacted prompts.
The Python and Node packages ship a drop-in replacement for the official OpenAI client. Anonymisation and round-trip restoration happen automatically — including in streamed responses. Running a LiteLLM proxy? Use the guardrail plugin instead.
Python
# Drop-in OpenAI wrapper + on-device detection uv add "gheim[local,openai]" # Or, if you'll point gheim at a remote detection server: uv add "gheim[openai]"
from gheim.openai import OpenAI client = OpenAI() # same args as openai.OpenAI r = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hi, my name is Joel"}], ) # r.choices[0].message.content contains "Joel". # OpenAI only ever saw <PERSON_1>.
Node / Bun
# Server-side / Bun / Node 18+ npm install gheim openai @huggingface/transformers # Or with bun: bun add gheim openai @huggingface/transformers
import { OpenAI } from "gheim/openai"; const client = new OpenAI(); const r = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "Hi, my name is Joel" }], }); // r.choices[0].message.content contains "Joel". // OpenAI only ever saw <PERSON_1>.
LiteLLM
# Your own LiteLLM (venv, VM, pod) pip install "gheim[litellm,local]" # Official LiteLLM Docker image: extend it. # Its virtualenv has no pip, so bootstrap it. FROM ghcr.io/berriai/litellm:main-stable RUN /app/.venv/bin/python -m ensurepip \ && /app/.venv/bin/python -m pip install \ --extra-index-url https://download.pytorch.org/whl/cpu \ "gheim[litellm,local]" # Build, then keep the ~2.2 GB model on a volume or the # container re-downloads it on every start: docker build -t litellm-gheim . docker run -p 4000:4000 -v $PWD/config.yaml:/app/config.yaml \ -v gheim-hf-cache:/root/.cache/huggingface \ litellm-gheim --config /app/config.yaml # No torch in the proxy? Install "gheim[litellm]" and # point it at a gheim-server container (see README).
guardrails: - guardrail_name: gheim-pii litellm_params: guardrail: gheim.litellm.GheimGuardrail mode: pre_call # post_call is added for you default_on: true # Every knob is plain YAML: # detector: composite | remote | local # model_detector: remote # model in a gheim-server container # api_base: http://gheim:8080 # o_bias: 0.5 # propagate_names: true # custom_patterns: # - label: case_id # regex: 'Dossier\s+(ZH-\d{4}-\d{5})' # group: 1 # Every provider behind the proxy only sees # <PERSON_1>; your clients get "Joel" back.
Browser
# Bundle for the browser (Vite, Webpack, etc.) npm install gheim @huggingface/transformers
import { Session, LocalDetector, anonymizeText, deanonymizeText } from "gheim"; const session = new Session(); session.detector = new LocalDetector({ // Defaults: model = joelbarmettler/gheim-ch-560m, // device = "auto" (probes WebGPU, falls back to WASM). dtype: "q8", }); const clean = await anonymizeText("Hi, my name is Joel", session); // → "Hi, my name is <PERSON_1>" // ...send `clean` to any LLM endpoint, then: const final = deanonymizeText(responseText, session);
Full reference: README on GitHub.