> This page is for version 0.6.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.metrics

## Module Contents

### Functions

| Name                                                                                           | Description                                                                                         |
| ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| [`_canonical_mlflow_key`](#nemo_gym-artifacts-exporters-metrics-_canonical_mlflow_key)         | The backend key shared by health preflight and MLflow delivery, without importing the optional SDK. |
| [`_elide_key`](#nemo_gym-artifacts-exporters-metrics-_elide_key)                               | Cut an over-long name in the middle, keeping both ends.                                             |
| [`_normalize_key_path`](#nemo_gym-artifacts-exporters-metrics-_normalize_key_path)             | Neutralize only the path segments MLflow rejects, leaving the rest of the name intact.              |
| [`flatten_aggregate_metrics`](#nemo_gym-artifacts-exporters-metrics-flatten_aggregate_metrics) | The scalar metrics a tracking backend gets from per-agent aggregate-metrics entries.                |
| [`health_source_artifacts`](#nemo_gym-artifacts-exporters-metrics-health_source_artifacts)     | Bind derived health to the same saved inputs, Store selection and underlying health report.         |

### Data

[`_ELISION_MARKER`](#nemo_gym-artifacts-exporters-metrics-_ELISION_MARKER)

[`_INVALID_KEY_CHARS`](#nemo_gym-artifacts-exporters-metrics-_INVALID_KEY_CHARS)

[`_LEADING_DOTS`](#nemo_gym-artifacts-exporters-metrics-_LEADING_DOTS)

[`_MLFLOW_KEY_LENGTH_LIMIT`](#nemo_gym-artifacts-exporters-metrics-_MLFLOW_KEY_LENGTH_LIMIT)

[`_PRIMITIVE_TYPES`](#nemo_gym-artifacts-exporters-metrics-_PRIMITIVE_TYPES)

### API

```python
nemo_gym.artifacts.exporters.metrics._canonical_mlflow_key(
    key: str,
    limit: int = _MLFLOW_KEY_LENGTH_LIMIT
) -> str | None
```

The backend key shared by health preflight and MLflow delivery, without importing the optional SDK.

```python
nemo_gym.artifacts.exporters.metrics._elide_key(
    key: str,
    limit: int = _MLFLOW_KEY_LENGTH_LIMIT
) -> str
```

Cut an over-long name in the middle, keeping both ends.

Gym names carry their identity at the edges — benchmark and agent in the prefix, the statistic
in the suffix (`gpqa.../simple_agent/mean/reward`) — so the middle is what can be spared.

```python
nemo_gym.artifacts.exporters.metrics._normalize_key_path(
    key: str
) -> str
```

Neutralize only the path segments MLflow rejects, leaving the rest of the name intact.

MLflow requires an already-normalized relative path, so it rejects empty segments (from
leading, doubled or trailing slashes) and the traversal forms `.` and `..`.

```python
nemo_gym.artifacts.exporters.metrics.flatten_aggregate_metrics(
    agent_entries: typing.Iterable[dict[str, typing.Any]],
    source_artifacts: dict[str, typing.Any] | None = None
) -> dict[str, typing.Any]
```

The scalar metrics a tracking backend gets from per-agent aggregate-metrics entries.

Live runs and post-run export both build their metrics here, so a run looks the same in the backend
either way: `<agent>/<metric>` and `key_metrics/<agent>/<metric>`, primitive values only.

```python
nemo_gym.artifacts.exporters.metrics.health_source_artifacts(
    artifacts,
    version: dict[str, typing.Any]
) -> dict[str, typing.Any]
```

Bind derived health to the same saved inputs, Store selection and underlying health report.

```python
nemo_gym.artifacts.exporters.metrics._ELISION_MARKER = '___'
```

```python
nemo_gym.artifacts.exporters.metrics._INVALID_KEY_CHARS = re.compile('[^/\\w.\\- :]')
```

```python
nemo_gym.artifacts.exporters.metrics._LEADING_DOTS = re.compile('^\\.+')
```

```python
nemo_gym.artifacts.exporters.metrics._MLFLOW_KEY_LENGTH_LIMIT = 250
```

```python
nemo_gym.artifacts.exporters.metrics._PRIMITIVE_TYPES = (bool, int, float, str, type(None))
```