Skip to content

feat: move structured generation parameters to an object#595

Merged
mckornfield merged 4 commits into
mainfrom
move-structured-gen-parameters/mck
Jun 26, 2026
Merged

feat: move structured generation parameters to an object#595
mckornfield merged 4 commits into
mainfrom
move-structured-gen-parameters/mck

Conversation

@mckornfield

@mckornfield mckornfield commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Pre-Review Checklist

Ensure that the following pass:

  • mise run format && mise run check or via prek validation.
  • mise run test passes locally
  • mise run test:e2e passes locally
  • mise run test:ci-container passes locally (recommended)
  • GPU CI status check passes -- comment /sync on this PR to trigger a run (auto-triggers on ready-for-review)

Pre-Merge Checklist

  • New or updated tests for any fix or new behavior
  • Updated documentation for new features and behaviors, including docstrings for API docs.

Other Notes

Summary by CodeRabbit

  • Documentation

    • Updated configuration guides and examples to reflect restructured structured generation settings.
    • Updated CLI command references with new option naming format.
  • Chores

    • Reorganized internal configuration structure for generation parameters.
    • Updated configuration files and references to align with new structure.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Structured-generation configuration fields (use_structured_generation, structured_generation_backend, etc.) are migrated from flat GenerateParameters fields to a new nested StructuredGenerationParameters model. Legacy flat keys are preserved as deprecated property aliases with a before-validator migration path. Configuration parsing, CLI option registration, runtime consumers (regex_manager, vllm_backend, config_builder), all tests, e2e YAML configs, and documentation are updated to the new nested shape.

Changes

Structured-Generation Configuration Migration

Layer / File(s) Summary
Nested schema, legacy field migration, and public export
src/nemo_safe_synthesizer/config/generate.py, src/nemo_safe_synthesizer/config/__init__.py, tests/config/conftest.py, tests/config/test_generate.py
Introduces StructuredGenerationParameters with enabled, backend, schema_method, use_single_sequence and a structural-tag compatibility validator. Nests it under GenerateParameters.structured_generation with a before-validator that migrates legacy flat keys and deprecated alias properties. Exports the new class; updates config fixtures and test suite to validate both nested and legacy migration paths.
Section value extraction and ConfigBuilder merge
src/nemo_safe_synthesizer/config/parameters.py, src/nemo_safe_synthesizer/sdk/config_builder.py, tests/config/test_parameters.py, tests/sdk/test_process_data.py
Adds _section_values() helper and rewrites from_params() to use model_validate per section. Unifies ConfigBuilder._resolve_config with merge_dicts. Updates config-parameter and SDK resume-flow tests to assert generation.structured_generation.* fields.
CLI legacy alias registration and parser tests
src/nemo_safe_synthesizer/configurator/pydantic_click_options.py, tests/configurator/test_pydantic_click_options.py, tests/cli/test_utils.py, tests/conftest.py, tests/benchmarks/test_generation_structured_methods.py
Adds _LEGACY_CLI_OPTION_PATHS and apply_leaf_option() to register hidden legacy CLI aliases alongside canonical nested options. Adds end-to-end Click runner tests for canonical and legacy option routing. Updates CLI utils tests, YAML fixture, and benchmark CLI argument names.
Runtime consumers and generation test rewiring
src/nemo_safe_synthesizer/generation/regex_manager.py, src/nemo_safe_synthesizer/generation/vllm_backend.py, tests/generation/test_regex_manager.py, tests/generation/test_vllm_backend.py, tests/generation/test_vllm_shutdown.py, tests/generation/test_timeseries_backend.py
Switches build_json_based_regex, build_json_structural_tag, and vLLM structured-output construction to read from config.generation.structured_generation.*. Updates all fixture assignments and test assertions for single-sequence gating, backend/schema resolution, and structural-tag error messages.
e2e, smoke, and conftest config path migration
tests/e2e/required_configs/*.yaml, tests/e2e/test_dataset_config.py, tests/e2e/test_safe_synthesizer.py, tests/smoke/test_nss_structured_gen_gpu.py
Replaces legacy flat structured-generation keys with nested generation.structured_generation blocks in all e2e required YAML configs and test setup calls.
Docs and tutorial key-path updates
docs/user-guide/configuration.md, docs/user-guide/running.md, docs/user-guide/troubleshooting.md, docs/tutorials/differential-privacy.ipynb
Replaces flat structured-generation key references with nested generation.structured_generation.* paths throughout; adds a "Python Parameter Construction" subsection to configuration.md.
DCO members policy update
.github/dco.yaml
Adds require.members: false to the repository DCO configuration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • NVIDIA-NeMo/Safe-Synthesizer#554: Updates tests/sdk/test_process_data.py assertions around generation.structured_generation fields including schema_method, which are the same nested fields this PR migrates and asserts throughout the resume-flow tests.
  • NVIDIA-NeMo/Safe-Synthesizer#541: Modifies src/nemo_safe_synthesizer/config/generate.py and the vLLM structured-output path (VllmBackend._build_structured_output_params) for "auto"/"structural_tag" method resolution—the exact code paths this PR refactors into the nested generation.structured_generation.* shape.

Suggested labels

refactor, feature, test, docs

Suggested reviewers

  • kendrickb-nvidia
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: moving structured generation parameters from flat fields to a nested object model.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch move-structured-gen-parameters/mck

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added docs Documentation-only change feature New feature or request test Test-only addition or change refactor Internal restructuring with no behavior change labels Jun 11, 2026
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.17749% with 25 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/nemo_safe_synthesizer/config/generate.py 71.23% 21 Missing ⚠️
src/nemo_safe_synthesizer/config/parameters.py 85.00% 3 Missing ⚠️
tests/e2e/test_dataset_config.py 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR refactors the four flat structured-generation fields on GenerateParameters into a single nested StructuredGenerationParameters sub-model (generation.structured_generation.{enabled,backend,schema_method,use_single_sequence}), adding backward-compatible migration so old YAML configs, CLI flags, and SDK calls continue to work during the transition.

  • New StructuredGenerationParameters model promoted to __init__.py's public API; GenerateParameters replaces the four flat fields with a single structured_generation composite field backed by a mode=\"before\" migration validator that rewrites legacy flat keys into the nested shape.
  • Backward-compat layer ships three ways: a _migrate_legacy_structured_generation_fields model-validator for YAML/dict inputs, deprecated property stubs on GenerateParameters that emit DeprecationWarning, and hidden legacy Click options registered via _LEGACY_CLI_OPTION_PATHS.
  • _section_values helper replaces the old model_copy(update=kwargs) construction in from_params, and config_builder._resolve_config switches to merge_dicts + model_validate for deep-merge semantics; docs and all tests updated throughout.

Confidence Score: 5/5

Safe to merge — all callers of the four removed flat fields are updated, three independent compat layers (migration validator, deprecated property stubs with warnings, hidden CLI aliases) guard existing configs and scripts, and the new StructuredGenerationParameters sub-model carries the same validation logic as before.

Every code path that previously read or wrote the flat structured-generation fields has been updated. The migration validator, deprecated property stubs, and hidden CLI option aliases form a complete backward-compat net. The notebook JSON fix (single-quote form) addresses the previously reported parsing failure. Tests cover nested construction, flat-key migration, mixed-input precedence, and the CLI legacy aliases. No regressions were identified in the logic.

No files require special attention. The migration logic in generate.py and the _section_values helper in parameters.py are the most complex additions but are thoroughly covered by the new TestMixedInputMigration test class.

Important Files Changed

Filename Overview
src/nemo_safe_synthesizer/config/generate.py Introduces StructuredGenerationParameters; removes four flat fields from GenerateParameters; adds migration validator, deprecated property stubs with DeprecationWarning, and the LEGACY_FIELDS map.
src/nemo_safe_synthesizer/config/parameters.py Adds _section_values helper; from_params switches from model_copy(update=kwargs) to model_validate(_section_values(...)), enabling proper deep-merge and triggering migration validators.
src/nemo_safe_synthesizer/configurator/pydantic_click_options.py Adds _LEGACY_CLI_OPTION_PATHS and apply_leaf_option helper; registers hidden legacy CLI aliases using the same FieldInfo (type, help) as the canonical option.
src/nemo_safe_synthesizer/sdk/config_builder.py _resolve_config replaces model_copy(update=...) with merge_dicts + model_validate for all three input shapes, enabling deep-merge of nested dicts like structured_generation.
tests/config/test_generate.py Comprehensive new tests for nested construction, legacy flat-key migration, and mixed-input precedence; all updated to use the new nested API.
docs/tutorials/differential-privacy.ipynb Updated notebook cell from double-quote dict literal (which broke the .ipynb JSON) to single-quote form {'enabled': True}; resolves the previously reported JSONDecodeError.
docs/user-guide/troubleshooting.md Corrects non-existent field name training.number_of_input_records_to_sample to the actual field training.num_input_records_to_sample; updates structured-gen reference to new path.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class Parameters {
        +model_config: ConfigDict
        +from_yaml()
        +from_json()
        +to_yaml()
    }

    class GenerateParameters {
        +num_records: int
        +temperature: float
        +structured_generation: StructuredGenerationParameters
        +_STRUCTURED_GENERATION_LEGACY_FIELDS: ClassVar[dict]
        +_migrate_legacy_structured_generation_fields() model_validator
        +use_structured_generation: property (deprecated)
        +structured_generation_backend: property (deprecated)
        +structured_generation_schema_method: property (deprecated)
        +structured_generation_use_single_sequence: property (deprecated)
    }

    class StructuredGenerationParameters {
        +enabled: bool = False
        +backend: StructuredGenerationBackend = "auto"
        +schema_method: StructuredGenerationSchemaMethod = "auto"
        +use_single_sequence: bool = False
        +_validate_structural_tag_backend() model_validator
    }

    class SafeSynthesizerParameters {
        +generation: GenerateParameters
        +training: TrainingHyperparams
        +privacy: DifferentialPrivacyHyperparams
        +from_params(**kwargs) classmethod
    }

    Parameters <|-- GenerateParameters
    Parameters <|-- StructuredGenerationParameters
    Parameters <|-- SafeSynthesizerParameters
    GenerateParameters *-- StructuredGenerationParameters : structured_generation
    SafeSynthesizerParameters *-- GenerateParameters : generation
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
classDiagram
    class Parameters {
        +model_config: ConfigDict
        +from_yaml()
        +from_json()
        +to_yaml()
    }

    class GenerateParameters {
        +num_records: int
        +temperature: float
        +structured_generation: StructuredGenerationParameters
        +_STRUCTURED_GENERATION_LEGACY_FIELDS: ClassVar[dict]
        +_migrate_legacy_structured_generation_fields() model_validator
        +use_structured_generation: property (deprecated)
        +structured_generation_backend: property (deprecated)
        +structured_generation_schema_method: property (deprecated)
        +structured_generation_use_single_sequence: property (deprecated)
    }

    class StructuredGenerationParameters {
        +enabled: bool = False
        +backend: StructuredGenerationBackend = "auto"
        +schema_method: StructuredGenerationSchemaMethod = "auto"
        +use_single_sequence: bool = False
        +_validate_structural_tag_backend() model_validator
    }

    class SafeSynthesizerParameters {
        +generation: GenerateParameters
        +training: TrainingHyperparams
        +privacy: DifferentialPrivacyHyperparams
        +from_params(**kwargs) classmethod
    }

    Parameters <|-- GenerateParameters
    Parameters <|-- StructuredGenerationParameters
    Parameters <|-- SafeSynthesizerParameters
    GenerateParameters *-- StructuredGenerationParameters : structured_generation
    SafeSynthesizerParameters *-- GenerateParameters : generation
Loading

Reviews (8): Last reviewed commit: "chore: address more CR" | Re-trigger Greptile

Comment on lines +310 to +346
return values

@property
def use_structured_generation(self) -> bool:
"""Deprecated flat alias for ``structured_generation.enabled``."""
return self.structured_generation.enabled

@use_structured_generation.setter
def use_structured_generation(self, value: bool) -> None:
self.structured_generation.enabled = value

@property
def structured_generation_backend(self) -> StructuredGenerationBackend:
"""Deprecated flat alias for ``structured_generation.backend``."""
return self.structured_generation.backend

@structured_generation_backend.setter
def structured_generation_backend(self, value: StructuredGenerationBackend) -> None:
self.structured_generation.backend = value

@property
def structured_generation_schema_method(self) -> StructuredGenerationSchemaMethod:
"""Deprecated flat alias for ``structured_generation.schema_method``."""
return self.structured_generation.schema_method

@structured_generation_schema_method.setter
def structured_generation_schema_method(self, value: StructuredGenerationSchemaMethod) -> None:
self.structured_generation.schema_method = value

@property
def structured_generation_use_single_sequence(self) -> bool:
"""Deprecated flat alias for ``structured_generation.use_single_sequence``."""
return self.structured_generation.use_single_sequence

@structured_generation_use_single_sequence.setter
def structured_generation_use_single_sequence(self, value: bool) -> None:
self.structured_generation.use_single_sequence = value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Deprecated property stubs do not emit DeprecationWarning

The four deprecated property accessors and setters (use_structured_generation, structured_generation_backend, structured_generation_schema_method, structured_generation_use_single_sequence) silently redirect to the new nested model without notifying callers. Existing code that reads or writes these properties — including third-party integrations — will continue working but will never learn that the flat API has been deprecated. A warnings.warn("...", DeprecationWarning, stacklevel=2) in each getter (and setter) would make migration noise visible to users before the aliases are eventually removed.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agent (review-pr): Deprecated aliases now warn

This looks addressed in the current branch.

Comment on lines +69 to +83
def _section_values(kwargs: dict[str, Any], section: str) -> dict[str, Any]:
"""Return values for a nested section merged with flat compatibility kwargs."""
section_value = kwargs.get(section)
match section_value:
case BaseModel() as model:
section_data = model.model_dump()
case Mapping() as mapping:
section_data = dict(mapping)
case None:
section_data = {}
case _:
section_data = {section: section_value}

flat_values = {key: value for key, value in kwargs.items() if key != section}
return section_data | flat_values

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 _section_values passes cross-section keys into every sub-model's model_validate

For a call like from_params(generation={"num_records": 100}, training={"batch_size": 8}), the result of _section_values(kwargs, "generation") is {"num_records": 100, "training": {"batch_size": 8}}, so GenerateParameters.model_validate receives training as an unknown field. This is silently dropped only because Parameters uses extra="ignore". The existing design worked the same way with the old model_copy(update=kwargs) path, but switching to model_validate makes the dependency on extra="ignore" load-bearing: a future tightening to extra="forbid" would break every from_params call that mixes section-level dicts with flat kwargs. A brief comment at the call site noting this assumption would prevent a confusing failure later.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agent (review-pr): Tracking broader construction cleanup in #596

This concern is about the broader flat from_params(...) construction model rather than the structured-generation object specifically. #596 is already open to clean up the typed config/construction boundaries, so I would avoid expanding #595 into that refactor unless a small piece needs to be pulled forward for compatibility.

For this PR, I would keep the scope to direct migration fixes and focused tests around generation.structured_generation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0abb9a52-1211-4e27-8a5d-9161880ed3aa

📥 Commits

Reviewing files that changed from the base of the PR and between 907b5f3 and 274eeeb.

📒 Files selected for processing (30)
  • .github/dco.yaml
  • docs/tutorials/differential-privacy.ipynb
  • docs/user-guide/configuration.md
  • docs/user-guide/running.md
  • docs/user-guide/troubleshooting.md
  • src/nemo_safe_synthesizer/config/__init__.py
  • src/nemo_safe_synthesizer/config/generate.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/cli/test_utils.py
  • tests/config/conftest.py
  • tests/config/test_generate.py
  • tests/config/test_parameters.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/conftest.py
  • tests/e2e/required_configs/mistral-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/test_dataset_config.py
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/generation/test_timeseries_backend.py
  • tests/generation/test_vllm_backend.py
  • tests/generation/test_vllm_shutdown.py
  • tests/sdk/test_process_data.py
  • tests/smoke/test_nss_structured_gen_gpu.py
💤 Files with no reviewable changes (1)
  • tests/generation/test_timeseries_backend.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Unit Tests (3.12)
  • GitHub Check: Unit Tests (3.13)
  • GitHub Check: Unit Tests (3.11)
  • GitHub Check: Smoke Tests
🧰 Additional context used
📓 Path-based instructions (25)
tests/**

📄 CodeRabbit inference engine (.cursor/rules/repo-navigation.mdc)

tests/**: Mirror src/ directory structure in tests/ directory for test organization
Auto-mark tests by directory: tests/e2e/e2e, tests/smoke/smoke, otherwise default to unit

Files:

  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/e2e/required_configs/mistral-dp.yaml
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py

⚙️ CodeRabbit configuration file

tests/**:

Testing Guide

Comprehensive testing reference for Safe-Synthesizer developers. Covers commands, markers, test data, fixtures, and gotchas.

Read First

  1. tests/conftest.py -- auto-marking, load_test_dataset/load_test_dataframe, fixture_mock_processor pattern
  2. pytest.ini -- markers, asyncio, timeout
  3. tests/evaluation/conftest.py -- most complex: Faker-based make_df, nullable dtype conversion
  4. tests/generation/conftest.py -- JSONL/schema fixtures, fixture_valid_iris_dataset_jsonl_and_schema

Running Tests

All mise test tasks, grouped by scope:

mise run test                              # Unit (excludes slow, e2e, and smoke)
mise run test:unit-slow                    # Unit tests including slow (excludes e2e and smoke)
mise run test:smoke                        # CPU smoke tests (~few min, no GPU required)
mise run test:smoke:gpu                    # All staged GPU smoke tests (requires CUDA)
mise run test:smoke:gpu:train-only
mise run test:smoke:gpu:generation
mise run test:smoke:gpu:resume
mise run test:smoke:gpu:structured-generation
mise run test:smoke:gpu:timeseries
mise run test:smoke:gpu:smollm2
mise run test:e2e                          # All e2e (requires CUDA) -- runs default + dp
mise run test:e2e:default                  # e2e default (no-DP) tests only
mise run test:e2e:dp                       # e2e DP tests only
mise run test:ci                           # CI unit tests with coverage (excludes slow, e2e, gpu, smoke)
mise run test:ci-slow                      # CI slow tests with coverage
mise run test:ci-container                 # CI tests in a Linux container (Docker/Podman)

Run a single test:

uv run --frozen pytest tests/path/test_file.py::test_name -vvs -n0

Test runner: uv run --frozen pytest -n auto --dist loadscope -vv...

Files:

  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/e2e/required_configs/mistral-dp.yaml
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
**/*.{py,sh,yaml,yml,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

All source files (.py, .sh, .yaml, .yml, .md) must include SPDX copyright headers. Use mise run format to add them automatically

Files:

  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • docs/user-guide/configuration.md
  • src/nemo_safe_synthesizer/config/__init__.py
  • tests/e2e/required_configs/mistral-dp.yaml
  • docs/user-guide/troubleshooting.md
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • tests/benchmarks/test_generation_structured_methods.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • docs/user-guide/running.md
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • tests/sdk/test_process_data.py
  • src/nemo_safe_synthesizer/config/generate.py
**/*.{yaml,yml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

YAML configuration files must be checked for consistency. Run mise run check to validate YAML files

**/*.{yaml,yml}: Use 2-space indentation in YAML files.
Use colon-space for key-value pairs in YAML (: ).
Include SPDX copyright headers at the top of YAML files.
Use unquoted values in YAML unless special characters require them.
Include a newline at end of YAML files.

Files:

  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/e2e/required_configs/mistral-dp.yaml
**/*.{py,sh,yaml,yml}

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Include SPDX copyright headers in all source files using hash-comments for .py, .sh, .yaml, .yml files.

Files:

  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • src/nemo_safe_synthesizer/config/__init__.py
  • tests/e2e/required_configs/mistral-dp.yaml
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • tests/benchmarks/test_generation_structured_methods.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • tests/sdk/test_process_data.py
  • src/nemo_safe_synthesizer/config/generate.py
**/*

⚙️ CodeRabbit configuration file

**/*: Review as a senior maintainer for NeMo Safe Synthesizer. Prioritize issues that can change behavior, break user workflows, weaken privacy guarantees, hide failures, make tests unreliable, or create maintenance risk. Avoid generic style commentary unless it points to a concrete project convention that automated tools will not catch.
Comment only when the finding is actionable and tied to changed code. For each finding, state the impact, the condition that triggers it, and the smallest practical fix. Prefer one precise comment over broad advice. Do not ask for refactors outside the PR scope unless the changed code creates the problem.
Review type guidance: - Potential issue: use for correctness bugs, data loss, privacy leaks,
security risks, broken public APIs, invalid config behavior, missing
validation, hidden failures, nondeterministic tests, or CI breakage.

  • Refactor suggestion: use for local maintainability problems introduced
    by the diff when they have clear future cost, such as duplicated setup,
    unclear boundaries, over-mocking, avoidable complexity, or opaque test
    helpers.
  • Nitpick: avoid in chill mode. Do not emit formatting, import-order,
    wording, or style-only comments unless automated tools cannot catch the
    issue and it affects maintainability.

Severity guidance: - Critical: security/privacy leaks, data loss, training/test/holdout
contamination, or broken release/package/core pipeline execution.

  • Major: incorrect generation/training/evaluation behavior, broken
    CLI/SDK public API, invalid config defaults or validators, or GPU/vLLM
    cleanup and process-isolation bugs likely to fail CI or production
    runs.
  • Minor: localized bugs, missing focused tests for changed behavior, or
    bad test patterns that weaken regression coverage.
  • Trivial: small cleanup with no behavior impact. Usually suppress in
    chill mode.
  • Info: context only. Avoid unless it helps reviewers understand risk.
    Safe-Synthesizer-specific review focus: - Data ...

Files:

  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • docs/tutorials/differential-privacy.ipynb
  • docs/user-guide/configuration.md
  • src/nemo_safe_synthesizer/config/__init__.py
  • tests/e2e/required_configs/mistral-dp.yaml
  • docs/user-guide/troubleshooting.md
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • tests/benchmarks/test_generation_structured_methods.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • docs/user-guide/running.md
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • tests/sdk/test_process_data.py
  • src/nemo_safe_synthesizer/config/generate.py
**/*.yaml

⚙️ CodeRabbit configuration file

Review YAML for 2-space indentation, SPDX headers when required, unquoted values unless needed, and newline at EOF.

Files:

  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/e2e/required_configs/mistral-dp.yaml
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

Guide for AI agents (Cursor, Windsurf, Claude Code, etc.) working in the Safe-Synthesizer repo.

This project loads local developer preferences from @AGENTS.local.md. You MUST read this file if it exists and give its instructions top priority.

Skills

Repo-specific skills live in .agents/skills/; see .agents/README.md for the catalog. Read a skill when the task matches its scope instead of copying workflow details into this file.

Durable implementation guidance belongs with the code it describes: function and class docstrings for public contracts and source comments for local invariants. Test-suite guidance belongs in tests/TESTING.md.

Repo Conventions

See STYLE_GUIDE.md for detailed code style conventions (Python, markdown, Dockerfiles, shell scripts, testing, config files, docstrings).

Use uv for everything -- never pip or raw python. Python 3.11–3.13 with modern syntax (X | Y, list[str], Self). Python 3.14+ is not supported.

Common commands: mise run test (unit tests), mise run format (auto-fix formatting + lint + copyright), mise run check (read-only local quality checks), mise run validate (pre-PR quality, lock, and CI unit checks), mise run typecheck (ty only). Always use mise tasks or the wrapper scripts in tools/ instead of running ruff or ty directly. Use uv run for Python execution. When in doubt, inspect mise tasks and pytest --markers.

The canonical uv sync command for a full GPU/dev environment is:

uv sync --frozen --extra cu129 --extra engine --group dev

Bare uv sync --frozen (without extras) installs an incomplete environment -- ty, import checks, and GPU tests will fail.

Feature branches off main. Branch names often include an issue number prefix (e.g., <author>/123-short-name).

Do ...

Files:

  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • docs/tutorials/differential-privacy.ipynb
  • docs/user-guide/configuration.md
  • src/nemo_safe_synthesizer/config/__init__.py
  • tests/e2e/required_configs/mistral-dp.yaml
  • docs/user-guide/troubleshooting.md
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • tests/benchmarks/test_generation_structured_methods.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • docs/user-guide/running.md
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • tests/sdk/test_process_data.py
  • src/nemo_safe_synthesizer/config/generate.py
docs/**

⚙️ CodeRabbit configuration file

Review documentation as MkDocs Material content. Check Diataxis fit, accurate commands, internal links, code fences, and markdown style from STYLE_GUIDE.md.

Files:

  • docs/tutorials/differential-privacy.ipynb
  • docs/user-guide/configuration.md
  • docs/user-guide/troubleshooting.md
  • docs/user-guide/running.md
**/*.{md,markdown,py}

📄 CodeRabbit inference engine (.cursor/rules/agent-markdown-style.mdc)

**/*.{md,markdown,py}: Avoid decorative bold (**text**) in list items, body text, and docstrings; use structural cues (headers, list markers, colons, backticks) for emphasis instead
Use backticks for code identifiers, paths, and CLI commands in markdown and docstrings

Files:

  • docs/user-guide/configuration.md
  • src/nemo_safe_synthesizer/config/__init__.py
  • docs/user-guide/troubleshooting.md
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • tests/benchmarks/test_generation_structured_methods.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • docs/user-guide/running.md
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • tests/sdk/test_process_data.py
  • src/nemo_safe_synthesizer/config/generate.py
**/*.{md,markdown}

📄 CodeRabbit inference engine (.cursor/rules/agent-markdown-style.mdc)

**/*.{md,markdown}: Bold is acceptable only in markdown tables where it's the conventional way to mark header-like cells in the body
Use ## headers to segment markdown sections instead of bold text
Use -- (em-dash) instead of - (hyphen) for asides in markdown

Files:

  • docs/user-guide/configuration.md
  • docs/user-guide/troubleshooting.md
  • docs/user-guide/running.md
docs/**/*.md

📄 CodeRabbit inference engine (.cursor/rules/writing-docs.mdc)

docs/**/*.md: Use MkDocs Material admonition syntax (!!! note, !!! warning, ??? tip) for highlighting important information and collapsible sections in documentation
Use MkDocs Material tabs syntax (=== "Label") to present alternative views or language-specific examples in documentation
Use code block syntax with title and highlight line parameters (title="filename", hl_lines="2 3") for code examples in documentation
Use Mermaid diagram syntax (```mermaid flowchart, etc.) for visualizations in documentation

Documentation markdown files must follow MkDocs Material Markdown extensions syntax including admonitions, content tabs, code blocks with annotations, and Mermaid diagrams

docs/**/*.md: Classify documentation pages in docs/ as tutorial, how-to, explanation, or reference per the Diataxis framework.
Use MkDocs Material syntax for documentation: admonitions (!!! note), tabs (===), code blocks with titles and highlights.

Files:

  • docs/user-guide/configuration.md
  • docs/user-guide/troubleshooting.md
  • docs/user-guide/running.md
**/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Markdown documentation must be checked for consistency and formatting. Run mise run check to validate markdown files

**/*.md: No decorative **bold** in markdown body text, list items, or docstrings. Use headers, list markers, colons, and backticks for structure.
Use -- (em-dash) for asides in markdown, not - (hyphen).
Use single backticks for code identifiers, paths, and CLI commands in markdown.
Mermaid diagrams: no spaces in node IDs, quote labels with special characters, no explicit colors or styles.
Use HTML-comment SPDX headers for .md files, or hash-comment headers inside YAML frontmatter when markdown starts with ---.

Files:

  • docs/user-guide/configuration.md
  • docs/user-guide/troubleshooting.md
  • docs/user-guide/running.md
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place durable implementation guidance in function and class docstrings for public contracts and source comments for local invariants
Target Python 3.11–3.13 with modern syntax (X | Y, list[str], Self). Python 3.14+ is not supported

**/*.py: Python source files must use ruff for formatting and linting. Run mise run format to auto-fix formatting and linting issues, and mise run check for read-only quality checks
Use ty for Python type checking. Run mise run check to execute type checking on all tracked files

**/*.py: Use Field(description=...) as the canonical field docstring for Pydantic models.
Use assignment style (type = Field(default=..., description="...")) as the default for Pydantic model fields rather than Annotated-only patterns.
Use Annotated only when the field carries additional metadata beyond Field() -- ValueValidator, AutoParam, DependsOnValidator, reusable constrained type aliases, nested-type constraints, or discriminated unions.
Use @dataclass(frozen=True) for immutable value objects and validators. Mutable @dataclass acceptable for builders, accumulators, and pipeline state.
Use field(default_factory=list) for mutable defaults in dataclasses, never = [].
Use StrEnum for string-valued enums used in configs/serialization. Plain Enum for internal-only named constants.
Use X | Y not Optional[X] or Union[X, Y] in type hints.
Use list[str] not List[str], dict[str, int] not Dict[str, int] in type hints.
Use Self for fluent method returns in type hints.
Use collection ABCs for function arguments (Sequence, Mapping, Iterable) so callers can pass any compatible container; concrete types for return values.
Use Protocol for structural subtyping when you need duck-typing boundaries.
Avoid Any in type hints -- prefer object, generics, or Protocol.
Use TYPE_CHECKING guards for heavy imports (pandas, torch, transformers); not needed for stdlib or lightweight imports.
Prefer `m...

Files:

  • src/nemo_safe_synthesizer/config/__init__.py
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • tests/benchmarks/test_generation_structured_methods.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • tests/sdk/test_process_data.py
  • src/nemo_safe_synthesizer/config/generate.py
src/nemo_safe_synthesizer/**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

src/nemo_safe_synthesizer/**/*.py: Source code must remain Python 3.11 syntax-compatible. Do not use Python 3.12-only syntax such as PEP 695 type statements or bracketed generic class/function parameters in shared package code
Write Python docstrings in Google style format so they auto-generate into API reference pages via mkdocstrings and gen-files plugins

Files:

  • src/nemo_safe_synthesizer/config/__init__.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • src/nemo_safe_synthesizer/config/generate.py
**/config/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Use NSSBaseModel for config/parameter models in config/ which define the user-facing configuration of NSS. Raw BaseModel or module-specific bases for data transfer objects and internal structures.

Files:

  • src/nemo_safe_synthesizer/config/__init__.py
  • tests/config/conftest.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_generate.py
  • src/nemo_safe_synthesizer/config/generate.py
src/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

src/**/*.py: Use observability.get_logger(__name__) -- never logging.getLogger() or structlog.get_logger() directly.
Use category loggers: .runtime for internals, .user for progress/results, .system for system events.
Never use print() for operational output. Use click.echo() for CLI output or sys.stdout.write() for raw output in tools.
Use extra={} for data that downstream tools should query or aggregate in log calls (metrics, counts, durations). f-strings are fine for human-readable context.
Raise from the custom error hierarchy with dual inheritance: SafeSynthesizerError (base), UserError, DataError, ParameterError, GenerationError, InternalError.
Prefer clamping/saturation over raising when out-of-range inputs shouldn't crash the system -- return a bounded value with a log warning.
Error messages must precisely match the actual error condition. Use !r for repr of interpolated pieces to clearly identify them.
Use relative imports in src/ (from ..observability import get_logger), absolute imports in tests/ (from nemo_safe_synthesizer.observability import get_logger).
Use pathlib.Path instead of os.path. Tolerate os.path only in vendored/tooling scripts.
Do not use print() statements in library code. Use get_logger(__name__) from observability.py or click.echo() for CLI. print() is fine in tests, standalone scripts, and tooling.
Do not use assert for validation in library code. Use if/raise for input validation. assert is fine in tests where pytest relies on it.

Files:

  • src/nemo_safe_synthesizer/config/__init__.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • src/nemo_safe_synthesizer/config/generate.py

⚙️ CodeRabbit configuration file

Review library code against STYLE_GUIDE.md. Focus on behavior, API contracts, error handling, resource cleanup, typing, logging, and user-facing failures. Public APIs and nontrivial functions need Google-style docstrings.

Files:

  • src/nemo_safe_synthesizer/config/__init__.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • src/nemo_safe_synthesizer/config/generate.py
src/**/__init__.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Every directory under src/ that contains Python files must include an __init__.py file, even if empty.

Files:

  • src/nemo_safe_synthesizer/config/__init__.py
src/nemo_safe_synthesizer/config/**/*.py

⚙️ CodeRabbit configuration file

Treat config changes as user-facing API changes. Check Pydantic field descriptions, defaults, validators, aliases, override behavior, CLI help text impact, YAML compatibility, and documented parameter semantics.

Files:

  • src/nemo_safe_synthesizer/config/__init__.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
**/test_*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use the unit marker instead of the deprecated unit_test marker for test identification

Files:

  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
tests/**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

tests/**/*.py: New features must include tests before submitting a PR
Bug fixes must include regression tests before submitting a PR

tests/**/*.py: Use file naming test_*.py, class naming Test*, function naming test_<module>_<expected_behavior> for test files.
Use function-scoped fixtures by default. Session scope only when empirically justified by test runtime.
Use bare assert as primary assertion style; pytest.raises() with match= for exceptions; pytest.approx() for floating-point comparisons.
Docstrings are optional for simple tests, recommended for complex/e2e tests explaining purpose.
Markers are auto-assigned by path via pytest_collection_modifyitems (/e2e/ -> e2e, /smoke/ -> smoke, default -> unit). Use explicit markers: @pytest.mark.slow, @pytest.mark.requires_gpu, @pytest.mark.timeout().
Use tmp_path fixture for file operations in tests, never write to the repo tree.
Mark CUDA-dependent tests with @pytest.mark.e2e, @pytest.mark.smoke, or @pytest.mark.requires_gpu.
Mock only external boundaries in tests, not internal implementation details.
Ensure test isolation: no shared mutable state or execution-order dependencies between tests. Include required setup in the test or a fixture.
Use @pytest.mark.parametrize for testing multiple input combinations rather than copy-pasting similar tests.

tests/**/*.py: Assign exactly one of three pytest category markers (unit, smoke, e2e) to every test function
Markers 'slow' and 'requires_gpu' modify the three category markers (unit, smoke, e2e) to indicate when tests should be run or if separate pytest invocations are required
Use 'pytest.mark.noautouse' to skip autouse fixtures for specific tests
Auto-marking via pytest_collection_modifyitems assigns category markers based on test file path: '/e2e/' → 'e2e', '/smoke/' → 'smoke', no match → 'unit', only if no category marker is already present
Use load_test_dataset(filename) from root conftest.py to load HuggingFace Dataset...

Files:

  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py

⚙️ CodeRabbit configuration file

Review tests against tests/TESTING.md. Check marker usage, fixture naming, tmp_path usage, determinism, and GPU/vLLM process-isolation requirements. Flag slop tests that only check that code runs, assert result is not None when stronger invariants exist, over-mock internal implementation details, patch around the bug instead of reproducing it, or add broad snapshot/golden churn without a clear contract. Flag change detector tests that fail on harmless refactors, formatting, record ordering, incidental wording, or private implementation details without demonstrating a behavior regression. Prefer existing fixtures or focused new fixtures for repeated setup; keep tests DRY when reasonable without making the behavior under test opaque. print() is allowed in tests.

Files:

  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
tests/**/conftest.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Use fixture_ prefix convention for test fixtures for grep-ability. Add a one-line docstring describing the fixture's purpose and data.

tests/**/conftest.py: Name dataset and tokenizer fixtures with 'fixture_' prefix (e.g., fixture_iris_dataset, fixture_dow_jones_index_dataset, fixture_tinyllama_config)
Scope tokenizer fixtures as function-scoped since they are expensive to load, and scope most other fixtures as function-scoped except session-scoped cache directories
Convert nullable dtypes to pd.Int64Dtype() or pd.BooleanDtype() before assigning np.nan in test dataframes to avoid Pandas dtype errors
Seed Faker instances with both fake.seed_instance(seed) and random.seed(seed) for reproducible test data generation

Files:

  • tests/config/conftest.py
  • tests/conftest.py
.github/**

⚙️ CodeRabbit configuration file

Review GitHub configuration for branch protection expectations, CODEOWNERS alignment, least privilege permissions, pinned actions where practical, and consistency with CONTRIBUTING.md.

Files:

  • .github/dco.yaml
src/nemo_safe_synthesizer/generation/**/*.py

⚙️ CodeRabbit configuration file

Review generation changes for retry loops, stopping conditions, invalid record handling, regex/structured output contracts, backend teardown, memory cleanup, and vLLM assumptions.

Files:

  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
tests/conftest.py

📄 CodeRabbit inference engine (.cursor/rules/repo-navigation.mdc)

Store shared test fixtures in tests/conftest.py

Files:

  • tests/conftest.py
src/nemo_safe_synthesizer/configurator/**/*.py

⚙️ CodeRabbit configuration file

Review Pydantic-to-Click mapping carefully. Check option names, type conversion, nullable sub-config behavior, validation errors, help text, and compatibility with parse_overrides().

Files:

  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
🧠 Learnings (2)
📚 Learning: 2026-05-27T22:20:37.354Z
Learnt from: kendrickb-nvidia
Repo: NVIDIA-NeMo/Safe-Synthesizer PR: 520
File: tests/generation/test_vllm_backend.py:556-587
Timestamp: 2026-05-27T22:20:37.354Z
Learning: In NVIDIA-NeMo/Safe-Synthesizer, `tests/conftest.py`’s `pytest_collection_modifyitems` hook applies pytest category markers automatically based on each test file’s path: tests under `/e2e/` get `pytest.mark.e2e`, tests under `/smoke/` get `pytest.mark.smoke`, and all other tests get `pytest.mark.unit`. Therefore, when reviewing pytest tests outside `tests/e2e/` and `tests/smoke/`, do not flag missing explicit `pytest.mark.unit` decorators on test classes/functions as an issue (the hook will add them during collection). If a new test directory/category is introduced, ensure the hook is updated so it’s categorized correctly.

Applied to files:

  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/config/conftest.py
  • tests/generation/test_vllm_shutdown.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/conftest.py
  • tests/e2e/test_dataset_config.py
  • tests/config/test_parameters.py
  • tests/generation/test_vllm_backend.py
  • tests/config/test_generate.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
📚 Learning: 2026-06-04T16:14:09.868Z
Learnt from: binaryaaron
Repo: NVIDIA-NeMo/Safe-Synthesizer PR: 526
File: tests/generation/test_vllm_backend.py:399-509
Timestamp: 2026-06-04T16:14:09.868Z
Learning: In this repo, only apply `pytest.mark.vllm` to smoke tests under `tests/smoke/` that actually run real vLLM GPU generation and therefore require per-file process isolation (e.g., `test-smoke-gpu-*` Makefile targets). Do not apply `pytest.mark.vllm` to unit-style tests under `tests/generation/` that merely import `vllm_backend` but never instantiate a real vLLM engine and never call `.generate()` (GPU not required). Note that `tests/conftest.py` auto-marks these as `unit` via `pytest_collection_modifyitems`, and `vllm` is not among the auto-mark categories—so if a test in `tests/generation/` has `vllm`, it should be treated as a review issue unless it meets the real GPU generation criteria above.

Applied to files:

  • tests/generation/test_regex_manager.py
  • tests/generation/test_vllm_shutdown.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/generation/test_vllm_backend.py
🔇 Additional comments (19)
docs/tutorials/differential-privacy.ipynb (1)

162-162: LGTM!

docs/user-guide/configuration.md (1)

161-164: LGTM!

tests/e2e/required_configs/mistral-dp.yaml (1)

8-10: LGTM!

tests/e2e/required_configs/smollm3-dp.yaml (1)

8-9: LGTM!

tests/e2e/required_configs/tinyllama-dp.yaml (1)

8-10: LGTM!

tests/e2e/test_dataset_config.py (1)

147-147: LGTM!

tests/e2e/test_safe_synthesizer.py (1)

55-58: LGTM!

tests/smoke/test_nss_structured_gen_gpu.py (1)

38-42: LGTM!

tests/config/conftest.py (1)

15-15: LGTM!

Also applies to: 43-46

tests/config/test_generate.py (1)

11-12: LGTM!

Also applies to: 39-119

src/nemo_safe_synthesizer/configurator/pydantic_click_options.py (1)

35-41: LGTM!

Also applies to: 320-353

tests/cli/test_utils.py (1)

273-275: LGTM!

Also applies to: 302-303, 368-369

tests/benchmarks/test_generation_structured_methods.py (1)

200-209: LGTM!

tests/conftest.py (1)

112-113: LGTM!

src/nemo_safe_synthesizer/generation/regex_manager.py (1)

363-363: LGTM!

Also applies to: 426-426

src/nemo_safe_synthesizer/generation/vllm_backend.py (1)

293-293: LGTM!

Also applies to: 347-347, 352-353, 369-369

tests/generation/test_regex_manager.py (1)

29-29: LGTM!

Also applies to: 110-110, 171-171, 566-566, 577-577, 595-596, 673-673, 676-676, 698-698, 701-701, 724-724, 729-729

tests/generation/test_vllm_backend.py (1)

103-104: LGTM!

Also applies to: 111-113, 120-122, 129-131, 268-268, 300-300, 332-332, 340-340

tests/generation/test_vllm_shutdown.py (1)

38-38: LGTM!

Comment thread .github/dco.yaml Outdated
Comment thread .github/dco.yaml Outdated
Comment thread docs/user-guide/running.md Outdated
Comment thread docs/user-guide/running.md Outdated
Comment thread docs/user-guide/troubleshooting.md Outdated
Comment thread src/nemo_safe_synthesizer/config/parameters.py
Comment thread tests/configurator/test_pydantic_click_options.py Outdated
@mckornfield
mckornfield force-pushed the move-structured-gen-parameters/mck branch from 274eeeb to 59057eb Compare June 12, 2026 20:40
@coderabbitai coderabbitai Bot removed docs Documentation-only change feature New feature or request test Test-only addition or change refactor Internal restructuring with no behavior change labels Jun 12, 2026
@@ -159,7 +159,7 @@
" .with_data_source(df) # .with_replace_pii(enable=False) to disable PII replacement\n",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unescaped double quotes break the notebook's JSON

The Jupyter notebook format stores cell source as a JSON array of strings. Python's dict literal {"enabled": True} contains double-quote characters that must be escaped as \" inside a JSON string. The file currently lacks this escaping, making the entire .ipynb file invalid JSON — Jupyter will refuse to open it.

Confirmed: python3 -c "import json,sys; json.load(sys.stdin)" < docs/tutorials/differential-privacy.ipynb exits with a JSONDecodeError. Use single quotes in the Python example ({'enabled': True}) or re-save the notebook through Jupyter which will escape the inner quotes automatically.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agent (review-pr): Notebook JSON issue is valid

Agree, this still needs a fix; render it in notebook to verify.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/configurator/test_pydantic_click_options.py (1)

497-508: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Expand legacy CLI alias coverage for all structured-generation compatibility paths.

The legacy alias test currently covers only backend, but _LEGACY_CLI_OPTION_PATHS defines three additional aliases (enabled, schema_method, use_single_sequence) that remain untested. A regression in any of those paths would not be caught.

Parameterize this test to cover all four structured-generation legacy aliases and assert the correct parsed override structure for each.

Proposed parameterized fix
-def test_structured_generation_legacy_option_end_to_end_via_click_runner():
-    """Legacy flat structured-generation CLI aliases remain accepted during migration."""
+@pytest.mark.parametrize(
+    "legacy_option,expected_key,value",
+    [
+        ("--generation__use_structured_generation", "use_structured_generation", "true"),
+        ("--generation__structured_generation_backend", "structured_generation_backend", "outlines"),
+        ("--generation__structured_generation_schema_method", "structured_generation_schema_method", "json"),
+        ("--generation__structured_generation_use_single_sequence", "structured_generation_use_single_sequence", "true"),
+    ],
+)
+def test_structured_generation_legacy_option_end_to_end_via_click_runner(legacy_option, expected_key, value):
+    """Legacy flat structured-generation CLI aliases remain accepted during migration."""
     captured: dict = {}

     `@pydantic_options`(SafeSynthesizerParameters, field_separator="__")
     `@click.command`()
     def cmd(**kwargs):
         captured.update(parse_overrides(kwargs))

-    result = CliRunner().invoke(cmd, ["--generation__structured_generation_backend", "outlines"])
+    result = CliRunner().invoke(cmd, [legacy_option, value])
     assert result.exit_code == 0, result.output
-    assert captured["generation"]["structured_generation_backend"] == "outlines"
+    assert captured["generation"][expected_key] == value
🧹 Nitpick comments (1)
tests/e2e/required_configs/smollm3-dp.yaml (1)

9-9: Clarify the YAML comment intent: it matches e2e behavior (auto for clinc_oos; outlines forced only for dow_jones_index)
smollm3-dp.yaml leaves generation.structured_generation.backend unset, and test_clinc_oos_dataset doesn’t override it—so the “auto backend works better … on clinc_oos” comment is aligned with current tests. test_dow_jones_index_dataset is the only place that forces outlines for smollm3-dp.yaml, so clarify that dow_jones_index intentionally uses outlines to prevent confusion.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 40e628a8-5c4f-40fd-ac30-00d61a2fd77e

📥 Commits

Reviewing files that changed from the base of the PR and between 274eeeb and 59057eb.

📒 Files selected for processing (31)
  • .github/dco.yaml
  • docs/tutorials/differential-privacy.ipynb
  • docs/user-guide/configuration.md
  • docs/user-guide/running.md
  • docs/user-guide/troubleshooting.md
  • src/nemo_safe_synthesizer/config/__init__.py
  • src/nemo_safe_synthesizer/config/generate.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • src/nemo_safe_synthesizer/telemetry.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/cli/test_utils.py
  • tests/config/conftest.py
  • tests/config/test_generate.py
  • tests/config/test_parameters.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/conftest.py
  • tests/e2e/required_configs/mistral-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/test_dataset_config.py
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/generation/test_timeseries_backend.py
  • tests/generation/test_vllm_backend.py
  • tests/generation/test_vllm_shutdown.py
  • tests/sdk/test_process_data.py
  • tests/smoke/test_nss_structured_gen_gpu.py
💤 Files with no reviewable changes (1)
  • tests/generation/test_timeseries_backend.py
✅ Files skipped from review due to trivial changes (8)
  • .github/dco.yaml
  • docs/user-guide/troubleshooting.md
  • docs/user-guide/configuration.md
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/e2e/required_configs/mistral-dp.yaml
  • src/nemo_safe_synthesizer/telemetry.py
  • tests/conftest.py
  • docs/user-guide/running.md
🚧 Files skipped from review as they are similar to previous changes (20)
  • docs/tutorials/differential-privacy.ipynb
  • src/nemo_safe_synthesizer/config/init.py
  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/generation/test_vllm_shutdown.py
  • tests/e2e/test_dataset_config.py
  • tests/e2e/test_safe_synthesizer.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/sdk/test_process_data.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • tests/config/conftest.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • tests/generation/test_vllm_backend.py
  • tests/generation/test_regex_manager.py
  • tests/cli/test_utils.py
  • tests/config/test_generate.py
  • src/nemo_safe_synthesizer/config/generate.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
tests/**

📄 CodeRabbit inference engine (.cursor/rules/repo-navigation.mdc)

tests/**: Mirror src/ directory structure in tests/ directory for test organization
Auto-mark tests by directory: tests/e2e/e2e, tests/smoke/smoke, otherwise default to unit

Mirror source code directory structure in tests directory (e.g., tests/training/, tests/generation/ parallel to source structure)

Files:

  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/configurator/test_pydantic_click_options.py

⚙️ CodeRabbit configuration file

tests/**:

Testing Guide

Comprehensive testing reference for Safe-Synthesizer developers. Covers commands, markers, test data, fixtures, and gotchas.

Read First

  1. tests/conftest.py -- auto-marking, load_test_dataset/load_test_dataframe, fixture_mock_processor pattern
  2. pytest.ini -- markers, asyncio, timeout
  3. tests/evaluation/conftest.py -- most complex: Faker-based make_df, nullable dtype conversion
  4. tests/generation/conftest.py -- JSONL/schema fixtures, fixture_valid_iris_dataset_jsonl_and_schema

Running Tests

All mise test tasks, grouped by scope:

mise run test                              # Unit (excludes slow, e2e, and smoke)
mise run test:unit-slow                    # Unit tests including slow (excludes e2e and smoke)
mise run test:smoke                        # CPU smoke tests (~few min, no GPU required)
mise run test:smoke:gpu                    # All staged GPU smoke tests (requires CUDA)
mise run test:smoke:gpu:train-only
mise run test:smoke:gpu:generation
mise run test:smoke:gpu:resume
mise run test:smoke:gpu:structured-generation
mise run test:smoke:gpu:timeseries
mise run test:smoke:gpu:smollm2
mise run test:e2e                          # All e2e (requires CUDA) -- runs default + dp
mise run test:e2e:default                  # e2e default (no-DP) tests only
mise run test:e2e:dp                       # e2e DP tests only
mise run test:ci                           # CI unit tests with coverage (excludes slow, e2e, gpu, smoke)
mise run test:ci-slow                      # CI slow tests with coverage
mise run test:ci-container                 # CI tests in a Linux container (Docker/Podman)

Run a single test:

uv run --frozen pytest tests/path/test_file.py::test_name -vvs -n0

Test runner: uv run --frozen pytest -n auto --dist loadscope -vv...

Files:

  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/configurator/test_pydantic_click_options.py
**/*.{yaml,yml}

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

**/*.{yaml,yml}: Use 2-space indentation in YAML files.
Use colon-space for key-value pairs in YAML (: ).
Include SPDX copyright headers at the top of YAML files.
Use unquoted values in YAML unless special characters require them.
Include a newline at end of YAML files.

Files:

  • tests/e2e/required_configs/smollm3-dp.yaml
**/*.{py,sh,yaml,yml}

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Include SPDX copyright headers in all source files using hash-comments for .py, .sh, .yaml, .yml files.

Files:

  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/configurator/test_pydantic_click_options.py
**/*.{py,sh,yaml,yml,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

All source files (.py, .sh, .yaml, .yml, .md) require SPDX copyright headers; mise run format adds them automatically

Files:

  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/configurator/test_pydantic_click_options.py
**/*

⚙️ CodeRabbit configuration file

**/*: Review as a senior maintainer for NeMo Safe Synthesizer. Prioritize issues that can change behavior, break user workflows, weaken privacy guarantees, hide failures, make tests unreliable, or create maintenance risk. Avoid generic style commentary unless it points to a concrete project convention that automated tools will not catch.
Comment only when the finding is actionable and tied to changed code. For each finding, state the impact, the condition that triggers it, and the smallest practical fix. Prefer one precise comment over broad advice. Do not ask for refactors outside the PR scope unless the changed code creates the problem.
Review type guidance: - Potential issue: use for correctness bugs, data loss, privacy leaks,
security risks, broken public APIs, invalid config behavior, missing
validation, hidden failures, nondeterministic tests, or CI breakage.

  • Refactor suggestion: use for local maintainability problems introduced
    by the diff when they have clear future cost, such as duplicated setup,
    unclear boundaries, over-mocking, avoidable complexity, or opaque test
    helpers.
  • Nitpick: avoid in chill mode. Do not emit formatting, import-order,
    wording, or style-only comments unless automated tools cannot catch the
    issue and it affects maintainability.

Severity guidance: - Critical: security/privacy leaks, data loss, training/test/holdout
contamination, or broken release/package/core pipeline execution.

  • Major: incorrect generation/training/evaluation behavior, broken
    CLI/SDK public API, invalid config defaults or validators, or GPU/vLLM
    cleanup and process-isolation bugs likely to fail CI or production
    runs.
  • Minor: localized bugs, missing focused tests for changed behavior, or
    bad test patterns that weaken regression coverage.
  • Trivial: small cleanup with no behavior impact. Usually suppress in
    chill mode.
  • Info: context only. Avoid unless it helps reviewers understand risk.
    Safe-Synthesizer-specific review focus: - Data ...

Files:

  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/configurator/test_pydantic_click_options.py
**/*.yaml

⚙️ CodeRabbit configuration file

Review YAML for 2-space indentation, SPDX headers when required, unquoted values unless needed, and newline at EOF.

Files:

  • tests/e2e/required_configs/smollm3-dp.yaml
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

Guide for AI agents (Cursor, Windsurf, Claude Code, etc.) working in the Safe-Synthesizer repo.

This project loads local developer preferences from @AGENTS.local.md. You MUST read this file if it exists and give its instructions top priority.

Skills

Repo-specific skills live in .agents/skills/; see .agents/README.md for the catalog. Read a skill when the task matches its scope instead of copying workflow details into this file.

Durable implementation guidance belongs with the code it describes: function and class docstrings for public contracts and source comments for local invariants. Test-suite guidance belongs in tests/TESTING.md.

Repo Conventions

See STYLE_GUIDE.md for detailed code style conventions (Python, markdown, Dockerfiles, shell scripts, testing, config files, docstrings).

Use uv for everything -- never pip or raw python. Python 3.11–3.13 with modern syntax (X | Y, list[str], Self). Python 3.14+ is not supported.

Common commands: mise run test (unit tests), mise run format (auto-fix formatting + lint + copyright), mise run check (read-only local quality checks), mise run validate (pre-PR quality, lock, and CI unit checks), mise run typecheck (ty only). Always use mise tasks or the wrapper scripts in tools/ instead of running ruff or ty directly. Use uv run for Python execution. When in doubt, inspect mise tasks and pytest --markers.

The canonical uv sync command for a full GPU/dev environment is:

uv sync --frozen --extra cu129 --extra engine --group dev

Bare uv sync --frozen (without extras) installs an incomplete environment -- ty, import checks, and GPU tests will fail.

Feature branches off main. Branch names often include an issue number prefix (e.g., <author>/123-short-name).

Do ...

Files:

  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/configurator/test_pydantic_click_options.py
**/*.{md,markdown,py}

📄 CodeRabbit inference engine (.cursor/rules/agent-markdown-style.mdc)

**/*.{md,markdown,py}: Avoid decorative bold (**text**) in list items, body text, and docstrings; use structural cues (headers, list markers, colons, backticks) for emphasis instead
Use backticks for code identifiers, paths, and CLI commands in markdown and docstrings

Files:

  • tests/configurator/test_pydantic_click_options.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place durable implementation guidance in function and class docstrings for public contracts and source comments for local invariants
Target Python 3.11–3.13 with modern syntax (X | Y, list[str], Self). Python 3.14+ is not supported

**/*.py: Use Field(description=...) as the canonical field docstring for Pydantic models.
Use assignment style (type = Field(default=..., description="...")) as the default for Pydantic model fields rather than Annotated-only patterns.
Use Annotated only when the field carries additional metadata beyond Field() -- ValueValidator, AutoParam, DependsOnValidator, reusable constrained type aliases, nested-type constraints, or discriminated unions.
Use @dataclass(frozen=True) for immutable value objects and validators. Mutable @dataclass acceptable for builders, accumulators, and pipeline state.
Use field(default_factory=list) for mutable defaults in dataclasses, never = [].
Use StrEnum for string-valued enums used in configs/serialization. Plain Enum for internal-only named constants.
Use X | Y not Optional[X] or Union[X, Y] in type hints.
Use list[str] not List[str], dict[str, int] not Dict[str, int] in type hints.
Use Self for fluent method returns in type hints.
Use collection ABCs for function arguments (Sequence, Mapping, Iterable) so callers can pass any compatible container; concrete types for return values.
Use Protocol for structural subtyping when you need duck-typing boundaries.
Avoid Any in type hints -- prefer object, generics, or Protocol.
Use TYPE_CHECKING guards for heavy imports (pandas, torch, transformers); not needed for stdlib or lightweight imports.
Prefer match/case for dispatch on types or tagged values. if/elif is fine for simple boolean predicates.
Use comprehensions over imperative loops where intent is clearer. No multiple for clauses -- optimize for readability, not conciseness.
Use builder pattern with with_* methods...

Files:

  • tests/configurator/test_pydantic_click_options.py
**/test_*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use the unit marker instead of the deprecated unit_test marker for test identification

Files:

  • tests/configurator/test_pydantic_click_options.py
tests/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

tests/**/*.py: Use file naming test_*.py, class naming Test*, function naming test_<module>_<expected_behavior> for test files.
Use function-scoped fixtures by default. Session scope only when empirically justified by test runtime.
Use bare assert as primary assertion style; pytest.raises() with match= for exceptions; pytest.approx() for floating-point comparisons.
Docstrings are optional for simple tests, recommended for complex/e2e tests explaining purpose.
Markers are auto-assigned by path via pytest_collection_modifyitems (/e2e/ -> e2e, /smoke/ -> smoke, default -> unit). Use explicit markers: @pytest.mark.slow, @pytest.mark.requires_gpu, @pytest.mark.timeout().
Use tmp_path fixture for file operations in tests, never write to the repo tree.
Mark CUDA-dependent tests with @pytest.mark.e2e, @pytest.mark.smoke, or @pytest.mark.requires_gpu.
Mock only external boundaries in tests, not internal implementation details.
Ensure test isolation: no shared mutable state or execution-order dependencies between tests. Include required setup in the test or a fixture.
Use @pytest.mark.parametrize for testing multiple input combinations rather than copy-pasting similar tests.

tests/**/*.py: Auto-mark tests based on file path: tests under /e2e/ get e2e marker, tests under /smoke/ get smoke marker, all others get unit marker (only if no category marker already present)
Every test should have exactly one category marker: unit, smoke, or e2e
Use pytest.mark.requires_gpu modifier on tests that need CUDA hardware
Use pytest.mark.vllm on tests using vLLM generation backend and ensure each vLLM test file runs in its own process for GPU memory isolation
Use pytest.mark.slow on long-running tests
Use pytest.mark.smollm2 for SmolLM2 Hub download tests to enable process isolation
Use pytest.mark.noautouse to skip autouse fixtures for specific tests
Use load_test_dataset(filename) helper to load test datasets from `...

Files:

  • tests/configurator/test_pydantic_click_options.py

⚙️ CodeRabbit configuration file

Review tests against tests/TESTING.md. Check marker usage, fixture naming, tmp_path usage, determinism, and GPU/vLLM process-isolation requirements. Flag slop tests that only check that code runs, assert result is not None when stronger invariants exist, over-mock internal implementation details, patch around the bug instead of reproducing it, or add broad snapshot/golden churn without a clear contract. Flag change detector tests that fail on harmless refactors, formatting, record ordering, incidental wording, or private implementation details without demonstrating a behavior regression. Prefer existing fixtures or focused new fixtures for repeated setup; keep tests DRY when reasonable without making the behavior under test opaque. print() is allowed in tests.

Files:

  • tests/configurator/test_pydantic_click_options.py
🧠 Learnings (1)
📚 Learning: 2026-05-27T22:20:37.354Z
Learnt from: kendrickb-nvidia
Repo: NVIDIA-NeMo/Safe-Synthesizer PR: 520
File: tests/generation/test_vllm_backend.py:556-587
Timestamp: 2026-05-27T22:20:37.354Z
Learning: In NVIDIA-NeMo/Safe-Synthesizer, `tests/conftest.py`’s `pytest_collection_modifyitems` hook applies pytest category markers automatically based on each test file’s path: tests under `/e2e/` get `pytest.mark.e2e`, tests under `/smoke/` get `pytest.mark.smoke`, and all other tests get `pytest.mark.unit`. Therefore, when reviewing pytest tests outside `tests/e2e/` and `tests/smoke/`, do not flag missing explicit `pytest.mark.unit` decorators on test classes/functions as an issue (the hook will add them during collection). If a new test directory/category is introduced, ensure the hook is updated so it’s categorized correctly.

Applied to files:

  • tests/configurator/test_pydantic_click_options.py
🔇 Additional comments (2)
tests/e2e/required_configs/smollm3-dp.yaml (1)

8-9: LGTM!

tests/configurator/test_pydantic_click_options.py (1)

483-494: LGTM!

@mckornfield
mckornfield force-pushed the move-structured-gen-parameters/mck branch from 59057eb to 6dfe4bd Compare June 12, 2026 20:48
@coderabbitai coderabbitai Bot added docs Documentation-only change feature New feature or request test Test-only addition or change refactor Internal restructuring with no behavior change labels Jun 12, 2026
@coderabbitai coderabbitai Bot removed docs Documentation-only change feature New feature or request test Test-only addition or change refactor Internal restructuring with no behavior change labels Jun 12, 2026

@kendrickb-nvidia kendrickb-nvidia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like @binaryaaron to also take a look. The tidying up params into nested fields looks good and a pattern I'd like to continue.

Biggest question is we've had some functionality where passing a single flat dict with keys for various/all subobjects of SafeSynthesizerParameters works, and curious how enabled in particular will interact there. Is that auto setup only work for one layer of subobjects, ie the top level DataParameters, EvaluationParameters, TrainingHyperparams, etc., or does it work for arbitrary nesting. If the latter, can we only have one of these suboejcts with a field named enabled? But maybe this is why examples and code snippets explicitly construct the StructuredGenerationParameters dict/object.

I may also be misremembering how this all works too. But with some magic stuff happening around the Parameters setup, some concrete documentation of what is okay and what isn't seems useful, otherwise I'll be asking these same questions on every related PR forever.

is built.
"""

enabled: Annotated[

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Do all nested fields need to have unique names for the various broadcasting and auto construction of pydantic model stuff to work?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

had codex run some experiment style things and it came back with

No, nested field names do not need to be globally unique when callers use the nested shape. But flat kwargs are name-based and therefore ambiguous if multiple reachable models expose the same field name. For new nested config groups, we should document that callers should pass the nested object/dict, not an unqualified leaf like enabled.

which I think I'll add the docs for

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agent (review-pr): Broader construction issue is covered separately

Nested fields do not need globally unique names when callers use the canonical nested shape. The ambiguity is in the legacy flat from_params(...) construction path, where an unqualified leaf like enabled=True can match the wrong section.

I would not ask this PR to solve the whole construction layer. #596 (refactor: improve type-safe config and NER boundaries) is already open and covers the broader typed config/construction cleanup. For #595, I think the right scope is documenting the nested form and fixing any direct structured-generation migration regressions.

Signed-off-by: Matt Kornfield <mkornfield@nvidia.com>
@mckornfield
mckornfield force-pushed the move-structured-gen-parameters/mck branch from 6f91bf5 to 98c4f3e Compare June 15, 2026 19:14
@coderabbitai coderabbitai Bot added docs Documentation-only change feature New feature or request test Test-only addition or change refactor Internal restructuring with no behavior change labels Jun 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
.github/dco.yaml (2)

1-2: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

DCO enforcement is weakened for organization members

Line 2 sets members: false, which exempts members from DCO sign-off checks and conflicts with the repository contribution policy requiring sign-off for all contributions. Smallest fix: set members to true.

Suggested patch
 require:
-  members: false
+  members: true

As per coding guidelines, .github/** must stay aligned with branch-protection/CONTRIBUTING expectations, and CONTRIBUTING requires DCO sign-off for all contributions.

Source: Coding guidelines


1-2: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing SPDX header in YAML source file

This YAML file is missing the required SPDX hash-comment header, which can fail mise run check/copyright checks. Smallest fix: add the two SPDX lines at the top.

Suggested patch
+# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+
 require:
   members: false

As per coding guidelines, all .yaml source files require SPDX hash-comment headers.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: aabf1b17-f567-421a-9080-67949639efda

📥 Commits

Reviewing files that changed from the base of the PR and between 6f91bf5 and 98c4f3e.

📒 Files selected for processing (30)
  • .github/dco.yaml
  • docs/tutorials/differential-privacy.ipynb
  • docs/user-guide/configuration.md
  • docs/user-guide/running.md
  • docs/user-guide/troubleshooting.md
  • src/nemo_safe_synthesizer/config/__init__.py
  • src/nemo_safe_synthesizer/config/generate.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • tests/benchmarks/test_generation_structured_methods.py
  • tests/cli/test_utils.py
  • tests/config/conftest.py
  • tests/config/test_generate.py
  • tests/config/test_parameters.py
  • tests/configurator/test_pydantic_click_options.py
  • tests/conftest.py
  • tests/e2e/required_configs/mistral-dp.yaml
  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/e2e/required_configs/tinyllama-dp.yaml
  • tests/e2e/test_dataset_config.py
  • tests/e2e/test_safe_synthesizer.py
  • tests/generation/test_regex_manager.py
  • tests/generation/test_timeseries_backend.py
  • tests/generation/test_vllm_backend.py
  • tests/generation/test_vllm_shutdown.py
  • tests/sdk/test_process_data.py
  • tests/smoke/test_nss_structured_gen_gpu.py
💤 Files with no reviewable changes (1)
  • tests/generation/test_timeseries_backend.py
✅ Files skipped from review due to trivial changes (7)
  • tests/e2e/required_configs/smollm3-dp.yaml
  • tests/generation/test_vllm_shutdown.py
  • docs/user-guide/configuration.md
  • tests/e2e/required_configs/tinyllama-dp.yaml
  • docs/user-guide/running.md
  • docs/user-guide/troubleshooting.md
  • tests/conftest.py
🚧 Files skipped from review as they are similar to previous changes (16)
  • docs/tutorials/differential-privacy.ipynb
  • tests/e2e/required_configs/mistral-dp.yaml
  • src/nemo_safe_synthesizer/config/init.py
  • tests/e2e/test_safe_synthesizer.py
  • src/nemo_safe_synthesizer/generation/regex_manager.py
  • src/nemo_safe_synthesizer/generation/vllm_backend.py
  • tests/smoke/test_nss_structured_gen_gpu.py
  • tests/e2e/test_dataset_config.py
  • tests/sdk/test_process_data.py
  • tests/benchmarks/test_generation_structured_methods.py
  • src/nemo_safe_synthesizer/sdk/config_builder.py
  • src/nemo_safe_synthesizer/configurator/pydantic_click_options.py
  • tests/config/conftest.py
  • tests/cli/test_utils.py
  • tests/generation/test_regex_manager.py
  • tests/generation/test_vllm_backend.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Unit Tests (3.11)
  • GitHub Check: Unit Tests (3.12)
  • GitHub Check: Unit Tests (3.13)
  • GitHub Check: Smoke Tests
🧰 Additional context used
📓 Path-based instructions (14)
.github/**

⚙️ CodeRabbit configuration file

Review GitHub configuration for branch protection expectations, CODEOWNERS alignment, least privilege permissions, pinned actions where practical, and consistency with CONTRIBUTING.md.

Files:

  • .github/dco.yaml
**/*.{md,markdown,py}

📄 CodeRabbit inference engine (.cursor/rules/agent-markdown-style.mdc)

**/*.{md,markdown,py}: Avoid decorative bold (**text**) in list items, body text, and docstrings; use structural cues (headers, list markers, colons, backticks) for emphasis instead
Use backticks for code identifiers, paths, and CLI commands in markdown and docstrings

Files:

  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
  • tests/config/test_generate.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place durable implementation guidance in function and class docstrings for public contracts and source comments for local invariants
Target Python 3.11–3.13 with modern syntax (X | Y, list[str], Self). Python 3.14+ is not supported

**/*.py: Use Field(description=...) as the canonical field docstring for Pydantic models.
Use assignment style (type = Field(default=..., description="...")) as the default for Pydantic model fields rather than Annotated-only patterns.
Use Annotated only when the field carries additional metadata beyond Field() -- ValueValidator, AutoParam, DependsOnValidator, reusable constrained type aliases, nested-type constraints, or discriminated unions.
Use @dataclass(frozen=True) for immutable value objects and validators. Mutable @dataclass acceptable for builders, accumulators, and pipeline state.
Use field(default_factory=list) for mutable defaults in dataclasses, never = [].
Use StrEnum for string-valued enums used in configs/serialization. Plain Enum for internal-only named constants.
Use X | Y not Optional[X] or Union[X, Y] in type hints.
Use list[str] not List[str], dict[str, int] not Dict[str, int] in type hints.
Use Self for fluent method returns in type hints.
Use collection ABCs for function arguments (Sequence, Mapping, Iterable) so callers can pass any compatible container; concrete types for return values.
Use Protocol for structural subtyping when you need duck-typing boundaries.
Avoid Any in type hints -- prefer object, generics, or Protocol.
Use TYPE_CHECKING guards for heavy imports (pandas, torch, transformers); not needed for stdlib or lightweight imports.
Prefer match/case for dispatch on types or tagged values. if/elif is fine for simple boolean predicates.
Use comprehensions over imperative loops where intent is clearer. No multiple for clauses -- optimize for readability, not conciseness.
Use builder pattern with with_* methods...

Files:

  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
  • tests/config/test_generate.py
**/test_*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use the unit marker instead of the deprecated unit_test marker for test identification

Files:

  • tests/configurator/test_pydantic_click_options.py
  • tests/config/test_parameters.py
  • tests/config/test_generate.py
tests/**

📄 CodeRabbit inference engine (.cursor/rules/repo-navigation.mdc)

tests/**: Mirror src/ directory structure in tests/ directory for test organization
Auto-mark tests by directory: tests/e2e/e2e, tests/smoke/smoke, otherwise default to unit

Mirror source code directory structure in tests directory (e.g., tests/training/, tests/generation/ parallel to source structure)

Files:

  • tests/configurator/test_pydantic_click_options.py
  • tests/config/test_parameters.py
  • tests/config/test_generate.py

⚙️ CodeRabbit configuration file

tests/**:

Testing Guide

Comprehensive testing reference for Safe-Synthesizer developers. Covers commands, markers, test data, fixtures, and gotchas.

Read First

  1. tests/conftest.py -- auto-marking, load_test_dataset/load_test_dataframe, fixture_mock_processor pattern
  2. pytest.ini -- markers, asyncio, timeout
  3. tests/evaluation/conftest.py -- most complex: Faker-based make_df, nullable dtype conversion
  4. tests/generation/conftest.py -- JSONL/schema fixtures, fixture_valid_iris_dataset_jsonl_and_schema

Running Tests

All mise test tasks, grouped by scope:

mise run test                              # Unit (excludes slow, e2e, and smoke)
mise run test:unit-slow                    # Unit tests including slow (excludes e2e and smoke)
mise run test:smoke                        # CPU smoke tests (~few min, no GPU required)
mise run test:smoke:gpu                    # All staged GPU smoke tests (requires CUDA)
mise run test:smoke:gpu:train-only
mise run test:smoke:gpu:generation
mise run test:smoke:gpu:resume
mise run test:smoke:gpu:structured-generation
mise run test:smoke:gpu:timeseries
mise run test:smoke:gpu:smollm2
mise run test:e2e                          # All e2e (requires CUDA) -- runs default + dp
mise run test:e2e:default                  # e2e default (no-DP) tests only
mise run test:e2e:dp                       # e2e DP tests only
mise run test:ci                           # CI unit tests with coverage (excludes slow, e2e, gpu, smoke)
mise run test:ci-slow                      # CI slow tests with coverage
mise run test:ci-container                 # CI tests in a Linux container (Docker/Podman)

Run a single test:

uv run --frozen pytest tests/path/test_file.py::test_name -vvs -n0

Test runner: uv run --frozen pytest -n auto --dist loadscope -vv...

Files:

  • tests/configurator/test_pydantic_click_options.py
  • tests/config/test_parameters.py
  • tests/config/test_generate.py
tests/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

tests/**/*.py: Use file naming test_*.py, class naming Test*, function naming test_<module>_<expected_behavior> for test files.
Use function-scoped fixtures by default. Session scope only when empirically justified by test runtime.
Use bare assert as primary assertion style; pytest.raises() with match= for exceptions; pytest.approx() for floating-point comparisons.
Docstrings are optional for simple tests, recommended for complex/e2e tests explaining purpose.
Markers are auto-assigned by path via pytest_collection_modifyitems (/e2e/ -> e2e, /smoke/ -> smoke, default -> unit). Use explicit markers: @pytest.mark.slow, @pytest.mark.requires_gpu, @pytest.mark.timeout().
Use tmp_path fixture for file operations in tests, never write to the repo tree.
Mark CUDA-dependent tests with @pytest.mark.e2e, @pytest.mark.smoke, or @pytest.mark.requires_gpu.
Mock only external boundaries in tests, not internal implementation details.
Ensure test isolation: no shared mutable state or execution-order dependencies between tests. Include required setup in the test or a fixture.
Use @pytest.mark.parametrize for testing multiple input combinations rather than copy-pasting similar tests.

tests/**/*.py: Auto-mark tests based on file path: tests under /e2e/ get e2e marker, tests under /smoke/ get smoke marker, all others get unit marker (only if no category marker already present)
Every test should have exactly one category marker: unit, smoke, or e2e
Use pytest.mark.requires_gpu modifier on tests that need CUDA hardware
Use pytest.mark.vllm on tests using vLLM generation backend and ensure each vLLM test file runs in its own process for GPU memory isolation
Use pytest.mark.slow on long-running tests
Use pytest.mark.smollm2 for SmolLM2 Hub download tests to enable process isolation
Use pytest.mark.noautouse to skip autouse fixtures for specific tests
Use load_test_dataset(filename) helper to load test datasets from `...

Files:

  • tests/configurator/test_pydantic_click_options.py
  • tests/config/test_parameters.py
  • tests/config/test_generate.py

⚙️ CodeRabbit configuration file

Review tests against tests/TESTING.md. Check marker usage, fixture naming, tmp_path usage, determinism, and GPU/vLLM process-isolation requirements. Flag slop tests that only check that code runs, assert result is not None when stronger invariants exist, over-mock internal implementation details, patch around the bug instead of reproducing it, or add broad snapshot/golden churn without a clear contract. Flag change detector tests that fail on harmless refactors, formatting, record ordering, incidental wording, or private implementation details without demonstrating a behavior regression. Prefer existing fixtures or focused new fixtures for repeated setup; keep tests DRY when reasonable without making the behavior under test opaque. print() is allowed in tests.

Files:

  • tests/configurator/test_pydantic_click_options.py
  • tests/config/test_parameters.py
  • tests/config/test_generate.py
**/*.{py,sh,yaml,yml}

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Include SPDX copyright headers in all source files using hash-comments for .py, .sh, .yaml, .yml files.

Files:

  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
  • tests/config/test_generate.py
**/*.{py,sh,yaml,yml,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

All source files (.py, .sh, .yaml, .yml, .md) require SPDX copyright headers; mise run format adds them automatically

Files:

  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
  • tests/config/test_generate.py
**/*

⚙️ CodeRabbit configuration file

**/*: Review as a senior maintainer for NeMo Safe Synthesizer. Prioritize issues that can change behavior, break user workflows, weaken privacy guarantees, hide failures, make tests unreliable, or create maintenance risk. Avoid generic style commentary unless it points to a concrete project convention that automated tools will not catch.
Comment only when the finding is actionable and tied to changed code. For each finding, state the impact, the condition that triggers it, and the smallest practical fix. Prefer one precise comment over broad advice. Do not ask for refactors outside the PR scope unless the changed code creates the problem.
Review type guidance: - Potential issue: use for correctness bugs, data loss, privacy leaks,
security risks, broken public APIs, invalid config behavior, missing
validation, hidden failures, nondeterministic tests, or CI breakage.

  • Refactor suggestion: use for local maintainability problems introduced
    by the diff when they have clear future cost, such as duplicated setup,
    unclear boundaries, over-mocking, avoidable complexity, or opaque test
    helpers.
  • Nitpick: avoid in chill mode. Do not emit formatting, import-order,
    wording, or style-only comments unless automated tools cannot catch the
    issue and it affects maintainability.

Severity guidance: - Critical: security/privacy leaks, data loss, training/test/holdout
contamination, or broken release/package/core pipeline execution.

  • Major: incorrect generation/training/evaluation behavior, broken
    CLI/SDK public API, invalid config defaults or validators, or GPU/vLLM
    cleanup and process-isolation bugs likely to fail CI or production
    runs.
  • Minor: localized bugs, missing focused tests for changed behavior, or
    bad test patterns that weaken regression coverage.
  • Trivial: small cleanup with no behavior impact. Usually suppress in
    chill mode.
  • Info: context only. Avoid unless it helps reviewers understand risk.
    Safe-Synthesizer-specific review focus: - Data ...

Files:

  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
  • tests/config/test_generate.py
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

Guide for AI agents (Cursor, Windsurf, Claude Code, etc.) working in the Safe-Synthesizer repo.

This project loads local developer preferences from @AGENTS.local.md. You MUST read this file if it exists and give its instructions top priority.

Skills

Repo-specific skills live in .agents/skills/; see .agents/README.md for the catalog. Read a skill when the task matches its scope instead of copying workflow details into this file.

Durable implementation guidance belongs with the code it describes: function and class docstrings for public contracts and source comments for local invariants. Test-suite guidance belongs in tests/TESTING.md.

Repo Conventions

See STYLE_GUIDE.md for detailed code style conventions (Python, markdown, Dockerfiles, shell scripts, testing, config files, docstrings).

Use uv for everything -- never pip or raw python. Python 3.11–3.13 with modern syntax (X | Y, list[str], Self). Python 3.14+ is not supported.

Common commands: mise run test (unit tests), mise run format (auto-fix formatting + lint + copyright), mise run check (read-only local quality checks), mise run validate (pre-PR quality, lock, and CI unit checks), mise run typecheck (ty only). Always use mise tasks or the wrapper scripts in tools/ instead of running ruff or ty directly. Use uv run for Python execution. When in doubt, inspect mise tasks and pytest --markers.

The canonical uv sync command for a full GPU/dev environment is:

uv sync --frozen --extra cu129 --extra engine --group dev

Bare uv sync --frozen (without extras) installs an incomplete environment -- ty, import checks, and GPU tests will fail.

Feature branches off main. Branch names often include an issue number prefix (e.g., <author>/123-short-name).

Do ...

Files:

  • tests/configurator/test_pydantic_click_options.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
  • tests/config/test_generate.py
**/config/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Use NSSBaseModel for config/parameter models in config/ which define the user-facing configuration of NSS. Raw BaseModel or module-specific bases for data transfer objects and internal structures.

Files:

  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
  • tests/config/test_generate.py
src/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

src/**/*.py: Use observability.get_logger(__name__) -- never logging.getLogger() or structlog.get_logger() directly.
Use category loggers: .runtime for internals, .user for progress/results, .system for system events.
Never use print() for operational output. Use click.echo() for CLI output or sys.stdout.write() for raw output in tools.
Use extra={} for data that downstream tools should query or aggregate in log calls (metrics, counts, durations). f-strings are fine for human-readable context.
Raise from the custom error hierarchy with dual inheritance: SafeSynthesizerError (base), UserError, DataError, ParameterError, GenerationError, InternalError.
Prefer clamping/saturation over raising when out-of-range inputs shouldn't crash the system -- return a bounded value with a log warning.
Error messages must precisely match the actual error condition. Use !r for repr of interpolated pieces to clearly identify them.
Use relative imports in src/ (from ..observability import get_logger), absolute imports in tests/ (from nemo_safe_synthesizer.observability import get_logger).
Use pathlib.Path instead of os.path. Tolerate os.path only in vendored/tooling scripts.
Do not use print() statements in library code. Use get_logger(__name__) from observability.py or click.echo() for CLI. print() is fine in tests, standalone scripts, and tooling.
Do not use assert for validation in library code. Use if/raise for input validation. assert is fine in tests where pytest relies on it.

Files:

  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/config/generate.py

⚙️ CodeRabbit configuration file

Review library code against STYLE_GUIDE.md. Focus on behavior, API contracts, error handling, resource cleanup, typing, logging, and user-facing failures. Public APIs and nontrivial functions need Google-style docstrings.

Files:

  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
src/nemo_safe_synthesizer/**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

API reference pages are auto-generated from Python docstrings using Google-style format; write docstrings in src/nemo_safe_synthesizer/ and they will appear in the reference/

Files:

  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
src/nemo_safe_synthesizer/config/**/*.py

⚙️ CodeRabbit configuration file

Treat config changes as user-facing API changes. Check Pydantic field descriptions, defaults, validators, aliases, override behavior, CLI help text impact, YAML compatibility, and documented parameter semantics.

Files:

  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/config/generate.py
🧠 Learnings (1)
📚 Learning: 2026-05-27T22:20:37.354Z
Learnt from: kendrickb-nvidia
Repo: NVIDIA-NeMo/Safe-Synthesizer PR: 520
File: tests/generation/test_vllm_backend.py:556-587
Timestamp: 2026-05-27T22:20:37.354Z
Learning: In NVIDIA-NeMo/Safe-Synthesizer, `tests/conftest.py`’s `pytest_collection_modifyitems` hook applies pytest category markers automatically based on each test file’s path: tests under `/e2e/` get `pytest.mark.e2e`, tests under `/smoke/` get `pytest.mark.smoke`, and all other tests get `pytest.mark.unit`. Therefore, when reviewing pytest tests outside `tests/e2e/` and `tests/smoke/`, do not flag missing explicit `pytest.mark.unit` decorators on test classes/functions as an issue (the hook will add them during collection). If a new test directory/category is introduced, ensure the hook is updated so it’s categorized correctly.

Applied to files:

  • tests/configurator/test_pydantic_click_options.py
  • tests/config/test_parameters.py
  • tests/config/test_generate.py

Comment thread src/nemo_safe_synthesizer/config/parameters.py
Comment thread tests/config/test_generate.py
@mckornfield mckornfield changed the title feat: move structured generation parameters to their obj feat: move structured generation parameters to an object Jun 22, 2026
Signed-off-by: Matt Kornfield <mkornfield@nvidia.com>
@binaryaaron

binaryaaron commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

The structured-generation object split looks good, and I agree this is a pattern worth continuing for parameter groups that are getting too flat.

The main thing to make explicit is the construction behavior. My understanding is that the compatibility from_params(...) path broadcasts flat kwargs to the top-level parameter sections (data, evaluation, training, generation, privacy, etc.). It does not recursively route arbitrary leaf names into deeper nested objects. So enabled=True is ambiguous and should not be used for structured generation; callers should use structured_generation={"enabled": True} or generation={"structured_generation": {"enabled": True}}.

On the broader field uniqueness / construction cleanup: I incidentally came across that in #609, which fixes the config/construction layer, so I would avoid expanding this PR into the full refactor unless a small compatibility piece needs to be pulled forward.

Prompt for an agent:

You are working in Safe-Synthesizer on PR #595.

Make the smallest fixes needed for the structured-generation migration review:

1. Fix docs/tutorials/differential-privacy.ipynb so it is valid notebook JSON. The changed .with_generate(structured_generation={"enabled": True}) line currently has unescaped quotes inside a JSON string; use single quotes in the Python snippet or otherwise produce valid .ipynb JSON. Render/open the notebook to verify it loads.
2. Fix docs/user-guide/troubleshooting.md to use training.num_input_records_to_sample, not training.number_of_input_records_to_sample.
3. Add focused regression coverage for mixed nested + legacy structured-generation inputs, asserting the intended precedence when both forms are provided.
4. Add or tighten user-facing docs that explain SafeSynthesizerParameters.from_params flat kwargs only route to top-level parameter sections. Make clear that nested groups such as generation.structured_generation should be passed as a nested dict/object, not as bare enabled=True.
5. Do not do a broad from_params/config-construction refactor in this PR. Note that #609 addresses the wider construction/field-uniqueness cleanup.

mckornfield pushed a commit that referenced this pull request Jun 25, 2026
…#607)

Code changes was requested by @binaryaaron.

*
#595 (comment)

The following files were modified:

* `tests/config/test_generate.py`

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
coderabbitai Bot and others added 2 commits June 25, 2026 16:20
…607)

Code changes was requested by @binaryaaron.

*
#595 (comment)

The following files were modified:

* `tests/config/test_generate.py`

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: mkornfield <mkornfield@nvidia.com>
Signed-off-by: mkornfield <mkornfield@nvidia.com>
@mckornfield
mckornfield force-pushed the move-structured-gen-parameters/mck branch from 2bd332c to fd114c3 Compare June 25, 2026 20:19
@mckornfield
mckornfield added this pull request to the merge queue Jun 26, 2026
Merged via the queue into main with commit b9dd6e7 Jun 26, 2026
21 of 26 checks passed
@mckornfield
mckornfield deleted the move-structured-gen-parameters/mck branch June 26, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ci area:config area:docs area:generation area:observability area:sdk-cli area:tests docs Documentation-only change feature New feature or request refactor Internal restructuring with no behavior change test Test-only addition or change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal: Add StructuredGenerationParameters subobject

3 participants