Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 41 additions & 20 deletions docs/about-nemo-relay/concepts/subscribers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ execution.

Subscribers are consumers of the NeMo Relay event stream. They receive emitted
lifecycle events and use them for observation, forwarding, export, or analysis.
On native Rust, Python, Node.js, and FFI surfaces, event-producing calls enqueue
In the native Rust, Python, Node.js, and FFI bindings, event-producing calls enqueue
subscriber delivery on a process-wide background dispatcher and return without
waiting for subscriber callbacks or exporter work.

Expand All @@ -31,10 +31,10 @@ defined by its API. On native targets, these are separate milestones.

That separation matters:

- The runtime can emit one canonical event stream
- Native event calls stay non-blocking for subscriber work
- Many subscribers can consume that same stream
- Observability behavior stays downstream from execution semantics
- The runtime can emit one ATOF event stream.
- Native event calls stay non-blocking for subscriber work.
- Many subscribers can consume the same stream.
- Observability behavior stays separate from call execution.

## Registration Levels

Expand All @@ -61,12 +61,33 @@ components.

## What Subscribers Consume

Subscribers consume the canonical event stream. They do not define the event
model. They react to it.
Subscribers consume the ATOF event stream. They do not define the event model.
They react to it.

This lets plain subscribers, exporters, and tracing adapters share one runtime
source of truth.

### ATOF Events and ATIF Trajectories

ATOF and ATIF describe different stages of the observability path:

| Format | What It Represents | What Subscribers or Exporters Do With It |
Comment thread
afourniernv marked this conversation as resolved.
| --- | --- | --- |
| ATOF | The canonical raw scope, tool, LLM, middleware, and mark event stream | Subscribers receive these events directly. The ATOF JSONL exporter writes one raw event per line. |
| ATIF | A trajectory projection assembled from related ATOF events | The ATIF exporter maps model and tool activity into trajectory steps for analysis, replay, or evaluation. |

Marks are point-in-time ATOF events, not ATIF trajectory steps, so ATIF `steps`
are not a lossless event log. ATIF is designed primarily for evaluation and
visualization. Its steps do not represent the complete parent-child graph needed
for perfect session replay. This limitation comes from the ATIF data model, not
from converting ATOF events. Plugin-managed ATIF files preserve nested scope
lineage and the raw events associated with the trajectory under
`extra.observed_events`, including observed marks, for analysis and debugging.
Those fields do not change the ATIF step model or make `steps` a complete replay
log. Refer to
[Agent Trajectory Interchange Format (ATIF)](/configure-plugins/observability/atif)
for the complete projection and file contract.

## Common Subscriber Roles

Subscribers are commonly used for in-process observation, counters, debugging, and
Expand All @@ -80,16 +101,17 @@ debugging logic.
#### Host Integration Event JSON

For host integrations that need a serialized event payload, use the event
object's canonical JSON helpers instead of reconstructing payloads from native
attributes. Python subscribers can call `event.to_dict()` or `event.to_json()`
from the callback while still using the normal subscriber registration API.
object's JSON serialization helpers instead of reconstructing payloads from
native attributes. Python subscribers can call `event.to_dict()` or
`event.to_json()` from the callback, while continuing to use the normal
subscriber registration API.

This pattern is useful when an agent runtime, framework adapter, or plugin host
already has its own lifecycle hooks but wants NeMo Relay to be the shared
telemetry representation. The host integration maps those hooks into NeMo Relay
scopes, LLM calls, tool calls, or marks. NeMo Relay emits the canonical ATOF event
ATOF event representation. The host integration maps those hooks into NeMo
Relay scopes, LLM calls, tool calls, or marks. NeMo Relay emits the ATOF event
stream, and each subscriber chooses whether to consume the native event object,
the canonical JSON helper, or an exporter-specific translation.
serialized event JSON, or an exporter-specific translation.

<MermaidStyles />

Expand Down Expand Up @@ -121,11 +143,10 @@ flowchart
Json -. host consumes canonical telemetry .-> Host
```

The important boundary is that subscribers do not define the event schema. They
receive the runtime event and can serialize it through the binding helper when
they need a stable JSON payload. Exporter subscribers, such as the ATOF JSONL
exporter, consume the same event stream and serialize the same canonical event
shape for their target backend.
Subscribers do not define the event schema. They receive the runtime event and
can serialize it through a binding helper when they need JSON. Exporter
subscribers, such as the ATOF JSONL exporter, consume the same event stream and
serialize it for their target backend.

Native subscribers are invoked by one process-wide worker thread in FIFO event
order and subscriber snapshot order.
Expand Down Expand Up @@ -226,7 +247,7 @@ backends. Each subscriber uses one `full`, `gen_ai`, or `openinference`
projection. Select `openinference` for model-centric OpenInference semantics;
there is no separate OpenInference subscriber.

Detailed setup, configuration, and API shape for this subscriber belongs in
Detailed setup, configuration, and API behavior for this subscriber belongs in
[Observability](/configure-plugins/observability/about).
For configuration-driven setup, use the built-in
[`observability` plugin](/configure-plugins/observability/configuration)
Expand All @@ -243,7 +264,7 @@ Use these practices when applying the concept in application or integration code
- Use the exporter's documented barrier before inspecting exporter output.
- Clear plugin-managed exporters during graceful shutdown.
- Use `event.to_dict()` or `event.to_json()` when a host runtime or exporter
needs the canonical event JSON shape in-process.
needs the ATOF event as JSON in process.
- Use a scope-local subscriber when the observation should disappear with the
owning scope.
- Use a plugin-installed subscriber when the behavior should be reusable and
Expand Down
Loading