nomos-ai 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nomos_ai-0.1.0/.claude/settings.local.json +8 -0
- nomos_ai-0.1.0/.gitignore +9 -0
- nomos_ai-0.1.0/CLAUDE.md +62 -0
- nomos_ai-0.1.0/NomosAI-Anonymisation-UserGuide.md +294 -0
- nomos_ai-0.1.0/PKG-INFO +300 -0
- nomos_ai-0.1.0/README.md +285 -0
- nomos_ai-0.1.0/TODO.md +21 -0
- nomos_ai-0.1.0/anonymize.py +367 -0
- nomos_ai-0.1.0/deanonymize.py +284 -0
- nomos_ai-0.1.0/docs/superpowers/plans/2026-04-25-pypi-distribution.md +532 -0
- nomos_ai-0.1.0/docs/superpowers/specs/2026-04-25-pypi-distribution-design.md +147 -0
- nomos_ai-0.1.0/pyproject.toml +26 -0
- nomos_ai-0.1.0/src/nomosai/__init__.py +1 -0
- nomos_ai-0.1.0/src/nomosai/engine.py +314 -0
- nomos_ai-0.1.0/src/nomosai/formatter.py +198 -0
- nomos_ai-0.1.0/src/nomosai/readers.py +243 -0
- nomos_ai-0.1.0/src/nomosai/recognizers_fr.py +309 -0
- nomos_ai-0.1.0/src/nomosai/server.py +580 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(grep -h \"^import\\\\|^from\" /c/Users/corla/Desktop/projects/NomosAI/*.py /c/Users/corla/Desktop/projects/NomosAI/src/*.py)",
|
|
5
|
+
"Bash(grep -h \"except ImportError\\\\|sys.exit.*pip install\" /c/Users/corla/Desktop/projects/NomosAI/src/*.py)"
|
|
6
|
+
]
|
|
7
|
+
}
|
|
8
|
+
}
|
nomos_ai-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
NomosAI is a CLI document anonymization tool for legal/compliance use cases. It detects and replaces PII in documents (DOCX, PDF, TXT, MD, RST) and outputs anonymized Markdown files with GDPR-compliant metadata. The project is Python 3, French-first, with no build system or test suite.
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install -r requirements.txt
|
|
13
|
+
python -m spacy download fr_core_news_lg # required for French (default)
|
|
14
|
+
python -m spacy download en_core_web_lg # optional for English
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Running the Tool
|
|
18
|
+
|
|
19
|
+
A conda env is installed on this machine to run the python scripts in this project.
|
|
20
|
+
Conda env name nomosai
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Single file
|
|
24
|
+
python anonymize.py report.pdf
|
|
25
|
+
|
|
26
|
+
# Specify output
|
|
27
|
+
python anonymize.py contract.docx --output contract_redacted.md
|
|
28
|
+
|
|
29
|
+
# Batch (recursive directory)
|
|
30
|
+
python anonymize.py dossier/ --recursive --language fr --output dossier_anonymise/
|
|
31
|
+
|
|
32
|
+
# Limit entity types
|
|
33
|
+
python anonymize.py notes.txt --entities PERSON EMAIL_ADDRESS PHONE_NUMBER
|
|
34
|
+
|
|
35
|
+
# Adjust confidence threshold (default 0.55)
|
|
36
|
+
python anonymize.py file.pdf --score-threshold 0.6
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
The pipeline is: **Reader → Engine → Formatter → Output file**
|
|
42
|
+
|
|
43
|
+
1. **`src/readers.py`** — Converts DOCX/PDF/TXT/MD/RST into a list of `Block` dataclasses (`type`, `text`, `cells`). Block types: `heading1/2/3`, `paragraph`, `table_row`, `list_item`, `hr`.
|
|
44
|
+
|
|
45
|
+
2. **`src/engine.py`** — Core anonymization logic.
|
|
46
|
+
- `DocumentAnonymizer` wraps the Microsoft Presidio analyzer (backed by spaCy NLP).
|
|
47
|
+
- Replaces PII with numbered placeholders (`[PERSON_1]`, `[EMAIL_2]`) — the same PII value always maps to the same placeholder within a document.
|
|
48
|
+
- French-specific recognizers are injected into the registry when `language=="fr"`.
|
|
49
|
+
- `_HIGH_CONFIDENCE_ONLY` set (`FR_SIREN`, `FR_CNI`, `FR_POSTAL_CODE`) requires score ≥ 0.70 regardless of the `--score-threshold` flag.
|
|
50
|
+
|
|
51
|
+
3. **`src/recognizers_fr.py`** — Custom regex recognizers for French PII: NIR (sécu), SIRET, SIREN, TVA, CNI, passport, driving license, postal codes, French phone numbers. Each is a `PatternRecognizer` registered for `language="fr"`.
|
|
52
|
+
|
|
53
|
+
4. **`src/formatter.py`** — Renders anonymized blocks as Markdown with a YAML frontmatter (source, timestamp, language, entity counts, GDPR flag) and a RGPD compliance footer.
|
|
54
|
+
|
|
55
|
+
5. **`anonymize.py`** — CLI entry point; parses args, wires up engine + reader + formatter, handles single-file and batch modes.
|
|
56
|
+
|
|
57
|
+
## Key Implementation Notes
|
|
58
|
+
|
|
59
|
+
- **Presidio is the detection backbone**: adding a new entity type means writing a `PatternRecognizer` or `EntityRecognizer` subclass in `recognizers_fr.py` and registering it.
|
|
60
|
+
- **Replacement order matters**: `engine.py` processes Presidio results in reverse position order to avoid index drift during in-place string substitution.
|
|
61
|
+
- **Batch mode** generates a GDPR summary report alongside the anonymized files.
|
|
62
|
+
- **No config file**: all defaults live in `anonymize.py` (default language `fr`, default threshold `0.55`).
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# NomosAI — Guide d'utilisation (Serveur MCP)
|
|
2
|
+
|
|
3
|
+
NomosAI expose ses capacités d'anonymisation via un serveur MCP (Model Context Protocol),
|
|
4
|
+
ce qui permet à un agent LLM (Claude Code, Claude Desktop) d'anonymiser et de restaurer
|
|
5
|
+
des documents directement, sans passer par le CLI.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Prérequis
|
|
10
|
+
|
|
11
|
+
**Première installation (une seule fois par machine) — installer `uv` :**
|
|
12
|
+
|
|
13
|
+
- **Windows** — ouvre PowerShell et colle :
|
|
14
|
+
```
|
|
15
|
+
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
16
|
+
```
|
|
17
|
+
- **Mac** — ouvre Terminal et colle :
|
|
18
|
+
```bash
|
|
19
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`uv` gère automatiquement Python et toutes les dépendances (spaCy, Presidio, modèle
|
|
23
|
+
français `fr_core_news_lg`). Aucun conda, aucun `pip install`, aucun `spacy download`
|
|
24
|
+
n'est nécessaire.
|
|
25
|
+
|
|
26
|
+
> **Windows — important :** après l'installation, `uv` et `uvx` ne sont pas disponibles
|
|
27
|
+
> dans le terminal déjà ouvert. **Ferme et rouvre un nouveau terminal** (PowerShell ou
|
|
28
|
+
> cmd) avant de continuer — sans ça, toutes les commandes suivantes échoueront avec
|
|
29
|
+
> *"n'est pas reconnu comme commande"*.
|
|
30
|
+
|
|
31
|
+
> **Première utilisation :** au premier démarrage, `uvx` télécharge automatiquement
|
|
32
|
+
> le modèle spaCy `fr_core_news_lg` (~550 Mo) et installe ~80 paquets — cela prend
|
|
33
|
+
> **2 à 5 minutes** selon la connexion. Les démarrages suivants sont instantanés.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 1. Brancher à Claude Desktop (recommandé)
|
|
38
|
+
|
|
39
|
+
Édite le fichier de configuration de Claude Desktop :
|
|
40
|
+
|
|
41
|
+
- **Windows :** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
42
|
+
- **Mac :** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
43
|
+
|
|
44
|
+
Ajoute (ou crée) ce contenu :
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"nomos-ai": {
|
|
50
|
+
"command": "uvx",
|
|
51
|
+
"args": ["nomos-ai"]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Redémarre Claude Desktop. Le serveur apparaît dans la liste des outils (icône marteau).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. Brancher à Claude Code
|
|
62
|
+
|
|
63
|
+
### Étape 1 — Enregistrer le serveur
|
|
64
|
+
|
|
65
|
+
Ouvre un terminal **dans le dossier du projet** et exécute cette commande **une seule fois** :
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
claude mcp add nomos-ai -- uvx --from "C:\chemin\vers\NomosAI" nomos-ai
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Remplace `C:\chemin\vers\NomosAI` par le chemin absolu du dossier cloné.
|
|
72
|
+
Exemple : `C:\Users\prenom\Desktop\projects\NomosAI`.
|
|
73
|
+
|
|
74
|
+
### Étape 2 — Pré-chauffer l'environnement (première fois uniquement)
|
|
75
|
+
|
|
76
|
+
La première fois, `uvx` doit télécharger les dépendances (~550 Mo). Lance manuellement
|
|
77
|
+
le serveur une fois pour déclencher ce téléchargement **avant** de faire la vérification :
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
uvx --from "C:\chemin\vers\NomosAI" nomos-ai
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Attends que les logs s'affichent (`INFO Loaded recognizer: ...`), puis interromps
|
|
84
|
+
avec `Ctrl+C`. Le cache est maintenant prêt.
|
|
85
|
+
|
|
86
|
+
### Étape 3 — Vérifier la connexion
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
claude mcp list
|
|
90
|
+
# → nomos-ai uvx --from ... ✓ Connected
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
> **"Failed to connect" à l'étape 3 ?** C'est normal si l'étape 2 n'est pas terminée
|
|
94
|
+
> (téléchargement encore en cours). Attend la fin du téléchargement et relance
|
|
95
|
+
> `claude mcp list`.
|
|
96
|
+
|
|
97
|
+
Le serveur démarre automatiquement quand Claude Code en a besoin.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 3. Tester les outils manuellement (inspecteur MCP)
|
|
102
|
+
|
|
103
|
+
Pour explorer et tester les outils interactivement :
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npx @modelcontextprotocol/inspector uvx nomos-ai
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Ouvre ensuite `http://localhost:5173` dans un navigateur.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 4. Anonymiser du texte brut
|
|
114
|
+
|
|
115
|
+
Demande à l'agent (dans Claude Code ou Claude Desktop) :
|
|
116
|
+
|
|
117
|
+
> *« Anonymise ce texte : "Le Dr Jean Dupont, joignable au 01 23 45 67 89, travaille
|
|
118
|
+
> à la clinique Saint-Louis. Son numéro de sécu est 1 85 06 75 123 456 78." »*
|
|
119
|
+
|
|
120
|
+
L'agent appelle l'outil `anonymize_text` et retourne dans sa réponse :
|
|
121
|
+
|
|
122
|
+
- le **texte anonymisé** avec les placeholders (`[PERSONNE_1]`, `[TELEPHONE_1]`…)
|
|
123
|
+
- les **statistiques** de détection par type d'entité
|
|
124
|
+
- l'**index complet** des remplacements (placeholder → valeur originale)
|
|
125
|
+
|
|
126
|
+
### Paramètres disponibles
|
|
127
|
+
|
|
128
|
+
| Paramètre | Défaut | Description |
|
|
129
|
+
|-----------|--------|-------------|
|
|
130
|
+
| `text` | — | Texte à anonymiser |
|
|
131
|
+
| `language` | `"fr"` | Langue NLP : `fr`, `en`, `de`, `es`, `it`, `nl`, `pt` |
|
|
132
|
+
| `entities` | tous | Limiter à certains types, ex. `["PERSON", "EMAIL_ADDRESS"]` |
|
|
133
|
+
| `score_threshold` | `0.55` | Seuil de confiance (0.0–1.0) |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 5. Anonymiser un fichier
|
|
138
|
+
|
|
139
|
+
Formats supportés : `.docx`, `.pdf`, `.txt`, `.md`, `.rst`
|
|
140
|
+
|
|
141
|
+
Demande à l'agent :
|
|
142
|
+
|
|
143
|
+
> *« Anonymise le fichier `C:\dossiers\contrat.pdf` »*
|
|
144
|
+
|
|
145
|
+
ou avec options :
|
|
146
|
+
|
|
147
|
+
> *« Anonymise `C:\dossiers\contrat.pdf` en français, uniquement les noms et e-mails,
|
|
148
|
+
> et écris le résultat dans `C:\dossiers\anonymise\contrat.md` »*
|
|
149
|
+
|
|
150
|
+
L'agent appelle `anonymize_file` et génère deux fichiers :
|
|
151
|
+
|
|
152
|
+
| Fichier | Contenu | Confidentialité |
|
|
153
|
+
|---------|---------|-----------------|
|
|
154
|
+
| `contrat_anonymise.md` | Document anonymisé, partageable | Public |
|
|
155
|
+
| `contrat_anonymise-index.md` | Mapping placeholder → valeur originale | **Confidentiel** |
|
|
156
|
+
|
|
157
|
+
### Paramètres disponibles
|
|
158
|
+
|
|
159
|
+
| Paramètre | Défaut | Description |
|
|
160
|
+
|-----------|--------|-------------|
|
|
161
|
+
| `file_path` | — | Chemin absolu du fichier source |
|
|
162
|
+
| `output_path` | `<nom>_anonymise.md` | Chemin du `.md` de sortie |
|
|
163
|
+
| `language` | `"fr"` | Langue NLP |
|
|
164
|
+
| `entities` | tous | Types d'entités à détecter (voir section 7) |
|
|
165
|
+
| `score_threshold` | `0.55` | Seuil de confiance |
|
|
166
|
+
| `write_index` | `true` | Générer le fichier confidentiel `-index.md` |
|
|
167
|
+
|
|
168
|
+
> **Important :** Conserve le fichier `-index.md` si tu veux pouvoir restaurer le document
|
|
169
|
+
> original ultérieurement. Sans lui, la désanonymisation est impossible.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 6. Désanonymiser un document
|
|
174
|
+
|
|
175
|
+
Demande à l'agent :
|
|
176
|
+
|
|
177
|
+
> *« Restaure le fichier `C:\dossiers\contrat_anonymise.md` »*
|
|
178
|
+
|
|
179
|
+
L'agent appelle `deanonymize_file`. Il cherche automatiquement `contrat_anonymise-index.md`
|
|
180
|
+
dans le même dossier et génère `contrat_restaure.md`.
|
|
181
|
+
|
|
182
|
+
Si l'index est dans un emplacement différent :
|
|
183
|
+
|
|
184
|
+
> *« Restaure `C:\dossiers\contrat_anonymise.md` avec l'index
|
|
185
|
+
> `C:\archives\contrat_anonymise-index.md` »*
|
|
186
|
+
|
|
187
|
+
### Paramètres disponibles
|
|
188
|
+
|
|
189
|
+
| Paramètre | Défaut | Description |
|
|
190
|
+
|-----------|--------|-------------|
|
|
191
|
+
| `anonymized_file_path` | — | Chemin du `.md` anonymisé |
|
|
192
|
+
| `index_path` | auto-détecté | Chemin de l'index si non standard |
|
|
193
|
+
| `output_path` | `<stem>_restaure.md` | Chemin du fichier restauré |
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 7. Découvrir les types de PII détectables
|
|
198
|
+
|
|
199
|
+
Demande à l'agent :
|
|
200
|
+
|
|
201
|
+
> *« Quels types d'entités NomosAI peut-il détecter en français ? »*
|
|
202
|
+
|
|
203
|
+
L'agent appelle `list_entities` et liste tous les types disponibles. Réponse instantanée,
|
|
204
|
+
sans chargement du modèle NLP.
|
|
205
|
+
|
|
206
|
+
### Types actifs par défaut (français)
|
|
207
|
+
|
|
208
|
+
| Type | Label | Description |
|
|
209
|
+
|------|-------|-------------|
|
|
210
|
+
| `PERSON` | `PERSONNE` | Noms de personnes physiques |
|
|
211
|
+
| `ORGANIZATION` | `ORGANISATION` | Noms d'organisations ou entreprises |
|
|
212
|
+
| `LOCATION` | `LIEU` | Lieux géographiques, adresses |
|
|
213
|
+
| `EMAIL_ADDRESS` | `EMAIL` | Adresses e-mail |
|
|
214
|
+
| `PHONE_NUMBER` | `TELEPHONE` | Numéros de téléphone |
|
|
215
|
+
| `IBAN_CODE` | `IBAN` | Codes IBAN bancaires |
|
|
216
|
+
| `CREDIT_CARD` | `CARTE_CREDIT` | Numéros de carte de crédit |
|
|
217
|
+
| `URL` | `URL` | URLs et liens web |
|
|
218
|
+
| `IP_ADDRESS` | `IP` | Adresses IP |
|
|
219
|
+
| `FR_NIR` | `NIR` | Numéro INSEE / Sécurité Sociale (15 chiffres) |
|
|
220
|
+
| `FR_SIRET` | `SIRET` | Identifiant établissement (14 chiffres) |
|
|
221
|
+
| `FR_SIREN` | `SIREN` | Identifiant entreprise (9 chiffres) |
|
|
222
|
+
| `FR_TVA` | `TVA` | Numéro TVA intracommunautaire |
|
|
223
|
+
| `FR_PASSPORT` | `PASSEPORT` | Numéro de passeport français |
|
|
224
|
+
| `FR_CNI` | `CNI` | Carte Nationale d'Identité (12 chiffres) |
|
|
225
|
+
| `FR_DRIVING_LICENSE` | `PERMIS` | Permis de conduire post-2013 (AA-NNN-AA) |
|
|
226
|
+
| `FR_POSTAL_CODE` | `CODE_POSTAL` | Code postal français (avec contexte) |
|
|
227
|
+
|
|
228
|
+
### Types opt-in (à activer explicitement)
|
|
229
|
+
|
|
230
|
+
Ces types génèrent du bruit s'ils sont activés par défaut ; ils doivent être demandés
|
|
231
|
+
explicitement dans le paramètre `entities` :
|
|
232
|
+
|
|
233
|
+
| Type | Label | Description |
|
|
234
|
+
|------|-------|-------------|
|
|
235
|
+
| `DATE_TIME` | `DATE` | Dates et heures |
|
|
236
|
+
| `FILE_PATH` | `CHEMIN_FICHIER` | Chemins de fichiers Windows/Unix (français uniquement) |
|
|
237
|
+
|
|
238
|
+
Exemple d'activation :
|
|
239
|
+
|
|
240
|
+
> *« Anonymise `rapport.pdf` en détectant aussi les dates »*
|
|
241
|
+
> → l'agent passe `entities: ["PERSON", "EMAIL_ADDRESS", ..., "DATE_TIME"]`
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 8. Récapitulatif des outils MCP
|
|
246
|
+
|
|
247
|
+
| Outil | Usage typique |
|
|
248
|
+
|-------|---------------|
|
|
249
|
+
| `anonymize_text` | Anonymiser un extrait de texte passé directement à l'agent |
|
|
250
|
+
| `anonymize_file` | Anonymiser un fichier `.docx/.pdf/.txt/.md/.rst` sur disque |
|
|
251
|
+
| `deanonymize_file` | Restaurer un `.md` anonymisé à partir de son index confidentiel |
|
|
252
|
+
| `list_entities` | Lister les types de PII détectables (réponse instantanée) |
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 9. Flux de travail typique
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
Document original (.pdf / .docx)
|
|
260
|
+
│
|
|
261
|
+
▼
|
|
262
|
+
anonymize_file
|
|
263
|
+
│
|
|
264
|
+
├─► contrat_anonymise.md ← partageable, traitable par LLM
|
|
265
|
+
└─► contrat_anonymise-index.md ← confidentiel, à conserver précieusement
|
|
266
|
+
│
|
|
267
|
+
▼
|
|
268
|
+
deanonymize_file
|
|
269
|
+
│
|
|
270
|
+
▼
|
|
271
|
+
contrat_restaure.md ← document original restauré
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Utilisation locale (développeurs)
|
|
277
|
+
|
|
278
|
+
Pour les contributeurs qui travaillent sur le code source, l'environnement conda `nomosai`
|
|
279
|
+
reste utilisable :
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
conda activate nomosai
|
|
283
|
+
python -m nomosai.server # démarre le serveur MCP manuellement
|
|
284
|
+
python anonymize.py rapport.pdf # CLI d'anonymisation
|
|
285
|
+
python deanonymize.py rapport_anonymise.md # CLI de restauration
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Configuration Claude Code (développement local) :
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
claude mcp add nomos-ai \
|
|
292
|
+
--cwd "C:\Users\corla\Desktop\projects\NomosAI" \
|
|
293
|
+
-- "C:\Users\corla\anaconda3\envs\nomosai\python.exe" -m nomosai.server
|
|
294
|
+
```
|
nomos_ai-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nomos-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: GDPR-compliant document anonymization MCP server for legal use
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: mcp[cli]>=1.0
|
|
7
|
+
Requires-Dist: pdfminer-six>=20221105
|
|
8
|
+
Requires-Dist: pdfplumber>=0.11
|
|
9
|
+
Requires-Dist: presidio-analyzer>=2.2.351
|
|
10
|
+
Requires-Dist: presidio-anonymizer>=2.2.351
|
|
11
|
+
Requires-Dist: pypdf>=4.0
|
|
12
|
+
Requires-Dist: python-docx>=1.1
|
|
13
|
+
Requires-Dist: spacy<3.9,>=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# NomosAI — Anonymiseur de documents juridiques
|
|
17
|
+
|
|
18
|
+
Détecte et masque les données personnelles (RGPD) dans des documents `.docx`, `.pdf`, `.txt`, `.md` et `.rst`, et produit un Markdown structuré avec métadonnées de conformité.
|
|
19
|
+
Moteur : [Microsoft Presidio](https://github.com/microsoft/presidio) + [spaCy](https://spacy.io/) · **Langue par défaut : français**.
|
|
20
|
+
|
|
21
|
+
Distribué comme **serveur MCP** — utilisable directement depuis Claude Desktop ou Claude Code, sans interface graphique.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation rapide (utilisateurs finaux)
|
|
26
|
+
|
|
27
|
+
**Étape 1 — Installer `uv` (une seule fois par machine) :**
|
|
28
|
+
|
|
29
|
+
- **Windows :** ouvre PowerShell et colle :
|
|
30
|
+
```
|
|
31
|
+
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
32
|
+
```
|
|
33
|
+
- **Mac :** ouvre Terminal et colle :
|
|
34
|
+
```bash
|
|
35
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Étape 2 — Ajouter NomosAI à Claude Desktop :**
|
|
39
|
+
|
|
40
|
+
Édite `%APPDATA%\Claude\claude_desktop_config.json` (Windows) ou
|
|
41
|
+
`~/Library/Application Support/Claude/claude_desktop_config.json` (Mac) :
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"nomos-ai": {
|
|
47
|
+
"command": "uvx",
|
|
48
|
+
"args": ["nomos-ai"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Redémarre Claude Desktop. Au premier démarrage, `uvx` télécharge automatiquement toutes les dépendances (modèle spaCy `fr_core_news_lg` inclus, ~600 Mo) — environ 1 à 3 minutes. Les démarrages suivants sont instantanés.
|
|
55
|
+
|
|
56
|
+
Pour plus de détails, voir le **[Guide d'utilisation MCP](NomosAI-Anonymisation-UserGuide.md)**.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Formats supportés
|
|
61
|
+
|
|
62
|
+
| Entrée | Librairie |
|
|
63
|
+
|--------|-----------|
|
|
64
|
+
| `.docx` | `python-docx` |
|
|
65
|
+
| `.pdf` | `pdfminer.six` (fallback : `pypdf`) |
|
|
66
|
+
| `.txt` / `.md` / `.rst` | natif |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Installation développeur
|
|
71
|
+
|
|
72
|
+
Pour travailler sur le code source :
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Cloner le dépôt et installer en mode éditable
|
|
76
|
+
pip install -e .
|
|
77
|
+
|
|
78
|
+
# Ou via conda (environnement existant)
|
|
79
|
+
conda activate nomosai
|
|
80
|
+
pip install -e .
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Le modèle spaCy est installé automatiquement comme dépendance du package.
|
|
84
|
+
Pour ajouter le modèle anglais (optionnel) :
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
python -m spacy download en_core_web_lg
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Anonymisation — `anonymize.py`
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Fichier unique (sortie : <nom>_anonymise.md)
|
|
96
|
+
python anonymize.py rapport.pdf
|
|
97
|
+
|
|
98
|
+
# Sortie explicite
|
|
99
|
+
python anonymize.py contrat.docx --output contrat_anon.md
|
|
100
|
+
|
|
101
|
+
# Dossier complet
|
|
102
|
+
python anonymize.py dossier/ --output dossier_anonymise/
|
|
103
|
+
|
|
104
|
+
# Dossier récursif
|
|
105
|
+
python anonymize.py dossier/ --recursive --language fr
|
|
106
|
+
|
|
107
|
+
# Limiter les types d'entités
|
|
108
|
+
python anonymize.py notes.txt --entities PERSON EMAIL_ADDRESS PHONE_NUMBER
|
|
109
|
+
|
|
110
|
+
# Ajuster le seuil de confiance (défaut : 0.55)
|
|
111
|
+
python anonymize.py file.pdf --score-threshold 0.6
|
|
112
|
+
|
|
113
|
+
# Activer les entités opt-in (dates, chemins fichiers)
|
|
114
|
+
python anonymize.py file.pdf --entities PERSON DATE_TIME FILE_PATH
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Chaque exécution produit deux fichiers :
|
|
118
|
+
|
|
119
|
+
| Fichier | Contenu |
|
|
120
|
+
|---------|---------|
|
|
121
|
+
| `<nom>_anonymise.md` | Document anonymisé avec frontmatter YAML RGPD |
|
|
122
|
+
| `<nom>_anonymise-index.md` | **Confidentiel** — table `placeholder → valeur originale` |
|
|
123
|
+
|
|
124
|
+
En mode dossier, un `RAPPORT_ANONYMISATION.md` de synthèse est également généré.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Désanonymisation — `deanonymize.py`
|
|
129
|
+
|
|
130
|
+
Restaure les valeurs originales à partir de l'index.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Index auto-détecté (<fichier>-index.md voisin)
|
|
134
|
+
python deanonymize.py contrat_anonymise.md
|
|
135
|
+
|
|
136
|
+
# Index explicite
|
|
137
|
+
python deanonymize.py contrat_anonymise.md --index contrat_anonymise-index.md
|
|
138
|
+
|
|
139
|
+
# Sortie explicite
|
|
140
|
+
python deanonymize.py contrat_anonymise.md --output contrat_restaure.md
|
|
141
|
+
|
|
142
|
+
# Dossier complet
|
|
143
|
+
python deanonymize.py dossier_anonymise/ --output dossier_restaure/
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
> ⚠ Ce script restaure des données personnelles. Réservez-le aux personnes habilitées.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Entités détectées
|
|
151
|
+
|
|
152
|
+
### Par défaut
|
|
153
|
+
|
|
154
|
+
| Entité | Label | Description |
|
|
155
|
+
|--------|-------|-------------|
|
|
156
|
+
| `PERSON` | `[PERSONNE_N]` | Noms de personnes (NLP + titres Pr/Dr/M./Mme) |
|
|
157
|
+
| `ORGANIZATION` | `[ORGANISATION_N]` | Noms d'organisations |
|
|
158
|
+
| `LOCATION` | `[ADRESSE_N]` | Lieux, adresses |
|
|
159
|
+
| `EMAIL_ADDRESS` | `[EMAIL_N]` | Adresses e-mail |
|
|
160
|
+
| `PHONE_NUMBER` | `[TELEPHONE_N]` | Numéros français (+33, 0033, local) |
|
|
161
|
+
| `URL` | `[URL_N]` | URLs |
|
|
162
|
+
| `IP_ADDRESS` | `[IP_N]` | Adresses IP |
|
|
163
|
+
| `CREDIT_CARD` | `[CARTE_BANCAIRE_N]` | Numéros de carte bancaire |
|
|
164
|
+
| `IBAN_CODE` | `[IBAN_N]` | IBAN |
|
|
165
|
+
| `NRP` | `[ID_NATIONAL_N]` | Identifiants nationaux |
|
|
166
|
+
| `MEDICAL_LICENSE` | `[LICENCE_MEDICALE_N]` | Licences médicales |
|
|
167
|
+
| `CRYPTO` | `[CRYPTO_N]` | Adresses crypto |
|
|
168
|
+
|
|
169
|
+
### Reconnaisseurs français supplémentaires
|
|
170
|
+
|
|
171
|
+
| Entité | Label | Exemples |
|
|
172
|
+
|--------|-------|---------|
|
|
173
|
+
| `FR_NIR` | `[NIR_N]` | Numéro de sécurité sociale (15 chiffres) |
|
|
174
|
+
| `FR_SIRET` | `[SIRET_N]` | SIRET (14 chiffres) |
|
|
175
|
+
| `FR_SIREN` | `[SIREN_N]` | SIREN (9 chiffres) |
|
|
176
|
+
| `FR_TVA` | `[TVA_N]` | TVA intracommunautaire |
|
|
177
|
+
| `FR_PASSPORT` | `[PASSEPORT_N]` | Passeport français |
|
|
178
|
+
| `FR_CNI` | `[CNI_N]` | Carte nationale d'identité |
|
|
179
|
+
| `FR_DRIVING_LICENSE` | `[PERMIS_N]` | Permis de conduire |
|
|
180
|
+
| `FR_POSTAL_CODE` | `[CODE_POSTAL_N]` | Code postal (avec contexte) |
|
|
181
|
+
|
|
182
|
+
### Entités opt-in (à activer via `--entities`)
|
|
183
|
+
|
|
184
|
+
| Entité | Label | Remarque |
|
|
185
|
+
|--------|-------|---------|
|
|
186
|
+
| `DATE_TIME` | `[DATE_N]` | Dates — exclus par défaut (bruit dans les rapports) |
|
|
187
|
+
| `FILE_PATH` | `[CHEMIN_FICHIER_N]` | Chemins Windows/Unix — français uniquement, exclus par défaut |
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Architecture
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
anonymize.py CLI principal (lecture, anonymisation, écriture)
|
|
195
|
+
deanonymize.py CLI de restauration à partir de l'index
|
|
196
|
+
src/
|
|
197
|
+
nomosai/
|
|
198
|
+
readers.py Lecture DOCX/PDF/TXT/MD/RST → liste de Block
|
|
199
|
+
engine.py Moteur Presidio + filtres faux positifs + dédup spans
|
|
200
|
+
recognizers_fr.py Reconnaisseurs regex français (NIR, SIRET, téléphone…)
|
|
201
|
+
formatter.py Rendu Markdown + frontmatter RGPD
|
|
202
|
+
server.py Serveur MCP (FastMCP, transport stdio)
|
|
203
|
+
pyproject.toml Métadonnées du package et dépendances
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Pipeline :** `Reader → Engine → Formatter → fichier .md + fichier -index.md`
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Qualité de détection
|
|
211
|
+
|
|
212
|
+
### Seuils de confiance
|
|
213
|
+
|
|
214
|
+
Le seuil global par défaut est `0.55`. Certains types ont un plancher plus élevé pour réduire les faux positifs :
|
|
215
|
+
|
|
216
|
+
| Type | Seuil minimum |
|
|
217
|
+
|------|---------------|
|
|
218
|
+
| `LOCATION` | 0.80 (sur-détection dans les tableaux PDF) |
|
|
219
|
+
| `FR_SIREN` | 0.70 |
|
|
220
|
+
| `FR_CNI` | 0.70 |
|
|
221
|
+
| `FR_POSTAL_CODE` | 0.70 |
|
|
222
|
+
|
|
223
|
+
### Filtres anti-faux-positifs
|
|
224
|
+
|
|
225
|
+
L'engine rejette automatiquement les détections dont le texte :
|
|
226
|
+
- est une lettre ou sigle de 1–3 caractères (`F`, `NR`, `ET`…)
|
|
227
|
+
- est une valeur statistique (`N=1 035`, `75,6`…)
|
|
228
|
+
- est un numéro de section (`II.4`, `I.1`…)
|
|
229
|
+
- est un fragment tout-en-majuscules multi-mots (en-tête de tableau)
|
|
230
|
+
- contient des artefacts OCR (espaces intrus dans un mot)
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Ajouter un reconnaisseur personnalisé
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from presidio_analyzer import PatternRecognizer, Pattern
|
|
238
|
+
|
|
239
|
+
MY_RECOGNIZER = PatternRecognizer(
|
|
240
|
+
supported_entity="MY_ENTITY",
|
|
241
|
+
supported_language="fr",
|
|
242
|
+
patterns=[Pattern("MY_PATTERN", r"\bREGEX\b", score=0.85)],
|
|
243
|
+
context=["mot", "clé", "contextuel"],
|
|
244
|
+
)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Ajouter l'instance dans `get_french_recognizers()` de `src/nomosai/recognizers_fr.py` et son label dans `FRENCH_ENTITY_LABELS`.
|
|
248
|
+
|
|
249
|
+
Voir la [doc Presidio](https://microsoft.github.io/presidio/analyzer/adding_recognizers/).
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Publier une nouvelle version sur PyPI
|
|
254
|
+
|
|
255
|
+
### Première publication (une seule fois)
|
|
256
|
+
|
|
257
|
+
1. Créer un compte sur [pypi.org](https://pypi.org/account/register/)
|
|
258
|
+
2. Générer un token API : **Account Settings → API tokens → Add API token**
|
|
259
|
+
3. Publier :
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
uv build
|
|
263
|
+
uv publish --token pypi-<VOTRE_TOKEN>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Mettre à jour une version existante
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# 1. Incrémenter la version dans pyproject.toml
|
|
270
|
+
# ex. version = "0.1.0" → version = "0.2.0"
|
|
271
|
+
|
|
272
|
+
# 2. Construire et publier
|
|
273
|
+
uv build
|
|
274
|
+
uv publish --token pypi-<VOTRE_TOKEN>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Vérifier l'installation depuis PyPI
|
|
278
|
+
|
|
279
|
+
Sur une machine sans l'environnement de développement :
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
uvx nomos-ai
|
|
283
|
+
# → le serveur MCP démarre (attend sur stdin, pas d'erreur)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Stocker le token de façon permanente
|
|
287
|
+
|
|
288
|
+
Pour ne pas retaper le token à chaque publication, créer `~/.pypirc` :
|
|
289
|
+
|
|
290
|
+
```ini
|
|
291
|
+
[pypi]
|
|
292
|
+
username = __token__
|
|
293
|
+
password = pypi-<VOTRE_TOKEN>
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Puis publier simplement avec :
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
uv publish
|
|
300
|
+
```
|