diff --git a/src/anonymizer/interface/anonymizer.py b/src/anonymizer/interface/anonymizer.py index d7c26eba..d7776eac 100644 --- a/src/anonymizer/interface/anonymizer.py +++ b/src/anonymizer/interface/anonymizer.py @@ -183,7 +183,7 @@ def __init__( # Tag DataDesigner telemetry events so they're filterable as anonymizer traffic in # the shared NeMo dashboards. `setdefault` so users (or upstream hosts) can override. os.environ.setdefault("NEMO_SESSION_PREFIX", "anonymizer-") - os.environ.setdefault("NEMO_DEPLOYMENT_TYPE", "sdk") + os.environ.setdefault("ANONYMIZER_USAGE_TYPE", "sdk") resolved_artifact_path = Path(artifact_path or ".anonymizer-artifacts") try: parsed = parse_model_configs(model_configs) diff --git a/src/anonymizer/interface/cli/main.py b/src/anonymizer/interface/cli/main.py index 54c1b79a..0418907d 100644 --- a/src/anonymizer/interface/cli/main.py +++ b/src/anonymizer/interface/cli/main.py @@ -11,10 +11,10 @@ from pathlib import Path from typing import Annotated, ClassVar, Literal -# When invoked via the CLI entry point, telemetry deployment type defaults to "cli". -# Anonymizer's SDK path sets "sdk" via os.environ.setdefault in Anonymizer.__init__, -# but the CLI is loaded first so its setdefault wins for CLI-driven runs. -os.environ.setdefault("NEMO_DEPLOYMENT_TYPE", "cli") +# Track whether Anonymizer is used through the CLI or SDK separately from the +# shared NeMo deployment type. Data Designer validates NEMO_DEPLOYMENT_TYPE +# against deployment-level values such as "library" and "api" at import time. +os.environ.setdefault("ANONYMIZER_USAGE_TYPE", "cli") logger = logging.getLogger("anonymizer.cli") diff --git a/src/anonymizer/telemetry.py b/src/anonymizer/telemetry.py index dce052f4..4b7f623b 100644 --- a/src/anonymizer/telemetry.py +++ b/src/anonymizer/telemetry.py @@ -13,7 +13,9 @@ Related environment variables (read at runtime, not import time): - ``NEMO_TELEMETRY_ENABLED``: set to ``false`` / ``0`` / ``no`` to disable. -- ``NEMO_DEPLOYMENT_TYPE``: ``cli``, ``sdk``, ``nmp``, ``nvidia-internal``. Defaults to ``sdk``. +- ``ANONYMIZER_USAGE_TYPE``: ``cli``, ``sdk``, or ``nmp``. Defaults to ``sdk``. +- ``NEMO_DEPLOYMENT_TYPE``: optional shared NeMo deployment override, such as + ``nvidia-internal``. When set, it takes precedence over ``ANONYMIZER_USAGE_TYPE``. - ``NEMO_TELEMETRY_ENDPOINT``: override the destination URL. - ``NEMO_SESSION_PREFIX``: prepended to session IDs. Set to ``"anonymizer-"`` automatically by ``Anonymizer.__init__`` for dashboard filtering. @@ -89,7 +91,8 @@ def _telemetry_endpoint() -> str: def _deployment_type() -> DeploymentTypeEnum: - raw = os.getenv("NEMO_DEPLOYMENT_TYPE", "sdk").lower() + raw = os.getenv("NEMO_DEPLOYMENT_TYPE") or os.getenv("ANONYMIZER_USAGE_TYPE", "sdk") + raw = raw.lower() try: return DeploymentTypeEnum(raw) except ValueError: diff --git a/tests/conftest.py b/tests/conftest.py index f1179e23..21784408 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -65,10 +65,11 @@ def _isolate_telemetry_env(monkeypatch: pytest.MonkeyPatch) -> None: - Disable emission by default. Tests that exercise the emit path can opt in by setting NEMO_TELEMETRY_ENABLED=true via their own monkeypatch. - - Clear NEMO_DEPLOYMENT_TYPE, NEMO_SESSION_PREFIX, and NEMO_TELEMETRY_ENDPOINT - so tests don't inherit values from the developer's shell. + - Clear ANONYMIZER_USAGE_TYPE, NEMO_DEPLOYMENT_TYPE, NEMO_SESSION_PREFIX, and + NEMO_TELEMETRY_ENDPOINT so tests don't inherit values from the developer's shell. """ monkeypatch.setenv("NEMO_TELEMETRY_ENABLED", "false") + monkeypatch.delenv("ANONYMIZER_USAGE_TYPE", raising=False) monkeypatch.delenv("NEMO_DEPLOYMENT_TYPE", raising=False) monkeypatch.delenv("NEMO_SESSION_PREFIX", raising=False) monkeypatch.delenv("NEMO_TELEMETRY_ENDPOINT", raising=False) diff --git a/tests/interface/cli/test_cli_help.py b/tests/interface/cli/test_cli_help.py index 85414499..4abeef76 100644 --- a/tests/interface/cli/test_cli_help.py +++ b/tests/interface/cli/test_cli_help.py @@ -3,6 +3,11 @@ from __future__ import annotations +import os +import subprocess +import sys +from pathlib import Path + import pytest from anonymizer.interface.cli.main import app @@ -14,3 +19,23 @@ def test_help_exits_zero(subcommand: str, capsys: pytest.CaptureFixture[str]) -> with pytest.raises(SystemExit) as exc: app([subcommand, "--help"]) assert exc.value.code == 0 + + +def test_console_script_starts_with_data_designer_telemetry_validation(tmp_path: Path) -> None: + """The real CLI must not set a deployment type rejected by Data Designer.""" + env = os.environ.copy() + env.pop("ANONYMIZER_USAGE_TYPE", None) + env.pop("NEMO_DEPLOYMENT_TYPE", None) + console_script = Path(sys.executable).parent / "anonymizer" + + completed = subprocess.run( + [console_script, "--help"], + cwd=tmp_path, + env=env, + check=False, + capture_output=True, + text=True, + ) + + assert completed.returncode == 0, completed.stderr + assert "NeMo Anonymizer CLI" in completed.stdout diff --git a/tests/interface/test_anonymizer_telemetry.py b/tests/interface/test_anonymizer_telemetry.py index aaaf0efc..5e2299ca 100644 --- a/tests/interface/test_anonymizer_telemetry.py +++ b/tests/interface/test_anonymizer_telemetry.py @@ -131,9 +131,10 @@ def test_session_prefix_respects_existing_value(self, monkeypatch: pytest.Monkey _make_anonymizer() assert os.environ["NEMO_SESSION_PREFIX"] == "custom-" - def test_deployment_type_defaults_to_sdk(self) -> None: + def test_usage_type_defaults_to_sdk(self) -> None: _make_anonymizer() - assert os.environ.get("NEMO_DEPLOYMENT_TYPE") == "sdk" + assert os.environ.get("ANONYMIZER_USAGE_TYPE") == "sdk" + assert "NEMO_DEPLOYMENT_TYPE" not in os.environ # ============================================================================= diff --git a/tests/test_telemetry.py b/tests/test_telemetry.py index ac0666a8..fd14a3bb 100644 --- a/tests/test_telemetry.py +++ b/tests/test_telemetry.py @@ -160,10 +160,17 @@ def test_telemetry_endpoint_override_preserves_case(self, monkeypatch: pytest.Mo assert _telemetry_endpoint() == custom def test_deployment_type_default_is_sdk(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.delenv("ANONYMIZER_USAGE_TYPE", raising=False) monkeypatch.delenv("NEMO_DEPLOYMENT_TYPE", raising=False) assert _deployment_type() == DeploymentTypeEnum.SDK + def test_deployment_type_reads_anonymizer_usage_type(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.delenv("NEMO_DEPLOYMENT_TYPE", raising=False) + monkeypatch.setenv("ANONYMIZER_USAGE_TYPE", "cli") + assert _deployment_type() == DeploymentTypeEnum.CLI + def test_deployment_type_accepts_nvidia_internal(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("ANONYMIZER_USAGE_TYPE", "cli") monkeypatch.setenv("NEMO_DEPLOYMENT_TYPE", "nvidia-internal") assert _deployment_type() == DeploymentTypeEnum.NVIDIA_INTERNAL