Metadata-Version: 2.5
Name: piighost
Version: 2.1.0
Summary: piighost replaces personal data with placeholders before the LLM sees it, then restores the real values in the reply and in tool calls.
Project-URL: Homepage, https://piighost.dev/en
Project-URL: Documentation, https://docs.piighost.dev/en/guide/
Project-URL: Repository, https://github.com/Athroniaeth/piighost
Project-URL: Issues, https://github.com/Athroniaeth/piighost/issues
Project-URL: Changelog, https://github.com/Athroniaeth/piighost/blob/master/CHANGELOG.md
Author: Athroniaeth
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai,anonymization,anthropic,claude-code,data-masking,data-privacy,de-identification,gdpr,gliner,guardrails,hipaa,langchain,langgraph,llamaindex,llm,llm-security,ner,nlp,openai,pii,pii-detection,pii-masking,pii-redaction,presidio,privacy,pseudonymization,pydantic-ai,redaction,rgpd,spacy
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Natural Language :: English
Classifier: Natural Language :: French
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: typing-extensions>=4.16.0
Provides-Extra: all
Requires-Dist: aiosqlite>=0.19; extra == 'all'
Requires-Dist: argon2-cffi>=23.1; extra == 'all'
Requires-Dist: asyncpg>=0.29; extra == 'all'
Requires-Dist: cryptography>=44; extra == 'all'
Requires-Dist: gliner2>=2; extra == 'all'
Requires-Dist: httpx>=0.28; extra == 'all'
Requires-Dist: langchain>=1.2; extra == 'all'
Requires-Dist: llama-index-core>=0.14; extra == 'all'
Requires-Dist: mistralai>=1.0; extra == 'all'
Requires-Dist: opentelemetry-api>=1.30; extra == 'all'
Requires-Dist: peft<1,>=0.10; extra == 'all'
Requires-Dist: presidio-analyzer>=2.2; extra == 'all'
Requires-Dist: pydantic-ai>=2.0; extra == 'all'
Requires-Dist: pydantic-settings>=2.0; extra == 'all'
Requires-Dist: pydantic>=2.6; extra == 'all'
Requires-Dist: rapidfuzz>=3.9; extra == 'all'
Requires-Dist: redis>=5; extra == 'all'
Requires-Dist: spacy>=3.7; extra == 'all'
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'all'
Requires-Dist: torch<3,>=2.1; extra == 'all'
Requires-Dist: torch>=2.0; extra == 'all'
Requires-Dist: transformers>=4.40; extra == 'all'
Requires-Dist: transformers>=5; extra == 'all'
Requires-Dist: typer>=0.12; extra == 'all'
Provides-Extra: argon2
Requires-Dist: argon2-cffi>=23.1; extra == 'argon2'
Provides-Extra: client
Requires-Dist: httpx>=0.28; extra == 'client'
Provides-Extra: config
Requires-Dist: pydantic-settings>=2.0; extra == 'config'
Requires-Dist: pydantic>=2.6; extra == 'config'
Requires-Dist: typer>=0.12; extra == 'config'
Provides-Extra: crypto
Requires-Dist: cryptography>=44; extra == 'crypto'
Provides-Extra: fuzzy
Requires-Dist: rapidfuzz>=3.9; extra == 'fuzzy'
Provides-Extra: gliner2
Requires-Dist: gliner2>=2; extra == 'gliner2'
Requires-Dist: peft<1,>=0.10; extra == 'gliner2'
Requires-Dist: torch<3,>=2.1; extra == 'gliner2'
Requires-Dist: transformers>=5; extra == 'gliner2'
Provides-Extra: langchain
Requires-Dist: langchain>=1.2; extra == 'langchain'
Provides-Extra: llama-index
Requires-Dist: llama-index-core>=0.14; extra == 'llama-index'
Provides-Extra: llm
Requires-Dist: langchain>=1.2; extra == 'llm'
Provides-Extra: mistral
Requires-Dist: mistralai>=1.0; extra == 'mistral'
Provides-Extra: observation
Requires-Dist: opentelemetry-api>=1.30; extra == 'observation'
Provides-Extra: presidio
Requires-Dist: presidio-analyzer>=2.2; extra == 'presidio'
Provides-Extra: pydantic-ai
Requires-Dist: pydantic-ai>=2.0; extra == 'pydantic-ai'
Provides-Extra: redis
Requires-Dist: redis>=5; extra == 'redis'
Provides-Extra: spacy
Requires-Dist: spacy>=3.7; extra == 'spacy'
Provides-Extra: sqlalchemy
Requires-Dist: aiosqlite>=0.19; extra == 'sqlalchemy'
Requires-Dist: asyncpg>=0.29; extra == 'sqlalchemy'
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'sqlalchemy'
Provides-Extra: transformers
Requires-Dist: torch>=2.0; extra == 'transformers'
Requires-Dist: transformers>=4.40; extra == 'transformers'
Description-Content-Type: text/markdown

# piighost

English | [Français](https://github.com/Athroniaeth/piighost/blob/master/docs/README.fr.md)

[![CI](https://github.com/Athroniaeth/piighost/actions/workflows/ci.yml/badge.svg)](https://github.com/Athroniaeth/piighost/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/Athroniaeth/piighost/branch/master/graph/badge.svg)](https://codecov.io/gh/Athroniaeth/piighost)
[![PyPI version](https://img.shields.io/pypi/v/piighost.svg)](https://pypi.org/project/piighost/)
[![Python versions](https://img.shields.io/pypi/pyversions/piighost.svg)](https://pypi.org/project/piighost/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Athroniaeth/piighost/blob/master/LICENSE)
[![Security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
[![Discord](https://img.shields.io/badge/Discord-join-5865F2?logo=discord&logoColor=white)](https://discord.gg/vFg9GHQR2s)

`piighost` replaces personal data with placeholders before the LLM sees it, then restores the real values in the reply and in tool calls. This Python library de-identifies confidential data, meaning personal data (PII) and secrets, through a reversible pseudonymization often called anonymization. LangChain, Pydantic AI, LlamaIndex and Claude Code integrations are provided, together with an OpenAI and Anthropic API connector.

This de-identification spots confidential data with pluggable detectors (regex, NER, LLM) and replaces each value with a placeholder, the token that takes its place. For example:

- `John Doe` becomes `<<PERSON:1>>`
- `john.doe@example.com` becomes `<<EMAIL:1>>`

This placeholder stays the same from one message to the next with the conversational pipeline, which keeps the mapping between a value and its placeholder across the whole conversation. If `john.doe@example.com` reappears three messages later, the placeholder is still `<<EMAIL:1>>`, which lets the LLM follow the thread.

The LLM therefore only receives de-identified text. When it returns placeholders, for example by answering `Hello <<PERSON:1>>`, `piighost` replaces them with the real values. The user sees `John Doe` and never sees the de-identification.

The same mechanism protects agents that call tools. With the LangChain middleware, a tool that needs the real email address receives it in clear, while the LLM that supplies it only writes `<<EMAIL:1>>`.

<p align="center">
  <img alt="A user chats with an agent, confidential values are replaced by placeholders before reaching the LLM and restored afterwards for the user and for tool calls." src="https://raw.githubusercontent.com/Athroniaeth/piighost/master/docs/assets/deid-chat-dark.gif" width="760">
</p>

*The LLM only sees placeholders. The tool receives the real address, the user gets a clear-text reply, and your agent code stays the same.*

> [!NOTE]
> This retained mapping makes the de-identification a pseudonymization under the GDPR, not a definitive anonymization. With the conversational pipeline, the real values stay stored for the duration of the conversation and must be protected accordingly.

## Quickstart

```bash
pip install piighost   # or: uv add piighost
```

`ExactMatchDetector` de-identifies a dictionary of known values, with no model to download.

```python
import asyncio

from piighost.components.detector import ExactMatchDetector
from piighost.pipeline import AnonymizationPipeline

detector = ExactMatchDetector({"John Doe": "PERSON", "john.doe@example.com": "EMAIL"})
pipeline = AnonymizationPipeline(detector)

result = asyncio.run(pipeline.anonymize("Write to John Doe at john.doe@example.com."))
print(result.text)  # Write to <<PERSON:1>> at <<EMAIL:1>>.
```

The [Quickstart](https://docs.piighost.dev/en/guide/getting-started/quickstart/) goes on with a real detector and a conversation.

## Go further

- **Start**: [installation](https://docs.piighost.dev/en/guide/getting-started/installation/), [first pipeline](https://docs.piighost.dev/en/guide/getting-started/first-pipeline/), [conversational pipeline](https://docs.piighost.dev/en/guide/getting-started/conversation/)
- **Configure**: [a pipeline in a TOML file](https://docs.piighost.dev/en/guide/getting-started/configuration/), [pattern groups from the catalog](https://docs.piighost.dev/en/guide/reference/detectors/#catalog-groups), [every config key](https://docs.piighost.dev/en/guide/configuration/toml/)
- **Integrate**: [LangChain](https://docs.piighost.dev/en/guide/examples/langchain/), [Pydantic AI](https://docs.piighost.dev/en/guide/examples/pydantic-ai/), [LlamaIndex](https://docs.piighost.dev/en/guide/examples/llama-index/), [Claude Code](https://docs.piighost.dev/en/guide/examples/claude-code/), [a remote piighost-api](https://docs.piighost.dev/en/guide/getting-started/api-client/)
- **Deploy**: [an API server from a catalog configuration](https://docs.piighost.dev/en/guide/getting-started/api-server/), [a production thread pipeline](https://docs.piighost.dev/en/guide/deployment/), [several instances](https://docs.piighost.dev/en/guide/multi-instance/)
- **Understand**: [why de-identify](https://docs.piighost.dev/en/guide/why-anonymize/), [architecture](https://docs.piighost.dev/en/guide/architecture/), [security](https://docs.piighost.dev/en/guide/security/), [GDPR compliance](https://docs.piighost.dev/en/guide/compliance/), [limitations](https://docs.piighost.dev/en/guide/limitations/), [detection, measured](https://docs.piighost.dev/en/guide/benchmark/), [how it compares](https://docs.piighost.dev/en/guide/comparison/)
- **Upgrade**: [versions and the move to 2.0](https://docs.piighost.dev/en/guide/community/upgrading/)

## Ecosystem

- **[piighost.dev](https://piighost.dev/en)**: the project website, with the ecosystem and its philosophy
- **[piighost catalog](https://catalog.piighost.dev)**: reviewed regex groups and ready-made pipeline configurations, pulled by reference
- **[piighost-api](https://github.com/Athroniaeth/piighost-api)**: a server hosting one pipeline behind HTTP, with OpenAI- and Anthropic-compatible proxies
- **[piighost-chat](https://github.com/Athroniaeth/piighost-chat)**: an example chat interface with human-in-the-loop
- **[caviardage](https://caviardage.piighost.dev/)**: a browser app that redacts a Word deed, with piighost running in Pyodide. Nothing of the document leaves the tab

## Project

- **Community**: [Discord](https://discord.gg/vFg9GHQR2s) to get help, report bugs and request features
- **Contributing**: [contribution guide](https://docs.piighost.dev/en/guide/community/contributing/) and [bug reports](https://docs.piighost.dev/en/guide/community/bug-reports/)
- **License**: [MIT](https://github.com/Athroniaeth/piighost/blob/master/LICENSE)

If `piighost` is useful to you, a [star on GitHub](https://github.com/Athroniaeth/piighost) helps other developers find it.
