pdf-anonymizer-cli 0.3.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.
- pdf_anonymizer_cli-0.3.0/PKG-INFO +100 -0
- pdf_anonymizer_cli-0.3.0/README.md +87 -0
- pdf_anonymizer_cli-0.3.0/pyproject.toml +29 -0
- pdf_anonymizer_cli-0.3.0/setup.cfg +4 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli/__init__.py +0 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli/cli.py +201 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli/main.py +4 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli.egg-info/PKG-INFO +100 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli.egg-info/SOURCES.txt +11 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli.egg-info/dependency_links.txt +1 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli.egg-info/entry_points.txt +2 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli.egg-info/requires.txt +3 -0
- pdf_anonymizer_cli-0.3.0/src/pdf_anonymizer_cli.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdf-anonymizer-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: CLI for a tool to anonymize PDF, Markdown, and plain text files using LLMs.
|
|
5
|
+
Author-email: Leonid Ganeline <leo.gan.57@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: repository, https://github.com/leo-gan/anonymizer
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: typer
|
|
11
|
+
Requires-Dist: rich
|
|
12
|
+
Requires-Dist: pdf-anonymizer-core
|
|
13
|
+
|
|
14
|
+
# PDF Anonymizer CLI
|
|
15
|
+
|
|
16
|
+
A command-line interface for anonymizing PDF, Markdown, and plain text files using LLMs.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
This project uses `uv` and is structured as a monorepo. The dependencies for the CLI and its core library are managed at the root of the project.
|
|
21
|
+
|
|
22
|
+
1. **Install `uv`**: Follow the [official installation instructions](https://astral.sh/docs/uv#installation).
|
|
23
|
+
2. **Install dependencies from the repository root**:
|
|
24
|
+
```bash
|
|
25
|
+
# From the repository root
|
|
26
|
+
uv sync
|
|
27
|
+
```
|
|
28
|
+
This installs the `pdf-anonymizer` executable.
|
|
29
|
+
|
|
30
|
+
## Environment Variables
|
|
31
|
+
|
|
32
|
+
The CLI will automatically load a `.env` file from the current directory or any parent directory. For consistency, it's recommended to place a single `.env` file at the root of the repository.
|
|
33
|
+
|
|
34
|
+
- `GOOGLE_API_KEY`: Required when using Google's Gemini models.
|
|
35
|
+
- `OLLAMA_HOST`: Optional, defaults to `http://localhost:11434` when using local Ollama models.
|
|
36
|
+
|
|
37
|
+
Example `.env` file:
|
|
38
|
+
```env
|
|
39
|
+
GOOGLE_API_KEY="YOUR_API_KEY_HERE"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### Anonymize
|
|
45
|
+
|
|
46
|
+
The `run` command anonymizes one or more files.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pdf-anonymizer run FILE_PATH [FILE_PATH ...] \
|
|
50
|
+
[--characters-to-anonymize INTEGER] \
|
|
51
|
+
[--prompt-name {simple|detailed}] \
|
|
52
|
+
[--model-name TEXT] \
|
|
53
|
+
[--anonymized-entities PATH]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Arguments**:
|
|
57
|
+
- `FILE_PATH`: Path to one or several PDF, Markdown, or text files for anonymization.
|
|
58
|
+
|
|
59
|
+
**Options**:
|
|
60
|
+
- `--characters-to-anonymize INTEGER`: Number of characters to process in each chunk (default: `100000`).
|
|
61
|
+
- `--prompt-name [simple|detailed]`: The prompt template to use (default: `detailed`).
|
|
62
|
+
- `--model-name TEXT`: The language model to use.
|
|
63
|
+
- `--anonymized-entities PATH`: Path to a file with a list of entities to anonymize.
|
|
64
|
+
|
|
65
|
+
**Models**:
|
|
66
|
+
- **Google**: `gemini-2.5-pro`, `gemini-2.5-flash` (default), `gemini-2.5-flash-lite`.
|
|
67
|
+
- **Ollama**: `gemma:7b`, `phi4-mini`.
|
|
68
|
+
|
|
69
|
+
### Examples
|
|
70
|
+
|
|
71
|
+
**Basic anonymization**:
|
|
72
|
+
```bash
|
|
73
|
+
pdf-anonymizer run document.pdf
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Custom model and prompt**:
|
|
77
|
+
```bash
|
|
78
|
+
pdf-anonymizer run notes.md --model-name phi4-mini --prompt-name simple
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Deanonymize
|
|
82
|
+
|
|
83
|
+
The `deanonymize` command reverts anonymization using a mapping file.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pdf-anonymizer deanonymize ANONYMIZED_FILE MAPPING_FILE
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Arguments**:
|
|
90
|
+
- `ANONYMIZED_FILE`: Path to the anonymized text file.
|
|
91
|
+
- `MAPPING_FILE`: Path to the JSON mapping file.
|
|
92
|
+
|
|
93
|
+
**Example**:
|
|
94
|
+
```bash
|
|
95
|
+
pdf-anonymizer deanonymize \
|
|
96
|
+
data/anonymized/document.anonymized.md \
|
|
97
|
+
data/mappings/document.mapping.json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This will create a deanonymized version of the file at `data/deanonymized/document.deanonymized.md`.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# PDF Anonymizer CLI
|
|
2
|
+
|
|
3
|
+
A command-line interface for anonymizing PDF, Markdown, and plain text files using LLMs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This project uses `uv` and is structured as a monorepo. The dependencies for the CLI and its core library are managed at the root of the project.
|
|
8
|
+
|
|
9
|
+
1. **Install `uv`**: Follow the [official installation instructions](https://astral.sh/docs/uv#installation).
|
|
10
|
+
2. **Install dependencies from the repository root**:
|
|
11
|
+
```bash
|
|
12
|
+
# From the repository root
|
|
13
|
+
uv sync
|
|
14
|
+
```
|
|
15
|
+
This installs the `pdf-anonymizer` executable.
|
|
16
|
+
|
|
17
|
+
## Environment Variables
|
|
18
|
+
|
|
19
|
+
The CLI will automatically load a `.env` file from the current directory or any parent directory. For consistency, it's recommended to place a single `.env` file at the root of the repository.
|
|
20
|
+
|
|
21
|
+
- `GOOGLE_API_KEY`: Required when using Google's Gemini models.
|
|
22
|
+
- `OLLAMA_HOST`: Optional, defaults to `http://localhost:11434` when using local Ollama models.
|
|
23
|
+
|
|
24
|
+
Example `.env` file:
|
|
25
|
+
```env
|
|
26
|
+
GOOGLE_API_KEY="YOUR_API_KEY_HERE"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Anonymize
|
|
32
|
+
|
|
33
|
+
The `run` command anonymizes one or more files.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pdf-anonymizer run FILE_PATH [FILE_PATH ...] \
|
|
37
|
+
[--characters-to-anonymize INTEGER] \
|
|
38
|
+
[--prompt-name {simple|detailed}] \
|
|
39
|
+
[--model-name TEXT] \
|
|
40
|
+
[--anonymized-entities PATH]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Arguments**:
|
|
44
|
+
- `FILE_PATH`: Path to one or several PDF, Markdown, or text files for anonymization.
|
|
45
|
+
|
|
46
|
+
**Options**:
|
|
47
|
+
- `--characters-to-anonymize INTEGER`: Number of characters to process in each chunk (default: `100000`).
|
|
48
|
+
- `--prompt-name [simple|detailed]`: The prompt template to use (default: `detailed`).
|
|
49
|
+
- `--model-name TEXT`: The language model to use.
|
|
50
|
+
- `--anonymized-entities PATH`: Path to a file with a list of entities to anonymize.
|
|
51
|
+
|
|
52
|
+
**Models**:
|
|
53
|
+
- **Google**: `gemini-2.5-pro`, `gemini-2.5-flash` (default), `gemini-2.5-flash-lite`.
|
|
54
|
+
- **Ollama**: `gemma:7b`, `phi4-mini`.
|
|
55
|
+
|
|
56
|
+
### Examples
|
|
57
|
+
|
|
58
|
+
**Basic anonymization**:
|
|
59
|
+
```bash
|
|
60
|
+
pdf-anonymizer run document.pdf
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Custom model and prompt**:
|
|
64
|
+
```bash
|
|
65
|
+
pdf-anonymizer run notes.md --model-name phi4-mini --prompt-name simple
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Deanonymize
|
|
69
|
+
|
|
70
|
+
The `deanonymize` command reverts anonymization using a mapping file.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pdf-anonymizer deanonymize ANONYMIZED_FILE MAPPING_FILE
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Arguments**:
|
|
77
|
+
- `ANONYMIZED_FILE`: Path to the anonymized text file.
|
|
78
|
+
- `MAPPING_FILE`: Path to the JSON mapping file.
|
|
79
|
+
|
|
80
|
+
**Example**:
|
|
81
|
+
```bash
|
|
82
|
+
pdf-anonymizer deanonymize \
|
|
83
|
+
data/anonymized/document.anonymized.md \
|
|
84
|
+
data/mappings/document.mapping.json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This will create a deanonymized version of the file at `data/deanonymized/document.deanonymized.md`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pdf-anonymizer-cli"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "CLI for a tool to anonymize PDF, Markdown, and plain text files using LLMs."
|
|
5
|
+
authors = [{ name = "Leonid Ganeline", email = "leo.gan.57@gmail.com" }]
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"typer",
|
|
11
|
+
"rich",
|
|
12
|
+
"pdf-anonymizer-core",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
repository = "https://github.com/leo-gan/anonymizer"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["setuptools>=61.0"]
|
|
20
|
+
build-backend = "setuptools.build_meta"
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
pdf-anonymizer = "pdf_anonymizer_cli.cli:app"
|
|
24
|
+
|
|
25
|
+
[tool.setuptools]
|
|
26
|
+
package-dir = {"" = "src"}
|
|
27
|
+
|
|
28
|
+
[tool.uv.sources]
|
|
29
|
+
pdf-anonymizer-core = { workspace = true }
|
|
File without changes
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
from pdf_anonymizer_core.conf import (
|
|
10
|
+
DEFAULT_CHARACTERS_TO_ANONYMIZE,
|
|
11
|
+
DEFAULT_MODEL_NAME,
|
|
12
|
+
DEFAULT_PROMPT_NAME,
|
|
13
|
+
ModelName,
|
|
14
|
+
ModelProvider,
|
|
15
|
+
PromptEnum,
|
|
16
|
+
get_enum_value,
|
|
17
|
+
)
|
|
18
|
+
from pdf_anonymizer_core.core import anonymize_file
|
|
19
|
+
from pdf_anonymizer_core.prompts import detailed, simple
|
|
20
|
+
from pdf_anonymizer_core.utils import (
|
|
21
|
+
consolidate_mapping,
|
|
22
|
+
deanonymize_file,
|
|
23
|
+
save_results,
|
|
24
|
+
)
|
|
25
|
+
from typing_extensions import Annotated
|
|
26
|
+
|
|
27
|
+
logging.basicConfig(
|
|
28
|
+
level=logging.INFO,
|
|
29
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
30
|
+
handlers=[logging.FileHandler("app.log"), logging.StreamHandler()],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
app = typer.Typer()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def load_environment() -> None:
|
|
37
|
+
"""Load environment variables from .env file if it exists."""
|
|
38
|
+
env_path = Path(__file__).parent.parent.parent / ".env"
|
|
39
|
+
if env_path.exists():
|
|
40
|
+
load_dotenv(env_path)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@app.command()
|
|
44
|
+
def run(
|
|
45
|
+
file_paths: Annotated[
|
|
46
|
+
List[Path],
|
|
47
|
+
typer.Argument(
|
|
48
|
+
help="A list of paths to files to anonymize.",
|
|
49
|
+
exists=True,
|
|
50
|
+
file_okay=True,
|
|
51
|
+
dir_okay=False,
|
|
52
|
+
writable=False,
|
|
53
|
+
readable=True,
|
|
54
|
+
resolve_path=True,
|
|
55
|
+
),
|
|
56
|
+
],
|
|
57
|
+
characters_to_anonymize: Annotated[
|
|
58
|
+
int,
|
|
59
|
+
typer.Option(help="Number of characters to send for anonymization in one go."),
|
|
60
|
+
] = DEFAULT_CHARACTERS_TO_ANONYMIZE,
|
|
61
|
+
prompt_name: Annotated[
|
|
62
|
+
PromptEnum,
|
|
63
|
+
typer.Option(
|
|
64
|
+
help="The name of the prompt to use for anonymization.",
|
|
65
|
+
case_sensitive=False,
|
|
66
|
+
),
|
|
67
|
+
] = get_enum_value(PromptEnum, DEFAULT_PROMPT_NAME),
|
|
68
|
+
model_name: Annotated[
|
|
69
|
+
ModelName,
|
|
70
|
+
typer.Option(
|
|
71
|
+
help="The name of the model to use for anonymization.",
|
|
72
|
+
case_sensitive=False,
|
|
73
|
+
),
|
|
74
|
+
] = get_enum_value(ModelName, DEFAULT_MODEL_NAME),
|
|
75
|
+
anonymized_entities: Annotated[
|
|
76
|
+
Optional[Path],
|
|
77
|
+
typer.Option(
|
|
78
|
+
"--anonymized-entities",
|
|
79
|
+
help="A file with a list of entities to anonymize.",
|
|
80
|
+
exists=True,
|
|
81
|
+
file_okay=True,
|
|
82
|
+
dir_okay=False,
|
|
83
|
+
writable=False,
|
|
84
|
+
readable=True,
|
|
85
|
+
resolve_path=True,
|
|
86
|
+
),
|
|
87
|
+
] = None,
|
|
88
|
+
) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Anonymize one or more files by replacing PII with anonymized placeholders.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
file_paths: List of paths to files to process.
|
|
94
|
+
characters_to_anonymize: Number of characters to process in each chunk.
|
|
95
|
+
prompt_name: The prompt template to use for anonymization.
|
|
96
|
+
model_name: The language model to use for anonymization.
|
|
97
|
+
anonymized_entities: A file with a list of entities to anonymize.
|
|
98
|
+
"""
|
|
99
|
+
load_environment()
|
|
100
|
+
|
|
101
|
+
if model_name.provider == ModelProvider.GOOGLE:
|
|
102
|
+
if "gemini" in model_name.value and not os.getenv("GOOGLE_API_KEY"):
|
|
103
|
+
logging.error(
|
|
104
|
+
"Error: GOOGLE_API_KEY not found. Please set it in the .env file."
|
|
105
|
+
)
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
|
|
108
|
+
logging.info(f" --file-paths: {file_paths}")
|
|
109
|
+
logging.info(f" --characters-to-anonymize: {characters_to_anonymize}")
|
|
110
|
+
logging.info(f" --model-name: {model_name.value}")
|
|
111
|
+
|
|
112
|
+
# Select the appropriate prompt template
|
|
113
|
+
prompt_templates: Dict[str, str] = {
|
|
114
|
+
PromptEnum.simple: simple.prompt_template,
|
|
115
|
+
PromptEnum.detailed: detailed.prompt_template,
|
|
116
|
+
}
|
|
117
|
+
prompt_template: str = prompt_templates[prompt_name]
|
|
118
|
+
logging.info(f" --prompt-name: {prompt_name.value}")
|
|
119
|
+
|
|
120
|
+
entities_to_anonymize = None
|
|
121
|
+
if anonymized_entities:
|
|
122
|
+
with open(anonymized_entities, "r") as f:
|
|
123
|
+
entities_to_anonymize = [line.strip() for line in f.readlines()]
|
|
124
|
+
logging.info(f" --anonymized-entities: {entities_to_anonymize}")
|
|
125
|
+
|
|
126
|
+
logging.info(f"Found {len(file_paths)} file(s) to process.")
|
|
127
|
+
|
|
128
|
+
for i, file_path in enumerate(file_paths, 1):
|
|
129
|
+
logging.info("=" * 40)
|
|
130
|
+
logging.info(f"Processing file {i}/{len(file_paths)}: {file_path}")
|
|
131
|
+
full_anonymized_text, final_mapping = anonymize_file(
|
|
132
|
+
str(file_path),
|
|
133
|
+
characters_to_anonymize,
|
|
134
|
+
prompt_template,
|
|
135
|
+
model_name.value,
|
|
136
|
+
entities_to_anonymize,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if full_anonymized_text and final_mapping:
|
|
140
|
+
# The mapping from anonymize_file is original -> placeholder.
|
|
141
|
+
# We will standardize on placeholder -> original for subsequent steps.
|
|
142
|
+
placeholder_to_original = {v: k for k, v in final_mapping.items()}
|
|
143
|
+
|
|
144
|
+
logging.info("Consolidating mapping...")
|
|
145
|
+
full_anonymized_text, consolidated_placeholder_map = consolidate_mapping(
|
|
146
|
+
full_anonymized_text, placeholder_to_original
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
anonymized_output_file, mapping_file = save_results(
|
|
150
|
+
full_anonymized_text, consolidated_placeholder_map, str(file_path)
|
|
151
|
+
)
|
|
152
|
+
logging.info(f"Anonymization for {file_path} complete!")
|
|
153
|
+
logging.info(f"Anonymized text saved into '{anonymized_output_file}'")
|
|
154
|
+
logging.info(f"Mapping vocabulary saved into '{mapping_file}'")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@app.command()
|
|
158
|
+
def deanonymize(
|
|
159
|
+
anonymized_file: Annotated[
|
|
160
|
+
Path,
|
|
161
|
+
typer.Argument(
|
|
162
|
+
help="Path to the anonymized file.",
|
|
163
|
+
exists=True,
|
|
164
|
+
file_okay=True,
|
|
165
|
+
dir_okay=False,
|
|
166
|
+
writable=False,
|
|
167
|
+
readable=True,
|
|
168
|
+
resolve_path=True,
|
|
169
|
+
),
|
|
170
|
+
],
|
|
171
|
+
mapping_file: Annotated[
|
|
172
|
+
Path,
|
|
173
|
+
typer.Argument(
|
|
174
|
+
help="Path to the mapping file.",
|
|
175
|
+
exists=True,
|
|
176
|
+
file_okay=True,
|
|
177
|
+
dir_okay=False,
|
|
178
|
+
writable=False,
|
|
179
|
+
readable=True,
|
|
180
|
+
resolve_path=True,
|
|
181
|
+
),
|
|
182
|
+
],
|
|
183
|
+
) -> None:
|
|
184
|
+
"""
|
|
185
|
+
Deanonymize a file using a mapping file.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
anonymized_file: Path to the anonymized file.
|
|
189
|
+
mapping_file: Path to the mapping file.
|
|
190
|
+
"""
|
|
191
|
+
logging.info(f"Deanonymizing '{anonymized_file}' using '{mapping_file}'")
|
|
192
|
+
deanonymized_output_file, stats_file = deanonymize_file(
|
|
193
|
+
str(anonymized_file), str(mapping_file)
|
|
194
|
+
)
|
|
195
|
+
logging.info("Deanonymization complete!")
|
|
196
|
+
logging.info(f"Deanonymized text saved into '{deanonymized_output_file}'")
|
|
197
|
+
logging.info(f"Deanonymization statistics saved into '{stats_file}'")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == "__main__":
|
|
201
|
+
app()
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdf-anonymizer-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: CLI for a tool to anonymize PDF, Markdown, and plain text files using LLMs.
|
|
5
|
+
Author-email: Leonid Ganeline <leo.gan.57@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: repository, https://github.com/leo-gan/anonymizer
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: typer
|
|
11
|
+
Requires-Dist: rich
|
|
12
|
+
Requires-Dist: pdf-anonymizer-core
|
|
13
|
+
|
|
14
|
+
# PDF Anonymizer CLI
|
|
15
|
+
|
|
16
|
+
A command-line interface for anonymizing PDF, Markdown, and plain text files using LLMs.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
This project uses `uv` and is structured as a monorepo. The dependencies for the CLI and its core library are managed at the root of the project.
|
|
21
|
+
|
|
22
|
+
1. **Install `uv`**: Follow the [official installation instructions](https://astral.sh/docs/uv#installation).
|
|
23
|
+
2. **Install dependencies from the repository root**:
|
|
24
|
+
```bash
|
|
25
|
+
# From the repository root
|
|
26
|
+
uv sync
|
|
27
|
+
```
|
|
28
|
+
This installs the `pdf-anonymizer` executable.
|
|
29
|
+
|
|
30
|
+
## Environment Variables
|
|
31
|
+
|
|
32
|
+
The CLI will automatically load a `.env` file from the current directory or any parent directory. For consistency, it's recommended to place a single `.env` file at the root of the repository.
|
|
33
|
+
|
|
34
|
+
- `GOOGLE_API_KEY`: Required when using Google's Gemini models.
|
|
35
|
+
- `OLLAMA_HOST`: Optional, defaults to `http://localhost:11434` when using local Ollama models.
|
|
36
|
+
|
|
37
|
+
Example `.env` file:
|
|
38
|
+
```env
|
|
39
|
+
GOOGLE_API_KEY="YOUR_API_KEY_HERE"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### Anonymize
|
|
45
|
+
|
|
46
|
+
The `run` command anonymizes one or more files.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pdf-anonymizer run FILE_PATH [FILE_PATH ...] \
|
|
50
|
+
[--characters-to-anonymize INTEGER] \
|
|
51
|
+
[--prompt-name {simple|detailed}] \
|
|
52
|
+
[--model-name TEXT] \
|
|
53
|
+
[--anonymized-entities PATH]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Arguments**:
|
|
57
|
+
- `FILE_PATH`: Path to one or several PDF, Markdown, or text files for anonymization.
|
|
58
|
+
|
|
59
|
+
**Options**:
|
|
60
|
+
- `--characters-to-anonymize INTEGER`: Number of characters to process in each chunk (default: `100000`).
|
|
61
|
+
- `--prompt-name [simple|detailed]`: The prompt template to use (default: `detailed`).
|
|
62
|
+
- `--model-name TEXT`: The language model to use.
|
|
63
|
+
- `--anonymized-entities PATH`: Path to a file with a list of entities to anonymize.
|
|
64
|
+
|
|
65
|
+
**Models**:
|
|
66
|
+
- **Google**: `gemini-2.5-pro`, `gemini-2.5-flash` (default), `gemini-2.5-flash-lite`.
|
|
67
|
+
- **Ollama**: `gemma:7b`, `phi4-mini`.
|
|
68
|
+
|
|
69
|
+
### Examples
|
|
70
|
+
|
|
71
|
+
**Basic anonymization**:
|
|
72
|
+
```bash
|
|
73
|
+
pdf-anonymizer run document.pdf
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Custom model and prompt**:
|
|
77
|
+
```bash
|
|
78
|
+
pdf-anonymizer run notes.md --model-name phi4-mini --prompt-name simple
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Deanonymize
|
|
82
|
+
|
|
83
|
+
The `deanonymize` command reverts anonymization using a mapping file.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pdf-anonymizer deanonymize ANONYMIZED_FILE MAPPING_FILE
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Arguments**:
|
|
90
|
+
- `ANONYMIZED_FILE`: Path to the anonymized text file.
|
|
91
|
+
- `MAPPING_FILE`: Path to the JSON mapping file.
|
|
92
|
+
|
|
93
|
+
**Example**:
|
|
94
|
+
```bash
|
|
95
|
+
pdf-anonymizer deanonymize \
|
|
96
|
+
data/anonymized/document.anonymized.md \
|
|
97
|
+
data/mappings/document.mapping.json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This will create a deanonymized version of the file at `data/deanonymized/document.deanonymized.md`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/pdf_anonymizer_cli/__init__.py
|
|
4
|
+
src/pdf_anonymizer_cli/cli.py
|
|
5
|
+
src/pdf_anonymizer_cli/main.py
|
|
6
|
+
src/pdf_anonymizer_cli.egg-info/PKG-INFO
|
|
7
|
+
src/pdf_anonymizer_cli.egg-info/SOURCES.txt
|
|
8
|
+
src/pdf_anonymizer_cli.egg-info/dependency_links.txt
|
|
9
|
+
src/pdf_anonymizer_cli.egg-info/entry_points.txt
|
|
10
|
+
src/pdf_anonymizer_cli.egg-info/requires.txt
|
|
11
|
+
src/pdf_anonymizer_cli.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pdf_anonymizer_cli
|