> This page is for version 0.7.0.
> For other versions, use one of these documentation indexes:
> - Main (default): https://docs.nvidia.com/nemo/gym/main/llms.txt
> - 0.7.0: https://docs.nvidia.com/nemo/gym/v0.7.0/llms.txt
> - 0.6.0: https://docs.nvidia.com/nemo/gym/v0.6.0/llms.txt
> - 0.5.1: https://docs.nvidia.com/nemo/gym/v0.5.1/llms.txt
> - 0.5.0: https://docs.nvidia.com/nemo/gym/v0.5.0/llms.txt
> - 0.4.0: https://docs.nvidia.com/nemo/gym/v0.4.0/llms.txt
> - 0.3.0: https://docs.nvidia.com/nemo/gym/v0.3.0/llms.txt
> - 0.2.1: https://docs.nvidia.com/nemo/gym/v0.2.1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/gym/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server.

# nemo_gym.artifacts.exporters.base

## Module Contents

### Classes

| Name                                                              | Description                                                                    |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| [`BaseExporter`](#nemo_gym-artifacts-exporters-base-BaseExporter) | Sink for the run metadata a NeMo Gym eval produces: config, metrics, rollouts. |
| [`ExportResult`](#nemo_gym-artifacts-exporters-base-ExportResult) | What one exporter did with one finished run.                                   |
| [`ExportState`](#nemo_gym-artifacts-exporters-base-ExportState)   | -                                                                              |
| [`Exporter`](#nemo_gym-artifacts-exporters-base-Exporter)         | Sends a finished run to a destination, or converts it to another format.       |

### Functions

| Name                                                                                        | Description                                                                                             |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| [`_accepted_stage_omissions`](#nemo_gym-artifacts-exporters-base-_accepted_stage_omissions) | How many planned rows an accepted partial stage omitted: the number recovery's acceptance gate states.  |
| [`_aggregation_failure`](#nemo_gym-artifacts-exporters-base-_aggregation_failure)           | Why the aggregate metrics file is not a complete result, or None.                                       |
| [`_artifact_version`](#nemo_gym-artifacts-exporters-base-_artifact_version)                 | Content identities for a run, independent of its writers, timestamps and directory location.            |
| [`_live_run_id`](#nemo_gym-artifacts-exporters-base-_live_run_id)                           | The backend run a live exporter recorded for this run, or None when there is none or it cannot be read. |
| [`_upload_intent`](#nemo_gym-artifacts-exporters-base-_upload_intent)                       | The upload settings in `config`, resolving only those values.                                           |
| [`_write_marker_atomically`](#nemo_gym-artifacts-exporters-base-_write_marker_atomically)   | Publish the already-exported marker only once it is fully written, so a failed write leaves none.       |
| [`unexportable_reason`](#nemo_gym-artifacts-exporters-base-unexportable_reason)             | What is missing for the run to be exported faithfully, or None when it can be.                          |

### Data

[`_BOOL`](#nemo_gym-artifacts-exporters-base-_BOOL)

[`__getattr__`](#nemo_gym-artifacts-exporters-base-__getattr__)

[`logger`](#nemo_gym-artifacts-exporters-base-logger)

### API

```python
class nemo_gym.artifacts.exporters.base.BaseExporter(
    global_config_dict: omegaconf.DictConfig
)
```

**Bases:** [Exporter](#nemo_gym-artifacts-exporters-base-Exporter)

Sink for the run metadata a NeMo Gym eval produces: config, metrics, rollouts.

A backend (W\&B, MLflow, ...) subclasses this and wraps its own run handle. Lifecycle is
`setup` -> any number of `log_*` calls -> `teardown`. Whether a backend runs at all is decided
by its `ExporterConfig` in the registry, not here.

Exporters are best-effort telemetry: a failing tracking server must not fail the eval. Call
sites go through the `export_*` wrappers, which swallow and log backend exceptions. Subclasses
implement the unwrapped `_*` hooks and may raise freely.

**`_exported_artifacts`** `Optional[RunArtifacts] = None`

---

**`_pending_config`** `Optional[DictConfig] = None`

---

**`_pending_run_id`** `Optional[str] = None`

---

**`_recorded`** `Optional[str] = None`

---

**`config_model`** `type[RunExportConfig] = RunExportConfig`

---

```python
nemo_gym.artifacts.exporters.base.BaseExporter.__enter__() -> nemo_gym.artifacts.exporters.base.BaseExporter
```

```python
nemo_gym.artifacts.exporters.base.BaseExporter.__exit__(
    exc_type,
    exc_value,
    traceback
) -> None
```

```python
nemo_gym.artifacts.exporters.base.BaseExporter._close_reopened() -> None
```

Close the reopened run. A backend that marks a run when it reopens it undoes that mark here, and lets go
of the run first, so that nothing more reaches it even when closing fails.

```python
nemo_gym.artifacts.exporters.base.BaseExporter._flag(
    name: str
) -> bool
```

A flag of the raw config read as the validated run config reads it, so "false" or "0" (as an
`${oc.env:...}` interpolation yields) is false. A value that config would reject does not continue a run.

```python
nemo_gym.artifacts.exporters.base.BaseExporter._flush_pending_config() -> None
```

Log the reopened run's config, as the run continues. Backends call this first in `teardown` too.

```python
nemo_gym.artifacts.exporters.base.BaseExporter._forget_recorded_run() -> None
```

Remove the marker that names the reopened run, so that a failed replacement `setup` cannot leave the next
resume reopening that run. A marker that names another run is left alone. Best effort: if the marker can be
neither removed nor replaced by the new run's, the next resume still reopens the old run.

```python
nemo_gym.artifacts.exporters.base.BaseExporter._guard(
    what: str,
    fn,
    args = ()
) -> None
```

```python
nemo_gym.artifacts.exporters.base.BaseExporter._log_config(
    config_dict: omegaconf.DictConfig
) -> None
```

abstract

Record the resolved run config. Secrets are already masked by `export_config`.

```python
nemo_gym.artifacts.exporters.base.BaseExporter._log_metrics(
    metrics: dict[str, typing.Any],
    step: typing.Optional[int] = None
) -> None
```

abstract

Record scalar metrics. `step` is the training step, or None for a one-shot eval.

```python
nemo_gym.artifacts.exporters.base.BaseExporter._log_rollouts(
    rollouts: list[dict[str, typing.Any]]
) -> None
```

abstract

Record rollout results as a table. Rollouts are sanitized dicts, one per rollout.

```python
nemo_gym.artifacts.exporters.base.BaseExporter._read_rollouts(
    artifacts: nemo_gym.artifacts.run_artifacts.RunArtifacts
) -> list[dict[str, typing.Any]]
```

staticmethod

The rollouts a live run would have uploaded.

```python
nemo_gym.artifacts.exporters.base.BaseExporter._record_run_id(
    run_id: str
) -> None
```

```python
nemo_gym.artifacts.exporters.base.BaseExporter._recorded_run_id() -> typing.Optional[str]
```

The run to reopen: the one recorded next to the output, when this run continues that output.

A resume continues it, as does a judge-failure recovery that appends to it. A fresh run into the same output
starts its rollouts anew, so it starts a new tracking run too, as does a resume that finds nothing to resume
(`start_over`).

```python
nemo_gym.artifacts.exporters.base.BaseExporter._run_id_fpath() -> typing.Optional[pathlib.Path]
```

`<output stem>_<name>_run_id` next to `output_jsonl_fpath`, or None when the run has no output.

A resumed or resubmitted run writes to the same output, so a backend that records its run id there
reopens that run instead of starting a new one. Deleting the file starts a new run. Only a live run
reads or writes it; a post-run export keeps its own marker (`RunArtifacts.export_done_fpath`).

```python
nemo_gym.artifacts.exporters.base.BaseExporter._start_over() -> None
```

```python
nemo_gym.artifacts.exporters.base.BaseExporter._upload_enabled(
    saved_config: omegaconf.DictConfig
) -> bool
```

Whether the rollouts are uploaded, as a live run would have decided.

An explicit `upload_rollouts=` on the export command wins; otherwise the run's own saved setting does.

```python
nemo_gym.artifacts.exporters.base.BaseExporter.export(
    artifacts: nemo_gym.artifacts.run_artifacts.RunArtifacts
) -> nemo_gym.artifacts.exporters.base.ExportResult
```

Send a finished run to the backend: its saved config, aggregate metrics and (optionally) rollouts.

Skipped, without contacting the backend, when the run's export receipt matches its current artifacts; a
receipt that does not match them, or a marker that is not a version-1 receipt, is a failure and nothing is
sent. Failures come back in the result rather than being swallowed the way live `export_*` calls are.

```python
nemo_gym.artifacts.exporters.base.BaseExporter.export_config() -> None
```

```python
nemo_gym.artifacts.exporters.base.BaseExporter.export_metrics(
    metrics: dict[str, typing.Any],
    step: typing.Optional[int] = None
) -> None
```

```python
nemo_gym.artifacts.exporters.base.BaseExporter.export_rollouts(
    rollouts: list[dict[str, typing.Any]]
) -> None
```

```python
nemo_gym.artifacts.exporters.base.BaseExporter.finalize_live(
    artifacts: nemo_gym.artifacts.run_artifacts.RunArtifacts,
    version: dict[str, str | None]
) -> None
```

Optionally certify an explicitly completed live activation; teardown alone never certifies it.

```python
nemo_gym.artifacts.exporters.base.BaseExporter.from_global_config(
    global_config_dict: omegaconf.DictConfig
) -> nemo_gym.artifacts.exporters.base.BaseExporter
```

classmethod

Build the exporter from the whole resolved config.

This overrides `Exporter.from_global_config` because these exporters have always been built from the full
config (a live run has no settings model of its own) instead of from a validated model. So only the shared
settings are validated here, and each backend validates its own in its constructor.

```python
nemo_gym.artifacts.exporters.base.BaseExporter.link_superseding_export(
    live_run_id: str,
    run_id: str
) -> None
```

Optionally mark that the post-run export `run_id` supersedes the live run `live_run_id` it could not
certify. Best effort: a backend logs and ignores any failure, and never changes either run's data.

```python
nemo_gym.artifacts.exporters.base.BaseExporter.setup() -> None
```

abstract

Open the backing run. Called once, after config resolution.

```python
nemo_gym.artifacts.exporters.base.BaseExporter.start_over() -> None
```

Track the output in a new run after all.

Rollout collection calls this when it was asked to resume its output but found nothing to resume and starts it
anew, before it logs any rollout. A no-op unless `setup` reopened the recorded run.

```python
nemo_gym.artifacts.exporters.base.BaseExporter.teardown() -> None
```

abstract

Close the backing run. Must be safe to call when `setup` failed or never ran.

```python
class nemo_gym.artifacts.exporters.base.ExportResult(
    state: nemo_gym.artifacts.exporters.base.ExportState,
    exit_code: int = 0,
    error: typing.Optional[str] = None,
    detail: dict[str, typing.Any] = dict()
)
```

Dataclass

What one exporter did with one finished run.

**`detail`** `dict[str, Any] = field(default_factory=dict)`

---

**`error`** `Optional[str] = None`

---

**`exit_code`** `int = 0`

---

**`state`** `ExportState`

---

```python
nemo_gym.artifacts.exporters.base.ExportResult.failed(
    error: str,
    exit_code: int = 1,
    detail: typing.Any = {}
) -> nemo_gym.artifacts.exporters.base.ExportResult
```

classmethod

```python
nemo_gym.artifacts.exporters.base.ExportResult.skipped(
    detail: typing.Any = {}
) -> nemo_gym.artifacts.exporters.base.ExportResult
```

classmethod

```python
nemo_gym.artifacts.exporters.base.ExportResult.succeeded(
    detail: typing.Any = {}
) -> nemo_gym.artifacts.exporters.base.ExportResult
```

classmethod

```python
class nemo_gym.artifacts.exporters.base.ExportState
```

**Bases:** `enum.Enum`

**`FAILED`** `= 'failed'`

---

**`SKIPPED`** `= 'skipped'`

---

**`SUCCEEDED`** `= 'succeeded'`

---

```python
class nemo_gym.artifacts.exporters.base.Exporter(
    config: nemo_gym.config.config_types.RunExportConfig
)
```

Abstract

Sends a finished run to a destination, or converts it to another format.

An exporter receives the run's `RunArtifacts` and decides which of those files it reads. Its settings
come from `config_model`, validated from the export command's own config before `export` is called.
Importing a backend's SDK belongs inside `export`, so listing exporters never loads one.

**`config_model`** `type[RunExportConfig]`

---

**`name`** `str`

---

```python
nemo_gym.artifacts.exporters.base.Exporter.export(
    artifacts: nemo_gym.artifacts.run_artifacts.RunArtifacts
) -> nemo_gym.artifacts.exporters.base.ExportResult
```

abstract

Export the run. Report an expected failure in the result; raise only for a bug.

```python
nemo_gym.artifacts.exporters.base.Exporter.from_global_config(
    global_config_dict: omegaconf.DictConfig
) -> nemo_gym.artifacts.exporters.base.Exporter
```

classmethod

Build the exporter from the export command's resolved config, validating it first.

```python
nemo_gym.artifacts.exporters.base._accepted_stage_omissions(
    materialized_inputs: pathlib.Path
) -> int
```

How many planned rows an accepted partial stage omitted: the number recovery's acceptance gate states.

```python
nemo_gym.artifacts.exporters.base._aggregation_failure(
    aggregate_metrics: pathlib.Path
) -> typing.Optional[str]
```

Why the aggregate metrics file is not a complete result, or None.

A failed `/aggregate_metrics` request leaves an `aggregation_error` entry beside the healthy agents'
metrics. A live run keeps those partial metrics and reports the failure, but an export marks the run
done, so it must not send a partial result.

```python
nemo_gym.artifacts.exporters.base._artifact_version(
    artifacts: nemo_gym.artifacts.run_artifacts.RunArtifacts
) -> dict[str, typing.Optional[str]]
```

Content identities for a run, independent of its writers, timestamps and directory location.

```python
nemo_gym.artifacts.exporters.base._live_run_id(
    artifacts: nemo_gym.artifacts.run_artifacts.RunArtifacts,
    name: str
) -> typing.Optional[str]
```

The backend run a live exporter recorded for this run, or None when there is none or it cannot be read.

```python
nemo_gym.artifacts.exporters.base._upload_intent(
    config: omegaconf.DictConfig
) -> nemo_gym.config.config_types.UploadRolloutsConfigMixin
```

The upload settings in `config`, resolving only those values.

A saved run config keeps its `${...}` interpolations unresolved, and resolving all of it would need the
environment the run had. The canonical key wins over the deprecated one, as in the settings model, so a
deprecated value that loses is never evaluated.

```python
nemo_gym.artifacts.exporters.base._write_marker_atomically(
    marker: pathlib.Path,
    run_id: str
) -> None
```

Publish the already-exported marker only once it is fully written, so a failed write leaves none.

```python
nemo_gym.artifacts.exporters.base.unexportable_reason(
    artifacts: nemo_gym.artifacts.run_artifacts.RunArtifacts,
    rollouts_required: bool = True
) -> typing.Optional[str]
```

What is missing for the run to be exported faithfully, or None when it can be.

Pass `rollouts_required=False` when the exporter does not read the rollouts file. When that file is there,
the aggregate metrics must still not be older than it.

```python
nemo_gym.artifacts.exporters.base._BOOL = TypeAdapter(bool)
```

```python
nemo_gym.artifacts.exporters.base.__getattr__ = moved_names_getattr(__name__)
```

```python
nemo_gym.artifacts.exporters.base.logger = logging.getLogger(__name__)
```