PII round-trip for LLM APIs: anonymize before the request, de-anonymize the stream on the way back.
Project description
Detect PII in text, substitute it with stable sentinels (<PERSON_1>,
<EMAIL_2>, ...), send the redacted text to any LLM, and restore the
originals on the way back, including in streamed responses. The package
is framework-agnostic and ships a drop-in openai client wrapper for
zero-effort integration.
See the monorepo README for the cross-language overview and architecture.
Install
uv add gheim # core: pairs with a RemoteDetector or GHEIM_API_KEY
uv add "gheim[local]" # + torch and transformers for on-device detection
uv add "gheim[openai]" # + drop-in OpenAI client
uv add "gheim[local,openai]" # both
Model choice
LocalDetector runs a token-classification model in process. The
package's default model is
joelbarmettler/gheim-ch-560m
— a 560M xlm-roberta-large fine-tune optimised for Swiss-market PII
(strict-span F1 0.916 on Swiss text, see
MODEL_CARD.md).
Any HuggingFace token-classification model that emits the same 33-class
BIOES schema can be substituted via the model_id constructor arg.
| Model | Best for | Parameters | Notes |
|---|---|---|---|
joelbarmettler/gheim-ch-560m (default) |
Swiss-market text (de_CH, fr_CH, it_CH, rm, en) with CH-format account numbers (IBAN, AHV, VAT-CHE) | 560M | Apache 2.0. Test F1 0.916. |
openai/privacy-filter |
English-first or general use, long-context (up to 128k tokens) | 1.4B (50M active, MoE) | Apache 2.0. Wider language coverage, larger weights. |
from gheim import LocalDetector
# Default — Swiss-tuned, 560M:
det = LocalDetector()
# Alternative for English or general use:
det = LocalDetector(model_id="openai/privacy-filter")
Drop-in OpenAI client
from gheim.openai import OpenAI
client = OpenAI() # same constructor 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>".
Custom endpoint or key (e.g. OpenRouter, local vLLM):
client = OpenAI(api_key="sk-or-...", base_url="https://openrouter.ai/api/v1")
Streaming:
stream = client.chat.completions.create(..., stream=True)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="", flush=True)
Async:
from gheim.openai import AsyncOpenAI
client = AsyncOpenAI()
r = await client.chat.completions.create(...)
Per-call overrides:
from gheim import Session
session = Session() # reuse across calls for multi-turn coherent sentinels
r = client.chat.completions.create(
model="gpt-4o",
messages=[...],
gheim_session=session, # or gheim_detector=...
)
Framework-agnostic
from gheim import Session, LocalDetector, anonymize_text, deanonymize_text
session = Session(detector=LocalDetector()) # gheim-ch-560m by default
clean = anonymize_text("Hi, my name is Joel", session)
# ... call any LLM with clean ...
final = deanonymize_text(response_text, session)
Streaming deanonymizer:
from gheim import deanonymize_stream
for chunk in deanonymize_stream(my_chunk_iterator, session):
print(chunk, end="", flush=True)
Chat-message helpers:
from gheim import anonymize_messages
redacted = anonymize_messages(messages, session) # preserves role, name, tool_call_id
Wrapped endpoints
The drop-in OpenAI / AsyncOpenAI clients automatically protect every
text-carrying endpoint: chat.completions, responses, completions (legacy),
embeddings, moderations, audio.speech, audio.transcriptions,
audio.translations, images.generate, images.edit. Tool-call arguments and
SSE delta chunks are restored on the way back. See the
monorepo README for the full
coverage matrix and the embeddings caveat.
Strict mode
gheim_strict=True (default) raises RuntimeError if you call an unwrapped
endpoint (beta.assistants, batches, files, uploads, fine_tuning,
vector_stores). The error message names client.raw.<path> as the documented
escape hatch.
client = OpenAI(gheim_strict=False) # downgrade to one-time warnings
client.raw.beta.assistants.create(...) # always works regardless of strict mode
Detector backends
import torch
from gheim import LocalDetector, RemoteDetector, default_detector
# Local inference. Weights download to the HF cache on first use.
# `model_id` defaults to "joelbarmettler/gheim-ch-560m"; pass
# `dtype=torch.bfloat16` for half-precision GPU inference.
det = LocalDetector(device="auto", dtype=torch.bfloat16)
# Remote inference against your own gheim-server or api.gheim.ch.
det = RemoteDetector(base_url="http://your-host:8080", api_key="...")
# default_detector() picks remote if GHEIM_API_KEY is set, else local.
det = default_detector()
Composite detector (recommended for production)
For categories where structure is verifiable by checksum (CH-IBAN, AHV,
VAT-CHE, credit cards, common token formats) the package ships a regex
catalogue under gheim.detectors.composite that pairs with the model
detector. The composite detector applies regex first, masks matched
spans, then runs the model on the remainder. This pushes effective
recall on account_number, private_phone, and private_url close
to 1.0 with high precision; the underlying ML model handles person
names, addresses, and dates.
License
Apache 2.0. Bundled model weights are inherited from the upstream license of the model you select.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gheim-0.1.5.tar.gz.
File metadata
- Download URL: gheim-0.1.5.tar.gz
- Upload date:
- Size: 38.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
beec744435b640babd882d41a7612832785080308dc2ca0ce32780e35647b3c7
|
|
| MD5 |
5ea4912c8a9bc841b7e7bd1d74650689
|
|
| BLAKE2b-256 |
cd5618b37b2f5a15832581424cbd11817b72b0e69c7099cbbf26faa8e0361a42
|
Provenance
The following attestation bundles were made for gheim-0.1.5.tar.gz:
Publisher:
release.yml on joelbarmettlerUZH/gheim
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gheim-0.1.5.tar.gz -
Subject digest:
beec744435b640babd882d41a7612832785080308dc2ca0ce32780e35647b3c7 - Sigstore transparency entry: 1518153635
- Sigstore integration time:
-
Permalink:
joelbarmettlerUZH/gheim@690c24f6f7855c219d796faec5443a3d0d843d95 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/joelbarmettlerUZH
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@690c24f6f7855c219d796faec5443a3d0d843d95 -
Trigger Event:
push
-
Statement type:
File details
Details for the file gheim-0.1.5-py3-none-any.whl.
File metadata
- Download URL: gheim-0.1.5-py3-none-any.whl
- Upload date:
- Size: 50.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72a984a17062ddaa54fc0f9307d81193ae545350948418082e32a898a6bf1401
|
|
| MD5 |
b31dd174056c68d0b5d434776191b67c
|
|
| BLAKE2b-256 |
dca9714597d24813c4c9e20bb305f324ec9019c79972e8ba83ffb801cdafc996
|
Provenance
The following attestation bundles were made for gheim-0.1.5-py3-none-any.whl:
Publisher:
release.yml on joelbarmettlerUZH/gheim
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gheim-0.1.5-py3-none-any.whl -
Subject digest:
72a984a17062ddaa54fc0f9307d81193ae545350948418082e32a898a6bf1401 - Sigstore transparency entry: 1518154024
- Sigstore integration time:
-
Permalink:
joelbarmettlerUZH/gheim@690c24f6f7855c219d796faec5443a3d0d843d95 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/joelbarmettlerUZH
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@690c24f6f7855c219d796faec5443a3d0d843d95 -
Trigger Event:
push
-
Statement type: