> 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.rollout_observability

Shared contracts for rollout observations and trajectories.

## Module Contents

### Classes

| Name                                                                                           | Description                                                                     |
| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| [`AgentEpisode`](#nemo_gym-rollout_observability-AgentEpisode)                                 | An Agent response and the observations available at its execution boundary.     |
| [`AgentInvocation`](#nemo_gym-rollout_observability-AgentInvocation)                           | One root Agent or subagent conversation observed by a harness.                  |
| [`AgentObservationBundle`](#nemo_gym-rollout_observability-AgentObservationBundle)             | Normalized observations returned by one Agent Server for one rollout.           |
| [`ContextCompactionObservation`](#nemo_gym-rollout_observability-ContextCompactionObservation) | An explicit context-compaction event reported by the Agent harness.             |
| [`ModelCallRef`](#nemo_gym-rollout_observability-ModelCallRef)                                 | Stable identifiers an Agent integration can observe for one model call.         |
| [`ObservationGap`](#nemo_gym-rollout_observability-ObservationGap)                             | A fact that the selected integration could not observe or join exactly.         |
| [`ObservationModel`](#nemo_gym-rollout_observability-ObservationModel)                         | -                                                                               |
| [`SandboxObservation`](#nemo_gym-rollout_observability-SandboxObservation)                     | Outcome and lifetime resource usage reported by a sandbox-owning harness.       |
| [`ToolCallObservation`](#nemo_gym-rollout_observability-ToolCallObservation)                   | Timing observed for one tool call at an Agent-owned boundary.                   |
| [`TrajectoryModelCall`](#nemo_gym-rollout_observability-TrajectoryModelCall)                   | -                                                                               |
| [`TrajectoryRecord`](#nemo_gym-rollout_observability-TrajectoryRecord)                         | -                                                                               |
| [`TrajectoryResponseMetadata`](#nemo_gym-rollout_observability-TrajectoryResponseMetadata)     | -                                                                               |
| [`TrajectoryTokenStats`](#nemo_gym-rollout_observability-TrajectoryTokenStats)                 | -                                                                               |
| [`TrajectoryToolCall`](#nemo_gym-rollout_observability-TrajectoryToolCall)                     | Tool observation enriched with the model-visible output in a trajectory record. |
| [`TrajectoryTurn`](#nemo_gym-rollout_observability-TrajectoryTurn)                             | -                                                                               |

### Functions

| Name                                                                                           | Description                                                                              |
| ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| [`join_model_call_observations`](#nemo_gym-rollout_observability-join_model_call_observations) | Resolve harness call references against captured model calls without guessing ownership. |

### Data

[`AgentObservationRecord`](#nemo_gym-rollout_observability-AgentObservationRecord)

### API

```python
class nemo_gym.rollout_observability.AgentEpisode(
    response: nemo_gym.openai_utils.NeMoGymResponse,
    observations: nemo_gym.rollout_observability.AgentObservationBundle
)
```

Dataclass

An Agent response and the observations available at its execution boundary.

**`observations`** `AgentObservationBundle`

---

**`response`** `NeMoGymResponse`

---

```python
class nemo_gym.rollout_observability.AgentInvocation()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

One root Agent or subagent conversation observed by a harness.

**`conversation`** `list[NeMoGymResponseInputItem]`

---

**`duration_ms`** `Optional[float] = Field(default=None, ge=0)`

---

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

---

**`invocation_id`** `str`

---

**`kind`** `Literal['agent_invocation'] = 'agent_invocation'`

---

**`model_calls`** `list[ModelCallRef] = Field(default_factory=list)`

---

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

---

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

---

**`status`** `Literal['completed', 'failed', 'incomplete', 'unknown']`

---

```python
class nemo_gym.rollout_observability.AgentObservationBundle()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

Normalized observations returned by one Agent Server for one rollout.

**`gaps`** `list[ObservationGap] = Field(default_factory=list)`

---

**`records`** `list[AgentObservationRecord]`

---

**`source`** `str`

---

```python
nemo_gym.rollout_observability.AgentObservationBundle.validate_identity() -> 'AgentObservationBundle'
```

Require unique invocation IDs and reject cycles in the observed parent graph.

```python
class nemo_gym.rollout_observability.ContextCompactionObservation()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

An explicit context-compaction event reported by the Agent harness.

**`after_model_call`** `Optional[ModelCallRef]`

---

**`before_model_call`** `Optional[ModelCallRef]`

---

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

---

**`invocation_id`** `str`

---

**`kind`** `Literal['context_compaction'] = 'context_compaction'`

---

**`model_calls`** `list[ModelCallRef]`

---

**`observed_at`** `Optional[float] = None`

---

**`outcome`** `Literal['completed', 'failed', 'aborted', 'unknown'] = 'unknown'`

---

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

---

**`tokens_after`** `Optional[int]`

---

**`tokens_before`** `Optional[int]`

---

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

---

```python
class nemo_gym.rollout_observability.ModelCallRef()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

Stable identifiers an Agent integration can observe for one model call.

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

---

**`model_ref`** `Optional[ModelServerRef] = None`

---

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

---

```python
nemo_gym.rollout_observability.ModelCallRef.validate_join_key() -> 'ModelCallRef'
```

```python
class nemo_gym.rollout_observability.ObservationGap()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

A fact that the selected integration could not observe or join exactly.

**`code`** `str`

---

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

---

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

---

```python
class nemo_gym.rollout_observability.ObservationModel()
```

**Bases:** `BaseModel`

**`model_config`**

---

```python
class nemo_gym.rollout_observability.SandboxObservation()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

Outcome and lifetime resource usage reported by a sandbox-owning harness.

**`cpu_time_s`** `Optional[float]`

---

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

---

**`exit_code`** `Optional[int] = None`

---

**`kind`** `Literal['sandbox'] = 'sandbox'`

---

**`outcome`** `Literal['completed', 'failed', 'timeout', 'oom', 'sandbox_error', 'cancelled', 'unknown'] = 'unknown'`

---

**`peak_memory_mib`** `Optional[float]`

---

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

---

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

---

**`role`** `Literal['agent', 'verifier', 'environment']`

---

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

---

**`wall_time_s`** `Optional[float] = Field(default=None, ge=0)`

---

```python
class nemo_gym.rollout_observability.ToolCallObservation()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

Timing observed for one tool call at an Agent-owned boundary.

**`completed_at`** `Optional[float] = None`

---

**`duration_ms`** `Optional[float] = Field(default=None, ge=0)`

---

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

---

**`invocation_id`** `str`

---

**`kind`** `Literal['tool_call'] = 'tool_call'`

---

**`sandbox_id`** `Optional[str]`

---

**`started_at`** `Optional[float] = None`

---

**`status`** `Literal['completed', 'failed', 'timeout', 'cancelled', 'incomplete', 'unknown'] = 'unknown'`

---

**`timing_source`** `Optional[Literal['executor', 'artifact', 'harness']] = None`

---

**`tool_call_id`** `str`

---

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

---

```python
nemo_gym.rollout_observability.ToolCallObservation.validate_timing() -> 'ToolCallObservation'
```

```python
class nemo_gym.rollout_observability.TrajectoryModelCall()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

**`completed_at`** `Optional[float] = None`

---

**`duration_ms`** `Optional[float] = Field(default=None, ge=0)`

---

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

---

**`request`** `Optional[Any] = None`

---

**`response`** `Optional[Any] = None`

---

**`response_metadata`** `TrajectoryResponseMetadata = Field(default_factory=TrajectoryResponseMetadata)`

---

**`started_at`** `Optional[float] = None`

---

**`token_stats`** `TrajectoryTokenStats = Field(default_factory=TrajectoryTokenStats)`

---

```python
class nemo_gym.rollout_observability.TrajectoryRecord()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

**`gaps`** `list[ObservationGap] = Field(default_factory=list)`

---

**`invocations`** `list[AgentInvocation] = Field(default_factory=list)`

---

**`model_calls`** `list[TrajectoryModelCall] = Field(default_factory=list)`

---

**`rollout_id`** `str`

---

**`schema_version`** `Literal['1.0'] = '1.0'`

---

**`task_id`** `str`

---

**`tool_calls`** `list[TrajectoryToolCall] = Field(default_factory=list)`

---

**`turns`** `list[TrajectoryTurn] = Field(default_factory=list)`

---

```python
nemo_gym.rollout_observability.TrajectoryRecord.validate_identity() -> 'TrajectoryRecord'
```

```python
class nemo_gym.rollout_observability.TrajectoryResponseMetadata()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

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

---

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

---

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

---

**`latency_ttft_ms`** `Optional[float] = Field(default=None, ge=0)`

---

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

---

**`model_ref`** `Optional[ModelServerRef] = None`

---

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

---

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

---

**`status_code`** `Optional[int] = None`

---

```python
class nemo_gym.rollout_observability.TrajectoryTokenStats()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

**`cached_tokens`** `Optional[int] = Field(default=None, ge=0)`

---

**`completion_tokens`** `Optional[int] = Field(default=None, ge=0)`

---

**`prompt_tokens`** `Optional[int] = Field(default=None, ge=0)`

---

**`reasoning_tokens`** `Optional[int] = Field(default=None, ge=0)`

---

**`total_tokens`** `Optional[int] = Field(default=None, ge=0)`

---

```python
class nemo_gym.rollout_observability.TrajectoryToolCall()
```

**Bases:** [ToolCallObservation](#nemo_gym-rollout_observability-ToolCallObservation)

Tool observation enriched with the model-visible output in a trajectory record.

**`output`** `Optional[Any] = None`

---

```python
class nemo_gym.rollout_observability.TrajectoryTurn()
```

**Bases:** [ObservationModel](#nemo_gym-rollout_observability-ObservationModel)

**`answer`** `Optional[Any] = None`

---

**`invocation_id`** `str`

---

**`model_calls`** `list[ModelCallRef] = Field(default_factory=list)`

---

**`question`** `Optional[Any] = None`

---

**`reasoning_content`** `Optional[Any] = None`

---

**`resolved`** `Optional[bool] = None`

---

**`rollout_id`** `str`

---

**`step_count`** `int`

---

**`task_id`** `str`

---

**`timestamp`** `float`

---

**`turn_no`** `int`

---

```python
nemo_gym.rollout_observability.join_model_call_observations(
    bundle: nemo_gym.rollout_observability.AgentObservationBundle,
    calls: collections.abc.Iterable[nemo_gym.base_responses_api_model.ModelCallRecord]
) -> nemo_gym.rollout_observability.AgentObservationBundle
```

Resolve harness call references against captured model calls without guessing ownership.

```python
nemo_gym.rollout_observability.AgentObservationRecord = Annotated[AgentInvocation | ToolCallObservation | ContextCompactionObservation |...
```