Skip to content

fix: allow parameter loading when just rerunning generate - #549

Merged
mckornfield merged 6 commits into
mainfrom
changeup-param-loading/mck
Jun 3, 2026
Merged

fix: allow parameter loading when just rerunning generate#549
mckornfield merged 6 commits into
mainfrom
changeup-param-loading/mck

Conversation

@mckornfield

@mckornfield mckornfield commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Pre-Review Checklist

Ensure that the following pass:

  • make format && make check or via prek validation.
  • make test passes locally
  • make test-e2e passes locally
  • make 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

  • Closes #

Summary by CodeRabbit

  • New Features

    • Resume now supports selective runtime overrides for generation, evaluation, and telemetry while preserving other saved settings.
  • Bug Fixes

    • Resume flow correctly applies runtime overrides when restoring cached runs.
    • Dataset-registry defaults are no longer merged in as overrides during resume.
    • Merge behavior preserves saved-only fields (e.g., training, data, privacy) unless explicitly overridden.
    • Users are warned when loaded saved values differ from current defaults.
  • Tests

    • Added tests for resume merge semantics, deep/partial/full overrides, metadata preservation, and saved-vs-current-default warnings.
  • Documentation

    • Clarified resume override behavior in run generate docs.

@mckornfield
mckornfield requested a review from a team as a code owner June 2, 2026 17:49
@coderabbitai

coderabbitai Bot commented Jun 2, 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

This PR extends resume/load behavior: SafeSynthesizer.load_from_save_path accepts an optional runtime_config, warns on saved-vs-current-default drift, merges explicit runtime overrides into saved generation/evaluation/emit_telemetry sections, adjusts CLI and utils resume merge semantics, and adds tests covering these behaviors.

Changes

Resume-time Configuration Overrides

Layer / File(s) Summary
SDK method signature and override logic
src/nemo_safe_synthesizer/sdk/library_builder.py
load_from_save_path accepts optional runtime_config and uses new helpers to warn on saved/default drift and merge only explicitly provided runtime overrides into saved generation, evaluation, and emit_telemetry sections; derived config attributes are updated.
CLI integration
src/nemo_safe_synthesizer/cli/run.py, tests/cli/test_run.py
run generate now calls nss.load_from_save_path(runtime_config=config) when loading a cached/recovered workdir; test asserts the call includes the runtime_config from common_setup.
CLI utils: merge and registry resume semantics
src/nemo_safe_synthesizer/cli/utils.py
common_setup no longer applies dataset registry overrides during resume; merge_overrides reads existing file config with exclude_unset=True and merges it with CLI overrides before validation.
CLI utils tests
tests/cli/test_utils.py
Adds tests verifying registry-provided values are treated as defaults during resume and that partial config merges preserve explicit-field metadata.
Config: overlay explicitly-set runtime fields
src/nemo_safe_synthesizer/config/parameters.py
Adds helpers to collect runtime-explicit fields and overlay them onto saved Parameters; exposes with_runtime_overrides to apply generation/evaluation/emit_telemetry overrides only when explicitly set in runtime config.
Config tests
tests/config/test_parameters.py
Tests cover top-level, nested, and telemetry override behavior and ensure the saved config is not mutated by overlay operations.
SDK tests for resume merge and drift warnings
tests/sdk/test_process_data.py
Adds tests covering selective override merging, no-override preservation, deep nested merges, full runtime replacements when fully materialized, and user warnings when saved values differ from current defaults.
Docs: resume override note
docs/user-guide/running.md
Documents that resuming a run reloads the saved config and only generation/evaluation/emit_telemetry overrides take effect; other sections are inherited.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

bug, test

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: enabling parameter loading with runtime overrides when rerunning the generate command.
Docstring Coverage ✅ Passed Docstring coverage is 85.29% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch changeup-param-loading/mck

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

@coderabbitai coderabbitai Bot added bug Defects in shipped behavior test Test-only addition or change labels Jun 2, 2026
@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR correctly implements field-level deep-merge semantics for the resume (run generate) path, addressing the previously flagged whole-section replacement bug. It adds _collect_set_fields/_overlay_set_fields/with_runtime_overrides for recursive merge of explicitly-set runtime fields onto saved configs, fixes dataset-registry overrides from leaking into resume builds, and tightens merge_overrides to use exclude_unset=True for YAML loading.

  • Adds field-level deep-merge of runtime overrides onto saved config, correctly handling in-place sub-model mutations.
  • Fixes load_from_save_path to accept runtime_config, apply overrides, and sync individual config attributes.
  • Changes merge_overrides to treat only explicitly-set YAML keys as overrides on resume.

Confidence Score: 5/5

Safe to merge; the field-level merge logic is correct for all reachable CLI and SDK paths, and the existing tests thoroughly cover the override semantics.

The core change loads saved configs and applies only explicitly-set runtime fields via a deep recursive merge, correctly handling in-place sub-model mutations that model_dump(exclude_unset=True) would miss. The process_data() early-return on _loaded_from_save_path ensures _resolve_nss_config() never overwrites the merged config in the normal resume flow. Two observations about warning noise and silently-skipped default sub-models are edge cases that do not affect normal CLI or SDK usage.

No files require special attention; the two observations in library_builder.py and parameters.py are non-critical edge cases worth a follow-up but not blocking.

Important Files Changed

Filename Overview
src/nemo_safe_synthesizer/config/parameters.py Adds _collect_set_fields, _overlay_set_fields, and with_runtime_overrides for field-level deep-merge; logic is sound with one subtle gap: a BaseModel sub-field explicitly assigned to its default value (empty pydantic_fields_set) is silently skipped rather than overwriting the saved value.
src/nemo_safe_synthesizer/sdk/library_builder.py load_from_save_path updated to apply runtime overrides and sync individual config attributes; _warn_for_saved_default_drift fires for every non-default value in the saved config including non-overridable sections, which could be noisy in practice.
src/nemo_safe_synthesizer/cli/utils.py Dataset-registry overrides now correctly skipped during resume; merge_overrides switches to exclude_unset=True for YAML loading, preventing defaults from being treated as explicit overrides.
src/nemo_safe_synthesizer/cli/run.py One-line fix passing runtime_config to load_from_save_path; straightforward and correct.
tests/config/test_parameters.py Comprehensive parametric tests for with_runtime_overrides covering empty runtime, top-level scalar override, nested sub-model override, full materialized config, telemetry handling, mutation safety, and deep-copy independence.
tests/sdk/test_process_data.py Good integration tests for load_from_save_path covering partial overrides, no-override preservation, deep nested merges, full replacement, and default-drift warnings.
tests/cli/test_utils.py New resume test correctly verifies registry overrides (num_records=500) are suppressed on resume; merge_overrides test correctly asserts exclude_unset behaviour.
tests/cli/test_run.py New assertion verifying load_from_save_path is called with the runtime_config returned from common_setup.

Sequence Diagram

sequenceDiagram
    participant CLI as run_generate (CLI)
    participant CS as common_setup
    participant SS as SafeSynthesizer
    participant LFS as load_from_save_path
    participant WRO as with_runtime_overrides

    CLI->>CS: "common_setup(settings, resume=True)"
    Note over CS: Dataset registry overrides skipped (resume=True)
    CS->>CS: "merge_overrides(config_path, synthesis_overrides, exclude_unset=True)"
    CS-->>CLI: (logger, runtime_config, df, workdir)

    CLI->>SS: "SafeSynthesizer(config=runtime_config)"
    CLI->>LFS: load_from_save_path(runtime_config)
    LFS->>LFS: from_json(config_file) to saved_config
    LFS->>LFS: _warn_for_saved_default_drift(saved_config)
    LFS->>WRO: saved_config.with_runtime_overrides(runtime_config)
    WRO->>WRO: _overlay_set_fields(saved.generation, runtime.generation)
    WRO->>WRO: _overlay_set_fields(saved.evaluation, runtime.evaluation)
    WRO->>WRO: apply emit_telemetry if set
    WRO-->>LFS: merged_config
    LFS->>LFS: "_nss_config = merged_config"
    LFS->>LFS: sync _generation_config, _evaluation_config, _emit_telemetry_config

    CLI->>SS: ".process_data() returns early _loaded_from_save_path=True"
    CLI->>SS: .generate() uses _nss_config.generation
    CLI->>SS: .evaluate() uses _nss_config.evaluation
Loading

Reviews (4): Last reviewed commit: "Merge branch 'main' into changeup-param-..." | Re-trigger Greptile

Comment on lines +263 to +270
if runtime_config is not None:
saved_config = saved_config.model_copy(
update={
"generation": runtime_config.generation,
"evaluation": runtime_config.evaluation,
"emit_telemetry": runtime_config.emit_telemetry,
},
)

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 Whole sub-object replacement silently drops saved generation settings

The entire generation and evaluation objects from runtime_config replace the saved values — not just the fields the user explicitly specified on the CLI. Any saved generation settings that are not re-specified at the CLI (e.g. use_structured_generation=True, temperature, validation params) are silently reset to Pydantic defaults. The new test illustrates this: saved_config.generation.use_structured_generation = True is written to disk, but after load_from_save_path(runtime_config=...) that field is False because runtime_config.generation carries the CLI default — and the test never asserts on it.

A narrower approach is to merge only the fields that the caller explicitly set, using Pydantic's model_fields_set:

if runtime_config is not None:
    gen_overrides = runtime_config.generation.model_dump(include=runtime_config.generation.model_fields_set)
    eval_overrides = runtime_config.evaluation.model_dump(include=runtime_config.evaluation.model_fields_set)
    saved_config = saved_config.model_copy(
        update={
            "generation": saved_config.generation.model_copy(update=gen_overrides),
            "evaluation": saved_config.evaluation.model_copy(update=eval_overrides),
            "emit_telemetry": runtime_config.emit_telemetry,
        },
    )

This preserves all saved generation settings while still allowing targeted CLI overrides.

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): Fixed in 355c2b6 with field-level merge

Confirmed and addressed. Whole-section replacement reset any saved generation/evaluation field not re-specified at the CLI. Replaced with a field-level merge that applies only explicitly-set runtime values via model_dump(exclude_unset=True), preserving saved values otherwise. Also switched merge_overrides to load config files with exclude_unset=True so partial files no longer materialize defaults as explicit, and skip dataset-registry overrides on resume. New test asserts use_structured_generation=True survives a resume that does not re-specify it.

@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

🧹 Nitpick comments (1)
src/nemo_safe_synthesizer/sdk/library_builder.py (1)

246-248: ⚡ Quick win

Clarify docstring to reflect section-level replacement.

The docstring says "runtime_config values for generation and evaluation are applied", which could be interpreted as field-level merging. However, the implementation replaces entire sections. Consider rewording for clarity:

-Optional ``runtime_config`` values for generation and evaluation are
-applied after loading the saved training-run config so resume-time CLI
-overrides work without mutating the persisted train config.
+Optional ``runtime_config`` replaces the entire ``generation``, ``evaluation``,
+and ``emit_telemetry`` sections after loading the saved training-run config,
+enabling resume-time CLI overrides without mutating the persisted train config.
+Callers must ensure ``runtime_config`` is fully populated (all fields resolved),
+not just the fields they wish to override.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 71d5be70-5437-4dc5-8b32-03614f57e6ff

📥 Commits

Reviewing files that changed from the base of the PR and between ad35852 and 2e7673a.

📒 Files selected for processing (4)
  • src/nemo_safe_synthesizer/cli/run.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/cli/test_run.py
  • tests/sdk/test_process_data.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). (8)
  • GitHub Check: Unit Tests (3.12)
  • GitHub Check: Unit Tests (3.13)
  • GitHub Check: Unit Tests (3.11)
  • GitHub Check: Smoke Tests
  • GitHub Check: Analyze (Python)
  • GitHub Check: Typecheck
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{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:

  • src/nemo_safe_synthesizer/cli/run.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/sdk/test_process_data.py
  • tests/cli/test_run.py
**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

**/*.py: Use American English spelling: 'initialize' not 'initialise', 'recognize' not 'recognise', 'color' not 'colour'.
Use observability.get_logger(__name__) for logging, 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={} in logging for structured data that downstream tools should query or aggregate; use f-strings for human-readable context.
Raise from the custom error hierarchy with dual inheritance: SafeSynthesizerError (base), UserError, DataError, ParameterError, GenerationError, InternalError.
Use NSSBaseModel for config/parameter models in config/ which define user-facing configuration. Use raw BaseModel or module-specific bases for data transfer objects and internal structures.
Use BaseSettings for env/CLI settings. Prefer AliasChoices on individual fields when a field needs to respond to both its Python name and an env var name.
Include Field(description=...) for Pydantic model fields as the canonical field docstring for API documentation and CLI help text.
Use assignment-style type = Field(default=..., description="...") as the default for Pydantic model fields because type checkers understand default, default_factory, and alias in assignment style.
Use Annotated only when the field carries additional metadata beyond Field() -- ValueValidator, AutoParam, DependsOnValidator, reusable constrained type aliases, nested-type constraints, or discriminated unions.
Put defaults as bare assignment (= value), not inside Field(default=...), when using Annotated. Exception: use assignment-style Field(default_factory=...) for defaults that cannot be expressed as bare assignments.
Use @dataclass(frozen=True) for immutable value objects and validators; mu...

Files:

  • src/nemo_safe_synthesizer/cli/run.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/sdk/test_process_data.py
  • tests/cli/test_run.py
src/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

src/**/*.py: Use relative imports in src/ (e.g., from ..observability import get_logger).
Do not use print() statements in library code. Use get_logger(__name__) from observability.py or click.echo() for CLI.
Do not use assert for validation in library code. Use if/raise for input validation. assert statements can be stripped by -O and must never guard correctness.

Files:

  • src/nemo_safe_synthesizer/cli/run.py
  • src/nemo_safe_synthesizer/sdk/library_builder.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/cli/run.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
**/*.{py,sh,yaml,yml}

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Include SPDX copyright header at the top: # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. and # SPDX-License-Identifier: Apache-2.0. The make format command handles this automatically.

Files:

  • src/nemo_safe_synthesizer/cli/run.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/sdk/test_process_data.py
  • tests/cli/test_run.py
**/*

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

**/*: Include a newline at the end of all files, never trailing whitespace. This is enforced by pre-commit.
Use line length of 120 characters for code, comments, and docstrings (configured in ruff.toml).

Files:

  • src/nemo_safe_synthesizer/cli/run.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/sdk/test_process_data.py
  • tests/cli/test_run.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:

  • src/nemo_safe_synthesizer/cli/run.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/sdk/test_process_data.py
  • tests/cli/test_run.py
tests/**

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

Tests should mirror the src/ directory structure in tests/

Files:

  • tests/sdk/test_process_data.py
  • tests/cli/test_run.py
tests/**/*.py

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

Tests in tests/e2e/ should be auto-marked with e2e marker, tests in tests/smoke/ with smoke marker, others with unit marker

tests/**/*.py: Use absolute imports in tests/ (e.g., from nemo_safe_synthesizer.observability import get_logger).
Use fixture_ prefix convention for fixtures for grep-ability and to separate fixtures from test functions. Add a one-line docstring describing the fixture's purpose and data.
Use function-scoped fixtures by default. Session scope only when empirically justified by test runtime.
Use bare assert as the primary assertion style; pytest.raises() with match= for exceptions; pytest.approx() for floating-point comparisons.
Mark CUDA-dependent tests with @pytest.mark.e2e, @pytest.mark.smoke, or @pytest.mark.requires_gpu.
Mock only external boundaries, not internal implementation details.
Ensure test isolation: no shared mutable state or execution-order dependencies between tests. If something must be run first before executing a test, include it in the test or a fixture.
Use @pytest.mark.parametrize for testing multiple input combinations rather than copy-pasting similar tests.

Organize tests using pytest following the structure in tests/TESTING.md with support for unit tests, smoke tests, and end-to-end tests

Files:

  • tests/sdk/test_process_data.py
  • tests/cli/test_run.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/sdk/test_process_data.py
  • tests/cli/test_run.py
**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

The unit_test marker is deprecated; use unit instead

Files:

  • tests/sdk/test_process_data.py
  • tests/cli/test_run.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/sdk/test_process_data.py
  • tests/cli/test_run.py
🔇 Additional comments (2)
src/nemo_safe_synthesizer/cli/run.py (1)

571-571: LGTM!

tests/cli/test_run.py (1)

623-650: LGTM!

Comment thread src/nemo_safe_synthesizer/sdk/library_builder.py Outdated
Comment thread tests/sdk/test_process_data.py
@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

mckornfield and others added 2 commits June 2, 2026 22:02
Signed-off-by: mkornfield <mkornfield@nvidia.com>
Resume-time generate runs replaced whole generation/evaluation config
sections with the runtime config, silently resetting any saved field not
re-specified at the CLI (e.g. use_structured_generation) to its default.

Apply only explicitly-set runtime fields via model_dump(exclude_unset=True),
preserving saved values otherwise. merge_overrides now loads config files with
exclude_unset=True so partial files no longer materialize defaults as explicit,
and dataset-registry overrides are skipped on resume. Warn when saved values
drift from current package defaults.

Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
@binaryaaron
binaryaaron force-pushed the changeup-param-loading/mck branch from 355c2b6 to 57b5f59 Compare June 2, 2026 22:03

@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: 1

🧹 Nitpick comments (2)
src/nemo_safe_synthesizer/cli/utils.py (1)

273-274: 💤 Low value

Consider documenting the resume exception in the precedence comment.

The comment block at lines 263-268 explains override precedence but doesn't mention that registry overrides are skipped during resume. Adding a note would help future maintainers understand this behavior without reading the implementation.

📝 Suggested comment addition
 # synthesis_overrides collects config overrides from dataset registry and
 # CLI, which is then combined with the config file when calling
 # merge_overrides(). CLI takes top precedence, then dataset registry, and
 # finally the config file. See test_utils.py and especially
 # test_overrides_config_registry_and_cli for examples of how the resolution
 # is expected to work.
+# Note: During resume mode, registry overrides are skipped to preserve
+# saved configuration values.
 synthesis_overrides: dict[str, Any] | None = dict()
tests/cli/test_utils.py (1)

355-368: ⚡ Quick win

Consider adding a test case with non-empty overrides.

The current test verifies behavior when overrides={}. Consider adding a follow-up test that passes non-empty overrides to ensure that both file config fields and override fields are preserved in exclude_unset output, validating the full merge path.

🧪 Suggested additional test
def test_partial_config_with_runtime_overrides_preserves_both(self, tmp_path: Path):
    """Both partial config and runtime overrides should appear in exclude_unset."""
    config_file = tmp_path / "config.yaml"
    config_file.write_text("""
generation:
  num_records: 77
""")

    config = merge_overrides(config_file, {"generation": {"temperature": 0.9}})

    assert config.generation.num_records == 77
    assert config.generation.temperature == 0.9
    dumped = config.model_dump(exclude_unset=True)
    assert dumped == {
        "generation": {
            "num_records": 77,
            "temperature": 0.9,
        }
    }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 38d40fc0-abb5-43d7-ba46-05c692e8e63a

📥 Commits

Reviewing files that changed from the base of the PR and between 2e7673a and 355c2b6.

📒 Files selected for processing (4)
  • src/nemo_safe_synthesizer/cli/utils.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{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:

  • src/nemo_safe_synthesizer/cli/utils.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

**/*.py: Use American English spelling: 'initialize' not 'initialise', 'recognize' not 'recognise', 'color' not 'colour'.
Use observability.get_logger(__name__) for logging, 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={} in logging for structured data that downstream tools should query or aggregate; use f-strings for human-readable context.
Raise from the custom error hierarchy with dual inheritance: SafeSynthesizerError (base), UserError, DataError, ParameterError, GenerationError, InternalError.
Use NSSBaseModel for config/parameter models in config/ which define user-facing configuration. Use raw BaseModel or module-specific bases for data transfer objects and internal structures.
Use BaseSettings for env/CLI settings. Prefer AliasChoices on individual fields when a field needs to respond to both its Python name and an env var name.
Include Field(description=...) for Pydantic model fields as the canonical field docstring for API documentation and CLI help text.
Use assignment-style type = Field(default=..., description="...") as the default for Pydantic model fields because type checkers understand default, default_factory, and alias in assignment style.
Use Annotated only when the field carries additional metadata beyond Field() -- ValueValidator, AutoParam, DependsOnValidator, reusable constrained type aliases, nested-type constraints, or discriminated unions.
Put defaults as bare assignment (= value), not inside Field(default=...), when using Annotated. Exception: use assignment-style Field(default_factory=...) for defaults that cannot be expressed as bare assignments.
Use @dataclass(frozen=True) for immutable value objects and validators; mu...

Files:

  • src/nemo_safe_synthesizer/cli/utils.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
src/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

src/**/*.py: Use relative imports in src/ (e.g., from ..observability import get_logger).
Do not use print() statements in library code. Use get_logger(__name__) from observability.py or click.echo() for CLI.
Do not use assert for validation in library code. Use if/raise for input validation. assert statements can be stripped by -O and must never guard correctness.

Files:

  • src/nemo_safe_synthesizer/cli/utils.py
  • src/nemo_safe_synthesizer/sdk/library_builder.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/cli/utils.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
**/*.{py,sh,yaml,yml}

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Include SPDX copyright header at the top: # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. and # SPDX-License-Identifier: Apache-2.0. The make format command handles this automatically.

Files:

  • src/nemo_safe_synthesizer/cli/utils.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
**/*

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

**/*: Include a newline at the end of all files, never trailing whitespace. This is enforced by pre-commit.
Use line length of 120 characters for code, comments, and docstrings (configured in ruff.toml).

Files:

  • src/nemo_safe_synthesizer/cli/utils.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.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:

  • src/nemo_safe_synthesizer/cli/utils.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
tests/**

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

Tests should mirror the src/ directory structure in tests/

Files:

  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
tests/**/*.py

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

Tests in tests/e2e/ should be auto-marked with e2e marker, tests in tests/smoke/ with smoke marker, others with unit marker

tests/**/*.py: Use absolute imports in tests/ (e.g., from nemo_safe_synthesizer.observability import get_logger).
Use fixture_ prefix convention for fixtures for grep-ability and to separate fixtures from test functions. Add a one-line docstring describing the fixture's purpose and data.
Use function-scoped fixtures by default. Session scope only when empirically justified by test runtime.
Use bare assert as the primary assertion style; pytest.raises() with match= for exceptions; pytest.approx() for floating-point comparisons.
Mark CUDA-dependent tests with @pytest.mark.e2e, @pytest.mark.smoke, or @pytest.mark.requires_gpu.
Mock only external boundaries, not internal implementation details.
Ensure test isolation: no shared mutable state or execution-order dependencies between tests. If something must be run first before executing a test, include it in the test or a fixture.
Use @pytest.mark.parametrize for testing multiple input combinations rather than copy-pasting similar tests.

Organize tests using pytest following the structure in tests/TESTING.md with support for unit tests, smoke tests, and end-to-end tests

Files:

  • 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/cli/test_utils.py
  • tests/sdk/test_process_data.py
**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

The unit_test marker is deprecated; use unit instead

Files:

  • tests/cli/test_utils.py
  • tests/sdk/test_process_data.py
🧠 Learnings (3)
📚 Learning: 2026-06-01T18:13:50.774Z
Learnt from: binaryaaron
Repo: NVIDIA-NeMo/Safe-Synthesizer PR: 538
File: src/nemo_safe_synthesizer/cli/utils.py:0-0
Timestamp: 2026-06-01T18:13:50.774Z
Learning: In `src/nemo_safe_synthesizer/cli/utils.py`, `_propagate_runtime_settings_to_env` intentionally uses write-only (non-`None`) propagation. The function runs exactly once per `common_setup` call in a one-shot CLI process, so there is no prior-invocation leakage risk. A clear-on-`None` guard (else: os.environ.pop(...)) would also be ineffective because `CLISettings` re-reads `os.environ` at construction time, meaning any value written by a hypothetical prior run is already reflected as non-`None` in the field — the else branch would never fire. A real cross-run isolation fix would require snapshotting/restoring `os.environ` around each run, which is out of scope for this PR.

Applied to files:

  • src/nemo_safe_synthesizer/cli/utils.py
  • tests/cli/test_utils.py
📚 Learning: 2026-05-14T21:47:20.140Z
Learnt from: CR
Repo: NVIDIA-NeMo/Safe-Synthesizer PR: 0
File: tests/TESTING.md:0-0
Timestamp: 2026-05-14T21:47:20.140Z
Learning: Applies to tests/**/tests/**/conftest.py : Use `fixture_` prefix for dataset/tokenizer fixture names. Use descriptive names for non-fixture helpers (e.g., `mock_workdir`).

Applied to files:

  • tests/cli/test_utils.py
📚 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/cli/test_utils.py
  • tests/sdk/test_process_data.py
🔇 Additional comments (7)
src/nemo_safe_synthesizer/cli/utils.py (1)

400-402: LGTM!

tests/cli/test_utils.py (2)

15-15: LGTM!


232-254: ⚡ Quick win

Test expectation is correct for the default: GenerateParameters.num_records defaults to 1000, so asserting config.generation.num_records == 1000 in test_resume_uses_registry_data_without_registry_config_overrides matches current config defaults.

src/nemo_safe_synthesizer/sdk/library_builder.py (3)

57-79: Field-level merge looks correct.

exclude_unset=True on the runtime sub-models with merge_dicts into the fully-dumped saved section correctly overrides only explicitly-set fields while preserving the rest. This resolves the prior section-level replacement concern.


82-84: Remove concern: _iter_parameters already guarantees single-key dicts.

Parameters._iter_parameters(recursive=False) constructs parameters = [{k: v} for k, v in self.model_dump().items()] and its docstring explicitly states it yields “single-key dicts”; nested groups use the same _iter_parameters implementation. So next(iter(item.items())) won’t silently drop extra keys under the current contract.


325-328: ⚡ Quick win

Fix telemetry gate on SDK resume when emit_telemetry is overridden

load_from_save_path() updates self._nss_config.emit_telemetry / self._emit_telemetry_config, but _emit_nss_telemetry() uses the value captured during __init__ (self._emit_telemetry), so runtime_config=SafeSynthesizerParameters(emit_telemetry=...) during resume may not change whether telemetry is emitted.

  • Make _emit_nss_telemetry() consult the latest self._emit_telemetry_config (or self._nss_config.emit_telemetry) instead of the stale __init__ value.
  • Add a focused test that calls load_from_save_path(runtime_config=SafeSynthesizerParameters(emit_telemetry=False)) and asserts telemetry is skipped.
tests/sdk/test_process_data.py (1)

442-578: Solid, behavior-focused coverage of the merge logic.

The tests assert concrete preserved/overridden values (including the previously-missing use_structured_generation preservation) and cover the no-override, partial-override, and fully-materialized cases plus the drift warning. Mocking is limited to the ModelMetadata boundary and tmp_path is used for IO.

Comment thread src/nemo_safe_synthesizer/sdk/library_builder.py
Saved configs carry no provenance, so the drift check flags every saved value
that differs from the current default, including deliberate user choices. The
prior "differs from the current package default" wording implied a version or
default change. Reword to "is non-default" to avoid asserting a package-default
change.

Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Relocate the resume-time override merge from the SDK pipeline to
SafeSynthesizerParameters.with_runtime_overrides, reusing utils.merge_dicts.
Override detection now walks __pydantic_fields_set__ recursively via
_collect_set_fields, so nested sub-group overrides (e.g.
generation.validation.*) set by in-place mutation are captured and deep-merged
instead of being dropped by model_dump(exclude_unset=True).

Document the resume override scope in the run-generate guide and add config-level
and resume-path regression tests, including nested generation.validation merges.

Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
@binaryaaron
binaryaaron requested a review from a team as a code owner June 3, 2026 15:28
@coderabbitai coderabbitai Bot added docs Documentation-only change and removed bug Defects in shipped behavior test Test-only addition or change labels Jun 3, 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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f30b5c6a-11e4-41d6-9fe4-21209cf3ed2a

📥 Commits

Reviewing files that changed from the base of the PR and between 57b5f59 and e4e3014.

📒 Files selected for processing (5)
  • docs/user-guide/running.md
  • src/nemo_safe_synthesizer/config/parameters.py
  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • tests/config/test_parameters.py
  • tests/sdk/test_process_data.py
✅ Files skipped from review due to trivial changes (1)
  • docs/user-guide/running.md
📜 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). (6)
  • GitHub Check: Unit Tests (3.13)
  • GitHub Check: Smoke Tests
  • GitHub Check: Unit Tests (3.12)
  • GitHub Check: Unit Tests (3.11)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (Python)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{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:

  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • tests/sdk/test_process_data.py
**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

**/*.py: Use American English spelling: 'initialize' not 'initialise', 'recognize' not 'recognise', 'color' not 'colour'.
Use observability.get_logger(__name__) for logging, 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={} in logging for structured data that downstream tools should query or aggregate; use f-strings for human-readable context.
Raise from the custom error hierarchy with dual inheritance: SafeSynthesizerError (base), UserError, DataError, ParameterError, GenerationError, InternalError.
Use NSSBaseModel for config/parameter models in config/ which define user-facing configuration. Use raw BaseModel or module-specific bases for data transfer objects and internal structures.
Use BaseSettings for env/CLI settings. Prefer AliasChoices on individual fields when a field needs to respond to both its Python name and an env var name.
Include Field(description=...) for Pydantic model fields as the canonical field docstring for API documentation and CLI help text.
Use assignment-style type = Field(default=..., description="...") as the default for Pydantic model fields because type checkers understand default, default_factory, and alias in assignment style.
Use Annotated only when the field carries additional metadata beyond Field() -- ValueValidator, AutoParam, DependsOnValidator, reusable constrained type aliases, nested-type constraints, or discriminated unions.
Put defaults as bare assignment (= value), not inside Field(default=...), when using Annotated. Exception: use assignment-style Field(default_factory=...) for defaults that cannot be expressed as bare assignments.
Use @dataclass(frozen=True) for immutable value objects and validators; mu...

Files:

  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • tests/sdk/test_process_data.py
src/**/*.py

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

src/**/*.py: Use relative imports in src/ (e.g., from ..observability import get_logger).
Do not use print() statements in library code. Use get_logger(__name__) from observability.py or click.echo() for CLI.
Do not use assert for validation in library code. Use if/raise for input validation. assert statements can be stripped by -O and must never guard correctness.

Files:

  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • src/nemo_safe_synthesizer/config/parameters.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/sdk/library_builder.py
  • src/nemo_safe_synthesizer/config/parameters.py
**/*.{py,sh,yaml,yml}

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

Include SPDX copyright header at the top: # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. and # SPDX-License-Identifier: Apache-2.0. The make format command handles this automatically.

Files:

  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • tests/sdk/test_process_data.py
**/*

📄 CodeRabbit inference engine (STYLE_GUIDE.md)

**/*: Include a newline at the end of all files, never trailing whitespace. This is enforced by pre-commit.
Use line length of 120 characters for code, comments, and docstrings (configured in ruff.toml).

Files:

  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • tests/sdk/test_process_data.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:

  • src/nemo_safe_synthesizer/sdk/library_builder.py
  • src/nemo_safe_synthesizer/config/parameters.py
  • tests/config/test_parameters.py
  • tests/sdk/test_process_data.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
tests/**

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

Tests should mirror the src/ directory structure in tests/

Files:

  • tests/config/test_parameters.py
  • tests/sdk/test_process_data.py
tests/**/*.py

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

Tests in tests/e2e/ should be auto-marked with e2e marker, tests in tests/smoke/ with smoke marker, others with unit marker

tests/**/*.py: Use absolute imports in tests/ (e.g., from nemo_safe_synthesizer.observability import get_logger).
Use fixture_ prefix convention for fixtures for grep-ability and to separate fixtures from test functions. Add a one-line docstring describing the fixture's purpose and data.
Use function-scoped fixtures by default. Session scope only when empirically justified by test runtime.
Use bare assert as the primary assertion style; pytest.raises() with match= for exceptions; pytest.approx() for floating-point comparisons.
Mark CUDA-dependent tests with @pytest.mark.e2e, @pytest.mark.smoke, or @pytest.mark.requires_gpu.
Mock only external boundaries, not internal implementation details.
Ensure test isolation: no shared mutable state or execution-order dependencies between tests. If something must be run first before executing a test, include it in the test or a fixture.
Use @pytest.mark.parametrize for testing multiple input combinations rather than copy-pasting similar tests.

Organize tests using pytest following the structure in tests/TESTING.md with support for unit tests, smoke tests, and end-to-end tests

Files:

  • tests/config/test_parameters.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/config/test_parameters.py
  • tests/sdk/test_process_data.py
**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

The unit_test marker is deprecated; use unit instead

Files:

  • tests/config/test_parameters.py
  • tests/sdk/test_process_data.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/config/test_parameters.py
  • tests/sdk/test_process_data.py
🔇 Additional comments (7)
src/nemo_safe_synthesizer/sdk/library_builder.py (2)

82-82: LGTM!


298-298: LGTM!

tests/sdk/test_process_data.py (5)

442-480: LGTM!


482-511: LGTM!


513-545: LGTM!


547-584: LGTM!


586-612: LGTM!

Comment thread src/nemo_safe_synthesizer/config/parameters.py Outdated
Comment thread tests/config/test_parameters.py
model_copy(update=...) defaults to deep=False, so sections not overridden
(data, training, privacy, time_series, preflight, replace_pii) shared mutable
references with the original config, and no-op generation/evaluation aliased the
original sub-objects. Later mutation of the returned config (e.g. timeseries
preprocessing writing config.data.*) could mutate the original, contradicting
the method's copy contract.

Record only changed sections in the update and use model_copy(deep=True) so
unchanged sections are deep-copied; the returned config is now fully independent
of self. Add a regression test asserting cross-section isolation.

Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
@coderabbitai coderabbitai Bot added bug Defects in shipped behavior test Test-only addition or change and removed docs Documentation-only change labels Jun 3, 2026
@mckornfield
mckornfield merged commit c47c29a into main Jun 3, 2026
18 checks passed
@mckornfield
mckornfield deleted the changeup-param-loading/mck branch June 3, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Defects in shipped behavior test Test-only addition or change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants