Skip to navigation

nemo_gym.sandbox.attribution

View as MarkdownOpen in Claude

Automatic job attribution (team / user / workload / run) for sandbox metadata.

Sandbox providers merge these keys into every sandbox’s metadata so cluster operators can attribute running sandboxes to the team, user, and workload that created them (on OpenSandbox, metadata becomes queryable Kubernetes labels on the sandbox). Each field resolves from explicit configuration first, then NEMO_GYM_* environment variables, then Slurm job environment variables, then (for user only) the OS login name and (for workload) the server instance’s config path. Fields that cannot be resolved are omitted rather than guessed.

run identifies one launch of the creating process so a run’s sandboxes can be listed or garbage-collected exactly, even when the same user runs the same workload twice. It resolves from explicit configuration, then NEMO_GYM_RUN_ID, then a per-process generated id.

Module Contents

Classes

NameDescription
AttributionConfigJob attribution merged into every sandbox’s metadata (Kubernetes labels on OpenSandbox and OpenShell).

Functions

NameDescription
_detect_user-
_first_env-
log_attribution_onceLog the resolved attribution metadata once per process.
resolve_attributionResolve team / user / workload attribution metadata.
resolve_process_idAn id unique to this process (a forked child gets its own).
resolve_run_idResolve the run attribution id: explicit value, then NEMO_GYM_RUN_ID,
sanitize_label_valueMake value a valid Kubernetes label value: invalid characters become _, at most 63 characters.

Data

ATTRIBUTION_KEY_PREFIX_RE

DEFAULT_ATTRIBUTION_KEY_PREFIX

IGNORED_LOGIN_NAMES

LABEL_VALUE_MAX_LENGTH

LOGGER

METADATA_VALUE_RE

PROCESS_KEY

RUN_ENV_VARS

RUN_KEY

TEAM_ENV_VARS

TEAM_KEY

USER_ENV_VARS

USER_KEY

WORKLOAD_ENV_VARS

WORKLOAD_KEY

_logged_attribution

_process_id_by_pid

_process_run_id

API

class nemo_gym.sandbox.attribution.AttributionConfig(
enabled: bool = True,
team: str | None = None,
user: str | None = None,
workload: str | None = None,
run: str | None = None,
key_prefix: str = DEFAULT_ATTRIBUTION_KEY_PREFIX
)
Dataclass

Job attribution merged into every sandbox’s metadata (Kubernetes labels on OpenSandbox and OpenShell).

Providers that propagate sandbox metadata as Kubernetes labels make attribution queryable at the cluster level (e.g. kubectl get pods -l nemo-gym.nvidia.com/team=my-team). key_prefix namespaces the label keys (Kubernetes prefixed-key convention); set it to "" for bare team / user / workload / run keys.

Unset fields are auto-detected: NEMO_GYM_TEAM / NEMO_GYM_USER / NEMO_GYM_WORKLOAD environment variables first, then Slurm job env vars (SLURM_JOB_ACCOUNT / SLURM_JOB_USER / SLURM_JOB_NAME), then the OS login name for user (root is ignored) and the gym CLI’s NEMO_GYM_CONFIG_PATH server instance name for workload. Fields that cannot be resolved are omitted. run scopes sandboxes to one launch of the creating process (NEMO_GYM_RUN_ID, else generated per process and logged) so a run’s sandboxes can be listed and cleaned up exactly. Explicit SandboxSpec.metadata keys always take precedence over attribution keys.

enabled
bool = True
key_prefix
str = DEFAULT_ATTRIBUTION_KEY_PREFIX
run
str | None = None
team
str | None = None
user
str | None = None
workload
str | None = None
nemo_gym.sandbox.attribution.AttributionConfig.__post_init__() -> None
nemo_gym.sandbox.attribution.AttributionConfig.metadata() -> dict[str, str]

The prefixed attribution keys for a new sandbox (empty when disabled); values are raw.

nemo_gym.sandbox.attribution._detect_user(
environ: collections.abc.Mapping[str, str]
) -> str | None
nemo_gym.sandbox.attribution._first_env(
environ: collections.abc.Mapping[str, str],
names: tuple[str, ...]
) -> str | None
nemo_gym.sandbox.attribution.log_attribution_once(
metadata: collections.abc.Mapping[str, str]
) -> None

Log the resolved attribution metadata once per process.

The generated run id only exists in this process, so surfacing it in the logs is what lets operators later filter or garbage-collect this run’s sandboxes.

nemo_gym.sandbox.attribution.resolve_attribution(
team: str | None = None,
user: str | None = None,
workload: str | None = None,
environ: collections.abc.Mapping[str, str] | None = None
) -> dict[str, str]

Resolve team / user / workload attribution metadata.

Parameters:

team
str | NoneDefaults to None

Explicit team; falls back to NEMO_GYM_TEAM, then SLURM_JOB_ACCOUNT.

user
str | NoneDefaults to None

Explicit user; falls back to NEMO_GYM_USER, then SLURM_JOB_USER, then the OS login name (root is ignored — containers run as root by default, so it attributes the image, not a person).

workload
str | NoneDefaults to None

Explicit workload; falls back to NEMO_GYM_WORKLOAD, then SLURM_JOB_NAME, then NEMO_GYM_CONFIG_PATH (the server instance name the gym CLI sets on every server process it spawns).

environ
Mapping[str, str] | NoneDefaults to None

Environment mapping override, for testing. Defaults to os.environ.

Returns: dict[str, str]

A dict with only the resolved keys among team, user, and workload.

nemo_gym.sandbox.attribution.resolve_process_id() -> str

An id unique to this process (a forked child gets its own).

run (see resolve_run_id) can be shared by every process of a job and is overridable through NEMO_GYM_RUN_ID, so it cannot tell this process’s sandboxes from a sibling’s. This id can, which is what lets a provider clean up only its own process’s sandboxes.

nemo_gym.sandbox.attribution.resolve_run_id(
run: str | None = None,
environ: collections.abc.Mapping[str, str] | None = None
) -> str

Resolve the run attribution id: explicit value, then NEMO_GYM_RUN_ID, then an id generated once per process.

Unlike resolve_attribution fields, run is always resolvable. It scopes sandboxes to one launch of the creating process, so an interrupted run’s sandboxes can be listed and cleaned up exactly (team / user / workload cannot distinguish two runs of the same workload by the same user).

nemo_gym.sandbox.attribution.sanitize_label_value(
value: object
) -> str

Make value a valid Kubernetes label value: invalid characters become _, at most 63 characters.

An empty value stays empty; a value with no valid character becomes metadata.

nemo_gym.sandbox.attribution.ATTRIBUTION_KEY_PREFIX_RE = re.compile('(?=.{1,253}$)[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-...
nemo_gym.sandbox.attribution.DEFAULT_ATTRIBUTION_KEY_PREFIX = 'nemo-gym.nvidia.com/'
nemo_gym.sandbox.attribution.IGNORED_LOGIN_NAMES = frozenset({'root'})
nemo_gym.sandbox.attribution.LABEL_VALUE_MAX_LENGTH = 63
nemo_gym.sandbox.attribution.LOGGER = logging.getLogger(__name__)
nemo_gym.sandbox.attribution.METADATA_VALUE_RE = re.compile('[^A-Za-z0-9_.-]+')
nemo_gym.sandbox.attribution.PROCESS_KEY = 'process'
nemo_gym.sandbox.attribution.RUN_ENV_VARS = ('NEMO_GYM_RUN_ID',)
nemo_gym.sandbox.attribution.RUN_KEY = 'run'
nemo_gym.sandbox.attribution.TEAM_ENV_VARS = ('NEMO_GYM_TEAM', 'SLURM_JOB_ACCOUNT')
nemo_gym.sandbox.attribution.TEAM_KEY = 'team'
nemo_gym.sandbox.attribution.USER_ENV_VARS = ('NEMO_GYM_USER', 'SLURM_JOB_USER')
nemo_gym.sandbox.attribution.USER_KEY = 'user'
nemo_gym.sandbox.attribution.WORKLOAD_ENV_VARS = ('NEMO_GYM_WORKLOAD', 'SLURM_JOB_NAME', 'NEMO_GYM_CONFIG_PATH')
nemo_gym.sandbox.attribution.WORKLOAD_KEY = 'workload'
nemo_gym.sandbox.attribution._logged_attribution = False
nemo_gym.sandbox.attribution._process_id_by_pid: dict[int, str] = {}
nemo_gym.sandbox.attribution._process_run_id: str | None = None