Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/gpu-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ jobs:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: mise -E gpu-ci run test:smoke:gpu:timeseries

- name: Run GPU smoke tests - local inference
timeout-minutes: 15
run: mise -E gpu-ci run test:smoke:gpu:local-inference

- name: Run GPU smoke tests - SmolLM2
timeout-minutes: 20
env:
Expand Down
6 changes: 6 additions & 0 deletions .mise/tasks/tests.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ run = [
{ task = "test:smoke:gpu:resume" },
{ task = "test:smoke:gpu:structured-generation" },
{ task = "test:smoke:gpu:timeseries" },
{ task = "test:smoke:gpu:local-inference" },
{ task = "test:smoke:gpu:smollm2" },
]

Expand Down Expand Up @@ -77,6 +78,11 @@ description = "Run GPU timeseries smoke tests"
alias = "test-smoke-gpu-timeseries"
run = "uv run --frozen pytest --dist loadscope -vv -n 0 tests/smoke/test_nss_timeseries_gpu.py"

["test:smoke:gpu:local-inference"]
description = "Run GPU smoke tests for the managed local vLLM PII planning server"
alias = "test-smoke-gpu-local-inference"
run = "uv run --frozen pytest --dist loadscope -vv -n 0 tests/smoke/test_local_inference_gpu.py"

["test:smoke:gpu:smollm2"]
description = "Run GPU SmolLM2 smoke tests"
alias = "test-smoke-gpu-smollm2"
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Source code lives in `src/nemo_safe_synthesizer/`:
| `generation/` | GeneratorBackend, VllmBackend, regex manager, batch gen |
| `holdout/` | Train/test splitting |
| `llm/` | Model loading, metadata, memory management |
| `pii_replacer/` | NER-based PII detection and replacement |
| `pii_replacer/` | PII replacement v3: plan discovery and validation (`planning/`), the OpenAI-compatible LLM client (`llm_client.py`), the managed local vLLM planning server (`local_inference/`), `TabularPiiReplacer` interface (`replacer.py`), replacement contracts and generator adapters (`replacement/`); execution is in progress |
| `privacy/` | DP transformers (Opacus integration) |
| `sdk/` | SafeSynthesizer builder, library_builder |
| `training/` | TrainingBackend, HuggingFace backend, timeseries_preprocessing (`timeseries_preprocessing.py`) |
Expand Down
38 changes: 1 addition & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,42 +290,7 @@ Common values: `FLASHINFER`, `FLASH_ATTN`, `TORCH_SDPA`, `TRITON_ATTN`, `FLEX_AT

## NIM Integration

Column classification uses a NIM/OpenAI-compatible endpoint to detect entity types
in your data. `NSS_INFERENCE_ENDPOINT` defaults to `https://integrate.api.nvidia.com/v1`;
override it to use a different endpoint.

When using the CLI or Python SDK, set `NSS_INFERENCE_KEY` (and `NSS_INFERENCE_ENDPOINT` only if not
using the default) so column classification can run.

### Local Endpoint

To point to a locally hosted LLM, add the variables to `.env.local` (git-ignored, auto-loaded by mise):

```bash
# .env.local
NSS_INFERENCE_ENDPOINT=https://your-local-nim-endpoint
NSS_INFERENCE_KEY=your-api-key # pragma: allowlist secret
```

Or export them in your shell:

```bash
export NSS_INFERENCE_ENDPOINT="https://your-local-nim-endpoint"
export NSS_INFERENCE_KEY="your-api-key" # pragma: allowlist secret
```

### Disable Classification

To disable classification entirely:

```yaml
replace_pii:
globals:
classify:
enable_classify: false
```

When classification is disabled, NSS falls back to default entity types.
PII replacement v3 NIM integration will be documented in a later update.

## Artifacts and Workdirs

Expand Down Expand Up @@ -354,7 +319,6 @@ By default, runs are nested under `--artifact-path` using the project name (`<co
│ ├── training.csv
│ ├── test.csv
│ ├── validation.csv # when training.validation_ratio > 0
│ └── transformed_training.csv # when PII replacement transforms the data
└── logs/
└── <phase>.jsonl # e.g. end_to_end.jsonl or train.jsonl
```
Expand Down
2 changes: 0 additions & 2 deletions STYLE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ def is_json_object(value: object) -> TypeIs[dict[str, JsonValue]]:
)
```

Legacy modules (`pii_replacer/`) still use `Optional`/`List`/`Dict`. Only `pii_replacer/` is excluded from `ty` type-checking (see `[tool.ty.src] exclude` in `pyproject.toml`). The remaining legacy usages will be migrated when those modules come under the type checker.

### Control flow

- Prefer `match`/`case` for dispatch on types or tagged values. Not a blanket rule -- `if`/`elif` is fine for simple boolean predicates.
Expand Down
1 change: 0 additions & 1 deletion containers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ Key flags:
- `-v HOST:CONTAINER` -- bind-mount data and HF cache; Docker requires absolute paths
- `-e HF_HOME=...` -- persist model downloads across container runs
- `-e HF_TOKEN=...` -- Hugging Face token for gated models
- `-e NSS_INFERENCE_KEY=...` -- inference API key for PII column classification

### Build Arguments

Expand Down
1 change: 0 additions & 1 deletion cuda_deps.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ torch_runtime_deps = [
"bitsandbytes>=0.46.1",
"flashinfer-python==0.6.16.post4; sys_platform == 'linux'",
"flashinfer-cubin==0.6.16.post4; sys_platform == 'linux'",
"gliner",
"kernels>=0.12.1,<0.15",
"opacus",
"peft>=0.18.0",
Expand Down
16 changes: 7 additions & 9 deletions design.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ graph TB
TrainConfig[TrainingHyperparams]
GenConfig[GenerateParameters]
EvalConfig[EvaluationParameters]
PIIConfig[PiiReplacerConfig]
PIIConfig[ReplacePiiConfig]
DPConfig[DifferentialPrivacyHyperparams]
end
end

subgraph "Data Processing Pipeline"
DataSource[Input Data<br/>DataFrame/CSV]
Holdout[Holdout<br/>Train/Test Split]
PIIReplacer[PII Replacer<br/>NemoPII]
PIIReplacer[PII Replacement v3<br/>Placeholder]
DataActions[ActionExecutor<br/>Data Transformations]
Assembler[ExampleAssembler<br/>Tokenization]
end
Expand Down Expand Up @@ -207,7 +207,7 @@ Path: `src/nemo_safe_synthesizer/config/`
- `TrainingHyperparams`: Training settings (learning rate, epochs, batch size, etc.)
- `GenerateParameters`: Generation settings (temperature, top_p, num_records, etc.)
- `EvaluationParameters`: Evaluation component toggles and settings
- `PiiReplacerConfig`: PII detection and replacement settings
- `ReplacePiiConfig`: Placeholder for PII replacement v3
- `DifferentialPrivacyHyperparams`: DP training parameters (epsilon, delta, clipping norm)

### 2. Data Processing Pipeline
Expand All @@ -217,10 +217,7 @@ Path: `src/nemo_safe_synthesizer/data_processing/`
#### Components:

- Holdout (`holdout/`): Splits data into train/test sets with stratification support
- `NemoPII` (`pii_replacer/`):
- Detects PII entities (names, emails, SSN, etc.)
- Replaces with synthetic but realistic values
- Maintains column statistics
- `pii_replacer/`: PII replacement v3 plan discovery, its OpenAI-compatible LLM client, and `local_inference/`, which runs a vLLM server on the local GPU only while LLM-assisted planning runs
- `ActionExecutor` (`actions/`): Executes data transformations (date normalization, distributions)
- `ExampleAssembler` (`assembler.py`):
- Converts records to JSON format
Expand Down Expand Up @@ -415,7 +412,8 @@ results = synthesizer.results

### PII Protection

- Named Entity Recognition (NER) to detect PII
(To update)
- Detect and replace sensitive values before training
- Deterministic replacement with synthetic values
- Preserves column statistics and distributions

Expand Down Expand Up @@ -469,7 +467,7 @@ safe-synthesizer-artifacts/
2. Custom Generation Backend: Implement `GeneratorBackend` abstract class
3. Custom Evaluation Component: Extend `Component` base class
4. Custom Data Actions: Add to `data_processing/actions/`
5. Custom PII Detectors: Extend NER pipeline
5. Custom evaluation metrics: extend the evaluation component interface

---

Expand Down
3 changes: 2 additions & 1 deletion docs/dev-notes/posts/introducing-nemo-safe-synthesizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ Before the model sees any data, Safe Synthesizer can detect sensitive values and

In this context, data like addresses, phone numbers, emails, social security numbers, and credit card numbers are referred to as entities, and we include those by default as replacement targets. Dozens of additional entity types are supported, and custom entities are configurable. PII replacement is on by default and can be disabled when your data does not contain PII.

Safe Synthesizer uses NVIDIA's fine-tuned [GLiNER PII model](https://huggingface.co/nvidia/gliner-PII#evaluation-datasets) for free-text columns and LLM-based classification for whole-column entities. For the complete entity list and replacement modes, see [PII Replacement](../../product-overview/pii_replacement.md).
PII replacement v3 will be added in a later update. See
[PII Replacement](../../product-overview/pii_replacement.md).

### Stage 2: Fine-Tuning

Expand Down
14 changes: 7 additions & 7 deletions docs/developer-guide/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ graph TB
TrainConfig["TrainingHyperparams"]
GenConfig["GenerateParameters"]
EvalConfig["EvaluationParameters"]
PIIConfig["PiiReplacerConfig"]
PIIConfig["ReplacePiiConfig"]
DPConfig["DifferentialPrivacyHyperparams"]
end
end

subgraph dataProcessing [Data Processing Pipeline]
DataSource["Input Data"]
Holdout["Holdout - Train/Test Split"]
PIIReplacer["PII Replacer - NemoPII"]
PIIReplacer["PII Replacement v3 - Placeholder"]
DataActions["ActionExecutor"]
Assembler["ExampleAssembler"]
end
Expand Down Expand Up @@ -188,7 +188,7 @@ flowchart TB
training_p["TrainingHyperparams"]
gen_p["GenerateParameters"]
eval_p["EvaluationParameters"]
pii_p["PiiReplacerConfig | None"]
pii_p["ReplacePiiConfig | None"]
dp_p["DifferentialPrivacyHyperparams | None"]
ts_p["TimeSeriesParameters"]
end
Expand Down Expand Up @@ -233,7 +233,7 @@ Exactly what avenues of configuration are available, and thus how precedence is
- CLI: CLI flags > dataset registry overrides > YAML config file > defaults
- SDK: Python SDK builder calls > YAML config file > defaults

Nullable sub-configs (`PiiReplacerConfig | None`, `DifferentialPrivacyHyperparams | None`)
Nullable sub-configs (`ReplacePiiConfig | None`, `DifferentialPrivacyHyperparams | None`)
use `None` as the sole disabled signal. The `@pydantic_options` decorator auto-generates
`--no_<field>` is-flags for these fields; `parse_overrides` translates them into
`{field: None}` in the overrides dict.
Expand Down Expand Up @@ -297,15 +297,15 @@ Path: `src/nemo_safe_synthesizer/config/`
- TrainingHyperparams: training settings (learning rate, epochs, batch size, etc.)
- GenerateParameters: generation settings (temperature, top_p, num_records, etc.)
- EvaluationParameters: evaluation component toggles and settings
- PiiReplacerConfig: PII detection and replacement settings
- ReplacePiiConfig: placeholder for PII replacement v3
- DifferentialPrivacyHyperparams: DP training parameters (epsilon, delta, clipping norm)

### 2. Data Processing Pipeline

Path: `src/nemo_safe_synthesizer/data_processing/`

- Holdout (`holdout/`): splits data into train/test sets with stratification support
- NemoPII (`pii_replacer/`): detects PII entities (names, emails, SSN, etc.) and replaces with synthetic but realistic values
- `pii_replacer/`: placeholder for PII replacement v3; only evaluation result models remain
- ActionExecutor (`actions/`): executes data transformations (date normalization, distributions)
- ExampleAssembler (`assembler.py`): converts records to JSON format, tokenizes for model training, handles truncation and padding

Expand Down Expand Up @@ -395,4 +395,4 @@ The execution follows a clear pipeline: Data --> PII Replacement --> Training --
2. Custom Generation Backend: implement `GeneratorBackend` abstract class
3. Custom Evaluation Component: extend `Component` base class
4. Custom Data Actions: add to `data_processing/actions/`
5. Custom PII Detectors: extend NER pipeline
5. Custom evaluation metrics: extend the evaluation component interface
6 changes: 4 additions & 2 deletions docs/developer-guide/configuration_management.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ Pydantic model fields -> _collect_params() -> LeafParam / FlagParam -> click.opt
- Scalar fields become `LeafParam` options and preserve underscores in field
names, such as `--training__learning_rate`.
- Nullable sub-model fields (`SomeModel | None`) become nested leaf options
plus a disabling `FlagParam`.
plus a disabling `FlagParam`. A union that includes a model but not `None`
(for example `SomeModel | str`) stays a single leaf option and does not
get `--no-*`.

The generated Click options use `Field(description=...)` as CLI help text.

Expand All @@ -39,7 +41,7 @@ The generated Click options use `Field(description=...)` as CLI help text.
Optional sub-configs use `None` as the disabled signal. Do not add a second
boolean flag for the same feature.

For a field such as `replace_pii: PiiReplacerConfig | None`,
For a field such as `replace_pii: ReplacePiiConfig | None`,
`pydantic_options()` emits:

```bash
Expand Down
Loading
Loading