Skip to content

feat: add streaming ATOF endpoints#232

Merged
rapids-bot[bot] merged 4 commits into
NVIDIA:mainfrom
willkill07:wkk_feat_streaming-atof-exporter
Jun 5, 2026
Merged

feat: add streaming ATOF endpoints#232
rapids-bot[bot] merged 4 commits into
NVIDIA:mainfrom
willkill07:wkk_feat_streaming-atof-exporter

Conversation

@willkill07
Copy link
Copy Markdown
Member

@willkill07 willkill07 commented Jun 5, 2026

Overview

Adds optional streaming endpoints to the ATOF exporter while preserving the existing JSONL file output path. Each raw ATOF event can now fan out to configured HTTP POST, WebSocket, or long-lived NDJSON endpoints, and nemo-relay doctor probes configured endpoints with a synthetic ATOF mark event.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Details

  • Extended Rust ATOF exporter config with endpoint definitions, transport parsing, startup validation, per-endpoint unbounded FIFO queues, terminal flush behavior, and best-effort streaming failure handling.
  • Added observability plugin schema/editor support, endpoint validation, wasm unsupported diagnostics, and doctor endpoint probes for http_post, websocket, and ndjson.
  • Updated Python, Node.js, FFI, Go, and Wasm type/config surfaces, preserving the existing FFI constructor and adding a JSON-config constructor for endpoint-aware creation.
  • Added focused core, CLI, Python, Node.js, Go, and plugin tests, plus ATOF/configuration/troubleshooting documentation updates.

Where should the reviewer start?

Start with crates/core/src/observability/atof.rs for exporter lifecycle and transport behavior, then crates/core/src/observability/plugin_component.rs and crates/cli/src/doctor.rs for config validation and doctor probing.

Validation run:

  • cargo test -p nemo-relay observability::atof --lib
  • cargo test -p nemo-relay observability::plugin_component --lib
  • cargo test -p nemo-relay-cli collect_observability_probes_atof_streaming_endpoint
  • cargo test -p nemo-relay-cli --test cli_tests
  • just build-python
  • just build-node
  • just build-go
  • uv run pytest python/tests/test_observability_plugin.py python/tests/test_types.py -q
  • node --test tests/atof_tests.mjs tests/observability_plugin_tests.mjs
  • go test -v -run 'Test(NewAtofExporterConfigDefaults|AtofExporterLifecycleWritesRawJSONL|AtofExporterAppendAndOverwriteModes|ObservabilityConfigHelpers|ObservabilityPluginAtofAndAtifFiles|ObservabilityPluginValidationRejectsBadValues)' ./...
  • cargo check -p nemo-relay --no-default-features
  • just docs
  • Commit hook: pre-commit checks, including cargo clippy, cargo check, cargo deny, attribution sync, Go vet/fmt, and Node prettier checks.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

Summary by CodeRabbit

  • New Features

    • ATOF streaming: send observability events to multiple endpoints (http_post, websocket, ndjson) with per-endpoint headers/timeouts; exporter lifecycle, flush/shutdown, and language/FFI bindings updated across Python, Node, Go, Rust, WASM, and C.
  • Tests

    • New unit/integration tests covering streaming transports, validation, FIFO/reconnect behavior, doctor probing, and FFI error cases.
  • Documentation

    • Updated docs and runbooks with examples, validation rules, and diagnostics for streaming endpoints.
  • Chores

    • License attributions standardized to SPDX identifiers and inlined license texts.

@willkill07 willkill07 requested review from a team as code owners June 5, 2026 16:36
@github-actions github-actions Bot added size:XL PR is extra large Feature a new feature lang:go PR changes/introduces Go code lang:js PR changes/introduces Javascript/Typescript code labels Jun 5, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds configurable ATOF streaming endpoints (http_post, websocket, ndjson), exposes them in plugin and language bindings, implements per-endpoint workers in the ATOF exporter, adds CLI doctor probes, tests, docs, and updates Rust license attributions.

Changes

ATOF Streaming Endpoint Implementation

Layer / File(s) Summary
Core endpoint contracts
crates/core/src/observability/atof.rs
Defines AtofEndpointTransport, AtofEndpointConfig, extends AtofExporterConfig with endpoints and builder APIs.
Core exporter runtime
crates/core/src/observability/atof.rs
Event serialized once to JSON, written to JSONL, and enqueued as raw JSON to per-endpoint workers; adds stored-failure propagation and closed flag.
Endpoint streaming subsystem
crates/core/src/observability/atof.rs
Per-endpoint worker lifecycle and transport-specific async loops: HTTP POST (NDJSON), NDJSON upload, WebSocket with buffering/reconnect; flush/shutdown semantics implemented.
Plugin config & validation
crates/core/src/observability/plugin_component.rs
Adds AtofEndpointSectionConfig, exposes endpoints in editor/schema, builds core endpoint configs during registration, and validates per-endpoint fields (url, transport, timeout) with wasm/feature gating.
CLI doctor probing
crates/cli/src/doctor.rs
Enumerates configured endpoints and probes them per transport (http_post, ndjson, websocket), strictly parses headers, applies timeouts, and maps outcomes to Pass/Fail.
CLI editor model
crates/cli/src/plugins/editor_model.rs
Box ComponentEditorState<T> variants for Observability, Adaptive, NeMo Guardrails.
Build & deps
crates/*/Cargo.toml
Adds atof-streaming feature, adjusts reqwest/tokio-stream features, and adds optional tokio-tungstenite/futures-util deps.
FFI JSON constructor
crates/ffi/src/api/observability.rs, crates/ffi/nemo_relay.h
Add nemo_relay_atof_exporter_create_from_json C API that deserializes JSON into AtofExporterConfig and constructs exporter; integration tests for error mapping.
Go FFI usage & types
go/nemo_relay/nemo_relay.go, go/nemo_relay/observability_plugin.go
Add endpoint types and JSON-based constructor path when endpoints present; test coverage for defaults/serialization.
Node bindings & typings
crates/node/src/api/mod.rs, crates/node/observability.d.ts
Add AtofEndpointConfig type, parse/validate endpoints in build_atof_config, and expose endpoints in d.ts; Node tests added/updated.
Python bindings & stubs
crates/python/src/py_types/observability.rs, python/nemo_relay/observability.py, python/nemo_relay/_native.pyi
Add PyAtofEndpointConfig and conversion, extend PyAtofExporterConfig to include endpoints, add dataclass binding and to_dict, update package exports and stubs, and add tests.
Wasm ESM typings
crates/wasm/wrappers/esm/observability.d.ts
Add AtofEndpointConfig interface and AtofConfig.endpoints.
Core tests
crates/core/tests/unit/observability/atof_tests.rs
Add HTTP and websocket capture helpers; tests for streamed marks, websocket FIFO/reconnect, invalid-endpoint construction, and lifecycle behavior.
Plugin tests
crates/core/tests/unit/observability/plugin_component_tests.rs
Editor/schema checks for endpoints, completeness tests, and validation tests asserting nested diagnostic paths.
CLI doctor tests
crates/cli/tests/coverage/doctor_tests.rs
Local HTTP capture server and tests asserting doctor probe sends expected nemo_relay.doctor.atof_probe mark and proper failure on invalid scheme.
Language binding tests
crates/node/tests/*, python/tests/*, go/*_test.go, crates/ffi/tests/integration/api_tests.rs
Assert endpoint serialization, defaults, invalid-transport errors, and FFI error-status mapping.
Docs & runbook
docs/observability-plugin/atof.mdx, docs/observability-plugin/configuration.mdx, docs/build-plugins/*, docs/resources/troubleshooting/*
Document endpoint fields, transports, lifecycle (force_flush/shutdown), validation failures, and update examples for Rust/Node/Python/Go.
Rust attributions
ATTRIBUTIONS-Rust.md
Replace combined license labels with SPDX identifiers and add/inline full license texts for several added/updated crates (data-encoding, sha1, tokio-tungstenite, tungstenite, etc.).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code labels Jun 5, 2026
@willkill07 willkill07 added this to the 0.4 milestone Jun 5, 2026
@willkill07 willkill07 self-assigned this Jun 5, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/core/src/observability/plugin_component.rs (1)

1440-1463: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align endpoint support diagnostics with actual runtime support.

This currently warns/errors on atof.endpoints for any wasm config, even when atof.enabled = false, but native builds without atof-streaming never emit the equivalent unsupported diagnostic at all. That gives the least portable behavior possible: dormant configs fail on wasm, while active configs on unsupported native builds validate and then get dropped later by start_endpoint_workers. Gate this on section.enabled and add the missing not(feature = "atof-streaming") branch. Based on learnings, "Ensure all language bindings (Python, Go, Node.js, and WebAssembly) expose the same logical knobs and semantics for observability config and subscriber/exporter methods".

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/doctor.rs`:
- Around line 653-655: The ATOF endpoint probes must be skipped when the binary
cannot stream (wasm32 target or built without the atof-streaming feature): in
the doctor flow, instead of unconditionally calling
observability_atof_endpoint_checks(config).await when section_enabled(config,
"atof"), gate that call with the same compile/runtime condition used by the
exporter (e.g. only run if cfg!(not(target_arch = "wasm32")) && cfg!(feature =
"atof-streaming")); otherwise push a single "unsupported" check into checks
(using the same check type used elsewhere) explaining ATOF streaming is not
available. Apply the same change to the other occurrences noted (the block
around the 735-748 range) so probing is consistently skipped when streaming is
disabled.

In `@crates/cli/tests/coverage/doctor_tests.rs`:
- Around line 696-731: The test
collect_observability_probes_atof_streaming_endpoint currently reads the
captured body immediately and can race with async delivery; update the test to
wait for the probe payload before asserting by polling the shared body returned
from start_doctor_http_capture_server (use body.lock().unwrap()) with a short
timeout/retry loop (e.g., loop with tokio::time::sleep and an overall
tokio::time::timeout) until the body is non-empty or contains the expected
strings, then perform the assert_eq!(endpoint.status, Status::Pass) and the
contains checks; ensure you reference the test function name
collect_observability_probes_atof_streaming_endpoint and collection call
collect_observability for locating where to add the polling/wait logic.

In `@crates/core/src/observability/atof.rs`:
- Around line 431-445: start_endpoint_workers currently swallows
start_endpoint_worker errors (logging and dropping invalid endpoints) which
hides malformed configs from AtofExporter::new; change start_endpoint_workers to
return a Result<Vec<AtofEndpointWorker>, Error> (or a specific error enum) and
propagate any Err from start_endpoint_worker instead of filter_map-ing them, so
AtofExporter::new (and callers using AtofExporterConfig) receive a hard failure
on invalid endpoint configs; update all call sites (including AtofExporter::new)
to handle or propagate the Result accordingly and preserve the existing log
context when converting/returning the error.
- Around line 587-593: The current startup path aborts on a single
connect_websocket error by calling drain_closed(rx) and returning, which
prevents any retries; instead initialize the stream as disconnected and let the
existing reconnect logic handle retries: when connect_websocket(&config).await
returns Err(error) do not call drain_closed(rx) or return, set socket = None (or
otherwise mark as disconnected) and proceed so the existing reconnect path can
attempt subsequent connections; update the code around the socket variable
initialization and any startup control flow to avoid early return on the first
connect failure and ensure reconnect handler (the existing reconnect loop) will
attempt reconnects.
- Around line 705-746: The loop is blocking on a std::sync::mpsc::Receiver
(rx.recv()) so the spawned async upload task (request) cannot make progress;
change the worker to use an async receiver (tokio::sync::mpsc::Receiver) and
await messages instead of blocking. Concretely, replace the blocking rx with a
tokio mpsc receiver and use while let Some(message) = rx.recv().await (matching
EndpointMessage::Event/Close) so body_tx.send(...) can be observed by the upload
task; keep the created body/body_tx/body_rx and the request spawn logic
(client.post(...).body(body).send().await) but ensure you only rely on async
recv paths before/while the request runs so the streaming upload proceeds.
Ensure EndpointMessage::Close still drops body_tx, awaits request, and signals
done.

In `@crates/ffi/src/api/observability.rs`:
- Around line 362-369: The current call to c_str_to_json collapses null/UTF-8
failures into InvalidJson; update the handling around c_str_to_json in
observability.rs so you match on its detailed outcome (or change it to return a
Result/enum) and return NemoRelayStatus::NullPointer when the C string is null
and NemoRelayStatus::InvalidUtf8 when it fails UTF‑8 decoding, only falling back
to parsing and returning NemoRelayStatus::InvalidJson for serde_json::from_value
failures; preserve the existing set_last_error(&error.to_string()) for the serde
error branch so error text is still recorded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 40455538-1a49-4b1a-923d-021efb9fcf53

📥 Commits

Reviewing files that changed from the base of the PR and between 559cce7 and 5eccee2.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (37)
  • ATTRIBUTIONS-Rust.md
  • crates/cli/Cargo.toml
  • crates/cli/src/doctor.rs
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/core/Cargo.toml
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/Cargo.toml
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/node/Cargo.toml
  • crates/node/observability.d.ts
  • crates/node/src/api/mod.rs
  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/python/Cargo.toml
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_types/observability.rs
  • crates/wasm/wrappers/esm/observability.d.ts
  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/observability-plugin/atof.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/observability_plugin_test.go
  • python/nemo_relay/__init__.py
  • python/nemo_relay/__init__.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py

Comment thread crates/cli/src/doctor.rs Outdated
Comment thread crates/cli/tests/coverage/doctor_tests.rs
Comment thread crates/core/src/observability/atof.rs Outdated
Comment thread crates/core/src/observability/atof.rs
Comment thread crates/core/src/observability/atof.rs Outdated
Comment thread crates/ffi/src/api/observability.rs Outdated
@willkill07 willkill07 force-pushed the wkk_feat_streaming-atof-exporter branch from 5eccee2 to 2f9f2bb Compare June 5, 2026 17:06
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/core/src/observability/plugin_component.rs (1)

1440-1464: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Report atof.endpoints as unsupported when this build lacks streaming.

On non-wasm32 builds compiled without atof-streaming, validation currently emits no diagnostic for atof.endpoints, so validate_plugin_config can pass a config that register_atof_exporter later rejects as unsupported. This should mirror the existing wasm and object-store feature-disabled checks.

Suggested direction
+        #[cfg(not(feature = "atof-streaming"))]
+        if !section.endpoints.is_empty() {
+            push_policy_diag(
+                &mut diagnostics,
+                config.policy.unsupported_value,
+                "observability.feature_disabled",
+                Some("atof".to_string()),
+                Some("endpoints".to_string()),
+                "ATOF streaming endpoints are not enabled in this build".to_string(),
+            );
+        }

Also applies to: 1638-1646

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/core/src/observability/plugin_component.rs` around lines 1440 - 1464,
Validation currently only reports unsupported ATOF endpoints for wasm32 but not
for builds compiled without the "atof-streaming" feature, so add a conditional
diagnostic like the wasm32 block that triggers when section.endpoints is not
empty under #[cfg(not(feature = "atof-streaming"))]; specifically, inside the
branch that checks if let Some(section) = &config.atof (after
validate_atof_values is called), call push_policy_diag with
config.policy.unsupported_value, key "observability.unsupported_value", plugin
"atof", field "endpoints" and an explanatory message similar to the wasm32
message; make the same change in the other duplicated location around
validate_plugin_config/register_atof_exporter so configs are rejected
consistently before register_atof_exporter later rejects them.
♻️ Duplicate comments (1)
crates/core/src/observability/plugin_component.rs (1)

605-619: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not register with silently dropped endpoints.

This filter_map logs build_atof_endpoint_config failures and then proceeds with a partial config. Callers that register without a separate validate phase will lose endpoints silently instead of getting an invalid-config error.

Suggested fix
-    let endpoints = section
-        .endpoints
-        .into_iter()
-        .enumerate()
-        .filter_map(
-            |(index, endpoint)| match build_atof_endpoint_config(index, endpoint) {
-                Ok(endpoint) => Some(endpoint),
-                Err(error) => {
-                    eprintln!("nemo_relay: disabled ATOF endpoint[{index}]: {error}");
-                    None
-                }
-            },
-        )
-        .collect();
+    let endpoints = section
+        .endpoints
+        .into_iter()
+        .enumerate()
+        .map(|(index, endpoint)| build_atof_endpoint_config(index, endpoint))
+        .collect::<PluginResult<Vec<_>>>()?;
     config = config.with_endpoints(endpoints);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/core/src/observability/plugin_component.rs` around lines 605 - 619,
The current filter_map swallows errors from build_atof_endpoint_config and
produces a partially-complete endpoints list; instead, change the logic to
propagate any build error so the caller receives an Err rather than silently
registering a subset. Replace the filter_map/collect with a fallible collection
(e.g., map build_atof_endpoint_config over section.endpoints and collect into
Result<Vec<_>, _>) and return or propagate that error up the call path rather
than calling config.with_endpoints on a truncated list; reference
build_atof_endpoint_config, endpoints, and config.with_endpoints when making the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ATTRIBUTIONS-Rust.md`:
- Around line 13516-13535: The inserted license block in ATTRIBUTIONS-Rust.md
contains CRLF line endings; normalize the file to LF to remove Windows-style
carriage returns by regenerating the attributions with the repository tooling
(run ./scripts/generate_attributions.sh rust) or convert the file endings (e.g.,
dos2unix or git checkout/commit with core.eol settings), then re-commit the
updated ATTRIBUTIONS-Rust.md so the license text no longer contains CRLF
artifacts.
- Around line 3124-3128: The "## block-buffer - 0.10.4" attribution block (and
other listed blocks) is violating markdownlint by missing blank lines around
headings and fenced code blocks and using unlabeled fences; update each license
section (e.g., the block starting with "## block-buffer - 0.10.4") to ensure
there is a blank line before the heading and before/after the ``` fenced block,
and change anonymous fences to a labeled code fence like ```text for the license
content; apply the same fix to the other mentioned blocks (7481-7486, 8108-8112,
8735-8740, 8795-8799, 32188-32193, 35715-35720, 37135-37140, 38725-38727,
38754-38759, 38806-38811, 39015-39020, 39596-39600) and run "uv run pre-commit
run --all-files" to validate formatting.

In `@crates/cli/tests/coverage/doctor_tests.rs`:
- Around line 18-43: The thread::spawn call returns a JoinHandle that must be
used to avoid unused_must_use warnings; capture the return value (e.g., let
handle = std::thread::spawn(...)) and ensure you call JoinHandle::join()
(handle.join().unwrap() or propagate the Result) after the spawned work
completes (or otherwise store it so the handle is dropped intentionally),
updating the closure invocation in the test to use the handle instead of
discarding it.

In `@crates/core/src/observability/atof.rs`:
- Around line 413-425: Replace the unbounded endpoint queue with a bounded tokio
mpsc channel: change AtofEndpointWorker.sender to a bounded
tokio::sync::mpsc::Sender<EndpointMessage> and create it with
tokio::sync::mpsc::channel(capacity) instead of unbounded_channel; in
AtofEndpointWorker::enqueue use try_send and implement an explicit overflow
policy (e.g., log and drop-newest, or drop-oldest by receiving one item then
retry) rather than letting the queue grow unbounded; update
AtofEndpointWorker::close to still send EndpointMessage::Close but be prepared
to handle a send error from the bounded Sender and to await the ack in a
non-blocking way (or convert the ack path to tokio::sync::oneshot for async
waits); apply the same bounded-queue change and overflow handling to the other
endpoint worker referenced around the 458-460 area so both streaming paths use
the same capacity and overflow policy.

In `@crates/python/src/py_types/observability.rs`:
- Around line 165-166: Update the docstring for the Python-exposed type
AtofEndpointConfig (the #[pyclass(name = "AtofEndpointConfig", from_py_object)]
struct) so it describes a streaming endpoint config rather than a
filesystem-backed exporter: mention that it configures remote/streaming
endpoints including fields like url, transport, headers, and timeout, and
clarify expected formats/defaults so Python help() shows the correct behavior
for NeMo Relay streaming endpoints.

---

Outside diff comments:
In `@crates/core/src/observability/plugin_component.rs`:
- Around line 1440-1464: Validation currently only reports unsupported ATOF
endpoints for wasm32 but not for builds compiled without the "atof-streaming"
feature, so add a conditional diagnostic like the wasm32 block that triggers
when section.endpoints is not empty under #[cfg(not(feature =
"atof-streaming"))]; specifically, inside the branch that checks if let
Some(section) = &config.atof (after validate_atof_values is called), call
push_policy_diag with config.policy.unsupported_value, key
"observability.unsupported_value", plugin "atof", field "endpoints" and an
explanatory message similar to the wasm32 message; make the same change in the
other duplicated location around validate_plugin_config/register_atof_exporter
so configs are rejected consistently before register_atof_exporter later rejects
them.

---

Duplicate comments:
In `@crates/core/src/observability/plugin_component.rs`:
- Around line 605-619: The current filter_map swallows errors from
build_atof_endpoint_config and produces a partially-complete endpoints list;
instead, change the logic to propagate any build error so the caller receives an
Err rather than silently registering a subset. Replace the filter_map/collect
with a fallible collection (e.g., map build_atof_endpoint_config over
section.endpoints and collect into Result<Vec<_>, _>) and return or propagate
that error up the call path rather than calling config.with_endpoints on a
truncated list; reference build_atof_endpoint_config, endpoints, and
config.with_endpoints when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 17a40224-4426-475c-bb9b-ff87682efefb

📥 Commits

Reviewing files that changed from the base of the PR and between 5eccee2 and 2f9f2bb.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (38)
  • ATTRIBUTIONS-Rust.md
  • crates/cli/Cargo.toml
  • crates/cli/src/doctor.rs
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/core/Cargo.toml
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/Cargo.toml
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/integration/api_tests.rs
  • crates/node/Cargo.toml
  • crates/node/observability.d.ts
  • crates/node/src/api/mod.rs
  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/python/Cargo.toml
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_types/observability.rs
  • crates/wasm/wrappers/esm/observability.d.ts
  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/observability-plugin/atof.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/observability_plugin_test.go
  • python/nemo_relay/__init__.py
  • python/nemo_relay/__init__.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py

Comment thread ATTRIBUTIONS-Rust.md
Comment thread ATTRIBUTIONS-Rust.md
Comment thread crates/cli/tests/coverage/doctor_tests.rs Outdated
Comment thread crates/core/src/observability/atof.rs Outdated
Comment thread crates/python/src/py_types/observability.rs Outdated
Comment thread crates/core/src/observability/atof.rs Outdated
Comment thread crates/core/src/observability/atof.rs
Comment thread crates/cli/src/doctor.rs Outdated
mnajafian-nv
mnajafian-nv previously approved these changes Jun 5, 2026
Copy link
Copy Markdown
Contributor

@mnajafian-nv mnajafian-nv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks directionally good, and I don’t want to block the overall approach. I left a few inline questions that I think should be resolved before merge, especially around the NDJSON shutdown path and the force_flush() contract change. Happy to re-review quickly once those are addressed.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/ffi/src/api/observability.rs (1)

22-27: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return a caller error for InvalidEndpoint.

The new JSON constructor can now hit AtofExporterError::InvalidEndpoint, but status_from_atof_error maps every non-runtime ATOF error to Internal. Bad endpoint config from the caller will therefore surface as a library fault over FFI. Map InvalidEndpoint to InvalidArg so clients can distinguish malformed config from an internal failure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ffi/src/api/observability.rs` around lines 22 - 27,
status_from_atof_error currently maps all non-runtime AtofExporterError variants
to NemoRelayStatus::Internal; update the match in status_from_atof_error to map
AtofExporterError::InvalidEndpoint to NemoRelayStatus::InvalidArg (keeping
set_last_error(&error.to_string()) as-is) so callers receive InvalidArg for bad
endpoint config instead of Internal; preserve the existing mapping for other
non-runtime variants to NemoRelayStatus::Internal.
crates/core/src/observability/plugin_component.rs (1)

1572-1639: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate unknown keys inside atof.endpoints entries.

Endpoint validation only checks a few known values after deserialization. Since these structs do not reject unknown fields, typos like timeout_ms or header inside an endpoint object are silently ignored and the defaults are applied. Add a per-entry unknown-field check so nested endpoint config is validated as strictly as the rest of the plugin surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/core/src/observability/plugin_component.rs` around lines 1572 - 1639,
Endpoint entries are not being checked for unknown keys; update validation so
validate_atof_endpoint_values (called from validate_atof_values) receives the
raw endpoint map (or make deserialization enforce deny_unknown_fields) and
detect any keys not in the allowed set for AtofEndpointSectionConfig, pushing a
policy diagnostic for each unknown key at path endpoints[{index}].<key>;
implement this by either enabling serde's deny_unknown_fields on
AtofEndpointSectionConfig or by comparing the source map's keys against the
struct's expected field names and calling push_policy_diag (same pattern as
other checks) when extra keys are found.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/doctor.rs`:
- Around line 861-961: Both probe_atof_http_post and probe_atof_ndjson duplicate
the same POST logic; extract the common work into a helper (e.g.,
probe_atof_post_common or probe_atof_post) that takes transport_label: &str
(values "http_post" or "ndjson"), url: &str, headers: Vec<(String,String)>,
payload: String, timeout_duration: Duration, and index: usize; move the rustls
install, reqwest client build, header/body setup, send().await match and Check
construction into that helper but use transport_label when formatting the
details strings, then have probe_atof_http_post and probe_atof_ndjson simply
call the new helper with the appropriate transport_label to eliminate
duplication while preserving behavior and error messages.
- Around line 970-1006: Validate the WebSocket URL scheme before calling
into_client_request(): parse the URL (e.g., via url.scheme() or Url::parse) and
ensure it is "ws" or "wss", and if not return the existing Check failure (same
shape as used in the into_client_request() error case) with a clear message like
"endpoints[{index}] websocket {url}: invalid scheme (must be ws or wss)";
perform this check immediately before the match on into_client_request() so that
into_client_request() and the subsequent
tokio_tungstenite::connect_async(request) are only attempted for valid ws/wss
URLs.

In `@crates/core/Cargo.toml`:
- Around line 17-23: Remove "atof-streaming" from the default feature list in
crates/core/Cargo.toml so it becomes opt-in: update the default = [...] array to
exclude "atof-streaming" and ensure the feature definition for "atof-streaming"
remains in the features table; also add a short note in the crate README or
Cargo.toml description explaining that "atof-streaming" is optional and
consumers can avoid its networking/tokio deps by not enabling that feature or by
using default-features = false when depending on this crate.

In `@crates/core/src/observability/atof.rs`:
- Around line 634-683: The Flush/Close handlers currently acknowledge
immediately while events may still live in pending and reconnect failures clear
pending; update the loop handling EndpointMessage::Event,
EndpointMessage::Flush, and EndpointMessage::Close so that pending is never
cleared on reconnect failure and Flush/Close do not send the done response until
pending is fully drained (or a deterministic fatal error occurs). Concretely: in
the Event branch (where socket, pending, connect_websocket, and ws.send are
used) stop calling pending.clear() on Err returned from connect_websocket and
preserve pending on transient failures; for Flush (EndpointMessage::Flush) wait
(async) until pending.is_empty() and all in-flight sends complete before calling
done.send(()); for Close (EndpointMessage::Close) attempt to drain pending by
reusing connect_websocket and ws.send retries, then close the socket and only
then send the done signal; keep existing error logging but ensure pending
remains intact across reconnect attempts so force_flush()/shutdown() honor the
documented contract.
- Around line 503-520: validate_endpoint_config currently only checks URL syntax
via reqwest::Url::parse but doesn't verify the URL scheme against the selected
transport; update validate_endpoint_config to parse the URL (using
reqwest::Url::parse) and then check url.scheme() against the transport in
AtofEndpointConfig (e.g., require "http" or "https" for "http_post" and
"ndjson", and "ws" or "wss" for "websocket"), returning
Err(AtofExporterError::InvalidEndpoint(...)) with a clear message on mismatch;
keep the existing header build call (build_header_map(&config.headers)?) and the
feature-gated block but add the scheme check inside that validation path so
invalid scheme+transport combos are rejected at construction time.

In `@crates/core/tests/unit/observability/atof_tests.rs`:
- Around line 284-299: The helper start_http_capture_server currently calls
listener.accept() and handles the accepted socket inline, blocking the accept
loop (read_http_request is long-lived for NDJSON), so change the loop to spawn a
new task/thread for each accepted connection: inside the for loop, after let
(stream, _) = listener.accept().unwrap(), clone thread_captures and move the
stream into a thread::spawn closure that reads the request (using
read_http_request), pushes to the captures Vec, and writes the HTTP response;
this ensures the accept loop continues and http_post requests won't be blocked
by a long-lived NDJSON connection.

In `@docs/observability-plugin/configuration.mdx`:
- Line 266: Replace the placeholder endpoints: Vec::new() with a short example
that constructs and adds Endpoint instances so readers can map the TOML to Rust;
show creating one or two Endpoint values (e.g., let endpoint = Endpoint { name:
"metrics".into(), url: "https://...".into(), headers: vec![] }; or using
Endpoint::new(...)) and then either initialize endpoints with vec![endpoint] or
call endpoints.push(endpoint) when building the surrounding config struct
(referencing the endpoints variable, Endpoint struct/Endpoint::new, and the
config struct name used in the snippet).

In `@python/nemo_relay/_native.pyi`:
- Around line 733-750: The stub shows an inconsistency for AtofEndpointConfig:
__init__ accepts headers: dict[str, str] | None but the class field headers is
annotated as dict[str, str]; update the stub to make the behavior explicit by
adding a short docstring note in AtofEndpointConfig/__init__ that a None headers
parameter is converted to an empty dict (so the instance field is non-optional),
or alternatively make the constructor parameter non-optional (headers: dict[str,
str] = {}) to match the field; reference symbols: class AtofEndpointConfig and
its __init__ method and the headers field.

In `@python/nemo_relay/observability.py`:
- Around line 55-74: AtofEndpointConfig currently sets url="" which permits
creating instances with an empty URL even though runtime/FFI require a non-empty
URL; remove the empty-string default on the url field in the AtofEndpointConfig
dataclass so url is a required positional argument at construction time, and
update any call sites that instantiate AtofEndpointConfig() without arguments to
pass a valid url (this change affects the AtofEndpointConfig class definition
referenced in to_dict and any usages creating AtofEndpointConfig instances).

---

Outside diff comments:
In `@crates/core/src/observability/plugin_component.rs`:
- Around line 1572-1639: Endpoint entries are not being checked for unknown
keys; update validation so validate_atof_endpoint_values (called from
validate_atof_values) receives the raw endpoint map (or make deserialization
enforce deny_unknown_fields) and detect any keys not in the allowed set for
AtofEndpointSectionConfig, pushing a policy diagnostic for each unknown key at
path endpoints[{index}].<key>; implement this by either enabling serde's
deny_unknown_fields on AtofEndpointSectionConfig or by comparing the source
map's keys against the struct's expected field names and calling
push_policy_diag (same pattern as other checks) when extra keys are found.

In `@crates/ffi/src/api/observability.rs`:
- Around line 22-27: status_from_atof_error currently maps all non-runtime
AtofExporterError variants to NemoRelayStatus::Internal; update the match in
status_from_atof_error to map AtofExporterError::InvalidEndpoint to
NemoRelayStatus::InvalidArg (keeping set_last_error(&error.to_string()) as-is)
so callers receive InvalidArg for bad endpoint config instead of Internal;
preserve the existing mapping for other non-runtime variants to
NemoRelayStatus::Internal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 24341699-38a4-47f6-8f57-4dd533e9fdcd

📥 Commits

Reviewing files that changed from the base of the PR and between 2f9f2bb and d264902.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (38)
  • ATTRIBUTIONS-Rust.md
  • crates/cli/Cargo.toml
  • crates/cli/src/doctor.rs
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/core/Cargo.toml
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/Cargo.toml
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/integration/api_tests.rs
  • crates/node/Cargo.toml
  • crates/node/observability.d.ts
  • crates/node/src/api/mod.rs
  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/python/Cargo.toml
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_types/observability.rs
  • crates/wasm/wrappers/esm/observability.d.ts
  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/observability-plugin/atof.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/observability_plugin_test.go
  • python/nemo_relay/__init__.py
  • python/nemo_relay/__init__.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py

Comment thread crates/cli/src/doctor.rs
Comment thread crates/cli/src/doctor.rs
Comment thread crates/core/Cargo.toml
Comment thread crates/core/src/observability/atof.rs
Comment thread crates/core/src/observability/atof.rs
Comment thread crates/core/tests/unit/observability/atof_tests.rs
Comment thread docs/observability-plugin/configuration.mdx Outdated
Comment thread python/nemo_relay/_native.pyi
Comment thread python/nemo_relay/observability.py
Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07 willkill07 force-pushed the wkk_feat_streaming-atof-exporter branch from d264902 to 977e8d7 Compare June 5, 2026 18:40
Signed-off-by: Will Killian <2007799+willkill07@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
crates/core/src/observability/atof.rs (2)

648-657: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Do not acknowledge WebSocket Flush or Close after a timed-out drain.

drain_websocket_pending already reports whether the timeout path left pending non-empty, but both branches ignore that result and send the ack anyway. That lets force_flush()/shutdown() report success with unsent events still buffered, and the Close branch drops them when the worker exits.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/core/src/observability/atof.rs` around lines 648 - 657, The Flush and
Close branches in the endpoint loop currently ignore the return from
drain_websocket_pending and always send the completion ack (done.send(())),
which can report success even when pending remained after a timeout; change both
EndpointMessage::Flush and EndpointMessage::Close handling to inspect the result
of drain_websocket_pending (e.g., a boolean or Result indicating whether pending
is empty) and only send the ack when drain_websocket_pending indicates all
messages were flushed; for Close also avoid closing and dropping the socket when
the drain timed out and pending remains so unsent events are not silently
dropped—propagate failure back to force_flush()/shutdown() by not sending the
ack when pending is non-empty.

431-433: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Replace the unbounded endpoint mailboxes with bounded queues.

Both the worker inbox and the NDJSON body stream can accumulate unlimited backlog when an endpoint is slow or unavailable. One bad destination can then grow process memory without bound while the exporter keeps accepting events, which defeats the intended backpressure on this path.

Also applies to: 487-499, 786-789

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/core/src/observability/atof.rs` around lines 431 - 433, The
AtofEndpointWorker uses tokio::sync::mpsc::UnboundedSender which allows
unlimited backlog; replace it with a bounded tokio::sync::mpsc::Sender with a
reasonable capacity to restore backpressure (e.g., replace
UnboundedSender<EndpointMessage> with Sender<EndpointMessage> and create
channels via tokio::sync::mpsc::channel(capacity) where the worker and NDJSON
body stream are spawned). Update all creation sites and call sites referenced
around AtofEndpointWorker, the endpoint inbox code (previously lines ~487-499),
and the NDJSON body stream producer/consumer (previously ~786-789) to await or
handle full-channel behavior (handle TrySendError/await .send().await and treat
send failures/closed channel appropriately), and adjust function signatures and
type aliases that mention UnboundedSender/UnboundedReceiver to the bounded
Sender/Receiver so backpressure prevents unbounded memory growth.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/doctor.rs`:
- Around line 996-1004: The send and close operations in probe_atof_websocket
are not time-bounded, allowing a hung peer to block doctor; wrap both
socket.send(...) and socket.close(None).await calls with
timeout(timeout_duration, ...) (same timeout_duration used for connect) and
handle the Result/Elapsed cases similarly to connect to return or propagate a
timeout error; update the match on send to handle timeout/Err from the timed
send and ensure you still attempt a timed close (or ignore close errors) so the
overall probe is always bounded.

In `@crates/core/src/observability/atof.rs`:
- Around line 800-809: run_ndjson_endpoint currently sends the Flush ack
immediately upon receiving EndpointMessage::Flush(done), which can occur before
the bytes enqueued on body_tx have been consumed by the long-lived
client.post(...).send() task; change the logic so the done.send(()) is only
performed after the upload stream (body_rx) has actually been drained/advanced.
Concretely: in run_ndjson_endpoint, do not call done.send() inline when matching
EndpointMessage::Flush(done); instead propagate a flush marker or a oneshot into
the stream consumed by the upload task (or have the upload task register an
acknowledgement oneshot) and have the upload task send the ack only after it has
consumed all preceding Ok(…) chunks from body_rx and progressed the request; use
the existing symbols EndpointMessage::Flush(done), body_tx/body_rx, and the
client.post(...).send() upload task to implement this synchronization so
force_flush() waits for actual network/drain completion before returning.

In `@crates/ffi/src/api/observability.rs`:
- Around line 380-386: The constructor error from AtofExporter::new(config) is
currently passed to status_from_atof_error which collapses invalid
endpoint/config validation failures to Internal; update the match so
validation/argument errors return NemoRelayStatus::InvalidArg instead of
delegating to status_from_atof_error. Locate the AtofExporter::new(...) branch
in the function that writes to out (FfiAtofExporter) and either (a) detect the
validation error variant/type from the Err(error) and return
NemoRelayStatus::InvalidArg, or (b) extend status_from_atof_error to map that
specific endpoint/validation error variant to NemoRelayStatus::InvalidArg,
ensuring other runtime/exporter errors still map through the existing logic.

---

Duplicate comments:
In `@crates/core/src/observability/atof.rs`:
- Around line 648-657: The Flush and Close branches in the endpoint loop
currently ignore the return from drain_websocket_pending and always send the
completion ack (done.send(())), which can report success even when pending
remained after a timeout; change both EndpointMessage::Flush and
EndpointMessage::Close handling to inspect the result of drain_websocket_pending
(e.g., a boolean or Result indicating whether pending is empty) and only send
the ack when drain_websocket_pending indicates all messages were flushed; for
Close also avoid closing and dropping the socket when the drain timed out and
pending remains so unsent events are not silently dropped—propagate failure back
to force_flush()/shutdown() by not sending the ack when pending is non-empty.
- Around line 431-433: The AtofEndpointWorker uses
tokio::sync::mpsc::UnboundedSender which allows unlimited backlog; replace it
with a bounded tokio::sync::mpsc::Sender with a reasonable capacity to restore
backpressure (e.g., replace UnboundedSender<EndpointMessage> with
Sender<EndpointMessage> and create channels via
tokio::sync::mpsc::channel(capacity) where the worker and NDJSON body stream are
spawned). Update all creation sites and call sites referenced around
AtofEndpointWorker, the endpoint inbox code (previously lines ~487-499), and the
NDJSON body stream producer/consumer (previously ~786-789) to await or handle
full-channel behavior (handle TrySendError/await .send().await and treat send
failures/closed channel appropriately), and adjust function signatures and type
aliases that mention UnboundedSender/UnboundedReceiver to the bounded
Sender/Receiver so backpressure prevents unbounded memory growth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 125adc47-c847-4ff0-8685-d6e4569ed55c

📥 Commits

Reviewing files that changed from the base of the PR and between d264902 and 977e8d7.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (38)
  • ATTRIBUTIONS-Rust.md
  • crates/cli/Cargo.toml
  • crates/cli/src/doctor.rs
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/core/Cargo.toml
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/Cargo.toml
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/integration/api_tests.rs
  • crates/node/Cargo.toml
  • crates/node/observability.d.ts
  • crates/node/src/api/mod.rs
  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/python/Cargo.toml
  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_types/observability.rs
  • crates/wasm/wrappers/esm/observability.d.ts
  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/observability-plugin/atof.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/observability_plugin_test.go
  • python/nemo_relay/__init__.py
  • python/nemo_relay/__init__.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (66)
{crates/adaptive/**,python/nemo_relay/adaptive.py,python/nemo_relay/plugin.py,go/nemo_relay/adaptive/**,go/nemo_relay/!(adaptive)/**,**/node/**,**/wasm/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Keep adaptive surface in sync across crates/adaptive, shared plugin behavior in core and bindings, Python adaptive/plugin wrappers in python/nemo_relay/adaptive.py and python/nemo_relay/plugin.py, Go adaptive helpers under go/nemo_relay/adaptive plus shared plugin helpers in go/nemo_relay, and Node/WebAssembly adaptive helpers and plugin wrappers

Files:

  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/observability.d.ts
  • crates/wasm/wrappers/esm/observability.d.ts
  • crates/node/Cargo.toml
  • crates/node/src/api/mod.rs
{crates/adaptive/**,python/nemo_relay/plugin.py,go/nemo_relay/**,**/node/**,**/wasm/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

{crates/adaptive/**,python/nemo_relay/plugin.py,go/nemo_relay/**,**/node/**,**/wasm/**}: Maintain consistent plugin lifecycle across all language bindings (Python, Go, Node/WebAssembly, and Rust)
Keep plugin context surfaces aligned across all language implementations

Files:

  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • go/nemo_relay/observability_plugin_test.go
  • crates/node/observability.d.ts
  • go/nemo_relay/atof_test.go
  • crates/wasm/wrappers/esm/observability.d.ts
  • crates/node/Cargo.toml
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
  • crates/node/src/api/mod.rs
crates/{python,ffi,node,wasm}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node,wasm}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/python/src/py_types/mod.rs
  • crates/node/observability.d.ts
  • crates/python/Cargo.toml
  • crates/wasm/wrappers/esm/observability.d.ts
  • crates/node/Cargo.toml
  • crates/ffi/Cargo.toml
  • crates/ffi/nemo_relay.h
  • crates/ffi/tests/integration/api_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • go/nemo_relay/observability_plugin_test.go
  • go/nemo_relay/atof_test.go
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/tests/integration/api_tests.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation,
integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # P...

Files:

  • crates/node/tests/atof_tests.mjs
  • crates/node/tests/observability_plugin_tests.mjs
  • docs/build-plugins/plugin-configuration-files.mdx
  • python/nemo_relay/__init__.pyi
  • go/nemo_relay/observability_plugin_test.go
  • crates/python/src/py_types/mod.rs
  • crates/node/observability.d.ts
  • go/nemo_relay/atof_test.go
  • crates/python/Cargo.toml
  • crates/wasm/wrappers/esm/observability.d.ts
  • crates/node/Cargo.toml
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • crates/ffi/Cargo.toml
  • crates/ffi/nemo_relay.h
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/cli/Cargo.toml
  • python/nemo_relay/__init__.py
  • crates/ffi/tests/integration/api_tests.rs
  • python/nemo_relay/observability.pyi
  • docs/observability-plugin/configuration.mdx
  • crates/core/Cargo.toml
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
  • crates/cli/src/plugins/editor_model.rs
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • docs/observability-plugin/atof.mdx
  • crates/cli/src/doctor.rs
  • ATTRIBUTIONS-Rust.md
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
{docs/**,README.md,CONTRIBUTING.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

{docs/**,README.md,CONTRIBUTING.md}: For docs-only changes, run targeted checks only if commands, package names, or examples changed. Use just docs for docs-site builds and just docs-linkcheck when links changed
Run docs site build with just docs

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
{docs/**,README.md,CONTRIBUTING.md,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run docs link validation with just docs-linkcheck when links change

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
  • ATTRIBUTIONS-Rust.md
{docs/**,README.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Verify README and docs entry points still match current package names and paths for large or public-facing changes

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
{docs/**,examples/**,README.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Verify examples still run with documented commands for large or public-facing changes

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
{docs/**,README.md,**/Cargo.toml,**/package.json,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Ensure renamed public surfaces are reflected consistently in manifests and docs for large or public-facing changes

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • crates/python/Cargo.toml
  • crates/node/Cargo.toml
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • crates/ffi/Cargo.toml
  • crates/cli/Cargo.toml
  • docs/observability-plugin/configuration.mdx
  • crates/core/Cargo.toml
  • docs/observability-plugin/atof.mdx
  • ATTRIBUTIONS-Rust.md
**/*.{md,mdx,py,sh,yaml,yml,toml,json}

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

Keep package names, repo references, and build commands current

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • crates/python/Cargo.toml
  • crates/node/Cargo.toml
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • crates/ffi/Cargo.toml
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • crates/cli/Cargo.toml
  • python/nemo_relay/__init__.py
  • docs/observability-plugin/configuration.mdx
  • crates/core/Cargo.toml
  • python/nemo_relay/observability.py
  • docs/observability-plugin/atof.mdx
  • ATTRIBUTIONS-Rust.md
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

In MDX files, top-of-file comments must use JSX comment delimiters: {/* to open and */} to close. Do not use HTML comments for MDX SPDX headers.

MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
**/*.{html,md,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license header in HTML and Markdown files using HTML comment syntax

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
  • ATTRIBUTIONS-Rust.md
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update embedded documentation snippets, patch docs, and binding-support notes if examples or supported bindings changed

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
docs/**

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run just docs or ./scripts/build-docs.sh html to regenerate ignored Fern API reference pages before validation for documentation site changes

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.

Files:

  • docs/build-plugins/plugin-configuration-files.mdx
  • docs/resources/troubleshooting/trace-incident-runbook.mdx
  • docs/observability-plugin/configuration.mdx
  • docs/observability-plugin/atof.mdx
{crates/python/src/py_api/**/*.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update Python native binding in crates/python/src/py_api/mod.rs with Python wrapper docstring in python/nemo_relay/<module>.py and type stubs in python/nemo_relay/*.pyi modules

Files:

  • python/nemo_relay/__init__.pyi
  • python/nemo_relay/__init__.py
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
python/nemo_relay/**/*

⚙️ CodeRabbit configuration file

python/nemo_relay/**/*: Review Python wrapper changes for typed API consistency, contextvars-based scope isolation, async behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/nemo_relay/__init__.pyi
  • python/nemo_relay/__init__.py
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
go/nemo_relay/**/*.go

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update Go wrapper in go/nemo_relay/nemo_relay.go with doc comment and shorthand package if the capability belongs there

go/nemo_relay/**/*.go: Format changed Go packages with cd go/nemo_relay && go fmt ./...
Run Go tests with just test-go to build and test the NeMo Relay Go binding
Use just build-go when you want an explicit build-only pass or need the artifact for other work
Use just ci=true test-go when you need the CI-style coverage and JUnit path
On macOS, set DYLD_LIBRARY_PATH to the ../../target/release directory before running the raw go test command directly

Files:

  • go/nemo_relay/observability_plugin_test.go
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
go/**/*.go

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use PascalCase naming convention for Go identifiers (e.g., nemo_relay.ToolCall)

Run Go formatting with cd go/nemo_relay && go fmt ./...

Files:

  • go/nemo_relay/observability_plugin_test.go
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • go/nemo_relay/observability_plugin_test.go
  • go/nemo_relay/atof_test.go
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/tests/integration/api_tests.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
{go/nemo_relay/go.mod,go/**/*.go}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure Go module path in go/nemo_relay/go.mod matches import statements in Go source files

Files:

  • go/nemo_relay/observability_plugin_test.go
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.go

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Go module paths and package paths during coordinated rename operations

**/*.go: Use gofmt for Go code formatting
Run go vet ./... for Go static analysis
Use Go PascalCase naming convention for Go identifiers
Include SPDX license header in all Go source files using double-slash comment syntax
Validate Go code with uv run pre-commit run --all-files to enforce gofmt formatting and go vet static analysis

Files:

  • go/nemo_relay/observability_plugin_test.go
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • go/nemo_relay/observability_plugin_test.go
  • crates/python/src/py_types/mod.rs
  • crates/node/observability.d.ts
  • go/nemo_relay/atof_test.go
  • crates/python/Cargo.toml
  • crates/wasm/wrappers/esm/observability.d.ts
  • crates/node/Cargo.toml
  • crates/ffi/Cargo.toml
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/cli/Cargo.toml
  • python/nemo_relay/__init__.py
  • crates/ffi/tests/integration/api_tests.rs
  • crates/core/Cargo.toml
  • python/nemo_relay/observability.py
  • crates/cli/src/plugins/editor_model.rs
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • crates/cli/src/doctor.rs
  • ATTRIBUTIONS-Rust.md
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • go/nemo_relay/observability_plugin_test.go
  • crates/python/src/py_types/mod.rs
  • crates/node/observability.d.ts
  • go/nemo_relay/atof_test.go
  • crates/wasm/wrappers/esm/observability.d.ts
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • python/nemo_relay/__init__.py
  • crates/ffi/tests/integration/api_tests.rs
  • python/nemo_relay/observability.py
  • crates/cli/src/plugins/editor_model.rs
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • crates/cli/src/doctor.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
go/nemo_relay/**/*

⚙️ CodeRabbit configuration file

go/nemo_relay/**/*: Review Go binding changes for cgo memory ownership, race safety, callback cleanup, idiomatic exported APIs, and parity with Rust/FFI behavior.
Any API change should include focused Go tests and consider race-test behavior.

Files:

  • go/nemo_relay/observability_plugin_test.go
  • go/nemo_relay/atof_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.rs: If any Rust code changed, always run just test-rust
If any Rust code changed, also run cargo fmt --all
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/python/src/py_types/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/tests/integration/api_tests.rs
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • crates/cli/src/doctor.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
**/{Cargo.toml,**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/python/src/py_types/mod.rs
  • crates/python/Cargo.toml
  • crates/node/Cargo.toml
  • crates/ffi/Cargo.toml
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/cli/Cargo.toml
  • crates/ffi/tests/integration/api_tests.rs
  • crates/core/Cargo.toml
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • crates/cli/src/doctor.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{h,hpp,c,cpp,rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/python/src/py_types/mod.rs
  • crates/ffi/nemo_relay.h
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/tests/integration/api_tests.rs
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • crates/cli/src/doctor.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/python/src/py_types/mod.rs
  • crates/python/Cargo.toml
  • crates/node/Cargo.toml
  • crates/ffi/Cargo.toml
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/cli/Cargo.toml
  • crates/ffi/tests/integration/api_tests.rs
  • crates/core/Cargo.toml
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • crates/cli/src/doctor.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
crates/python/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

If the native Rust bridge changed, add the Rust crate tests for nemo-relay-python

Files:

  • crates/python/src/py_types/mod.rs
  • crates/python/src/py_types/observability.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/python/src/py_types/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/tests/integration/api_tests.rs
  • crates/cli/src/plugins/editor_model.rs
  • crates/cli/tests/coverage/doctor_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • crates/cli/src/doctor.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{wasm,js,ts}{,x}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure WebAssembly package naming conventions are consistent with generated package expectations and downstream consumption

Files:

  • crates/node/observability.d.ts
  • crates/wasm/wrappers/esm/observability.d.ts
crates/node/**/*.{js,ts,jsx,tsx,json}

📄 CodeRabbit inference engine (.agents/skills/test-node-binding/SKILL.md)

Format changed Node files with npm run format --workspace=nemo-relay-node

Files:

  • crates/node/observability.d.ts
crates/node/**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.agents/skills/test-node-binding/SKILL.md)

Use npm run check:docstrings --workspace=nemo-relay-node to validate public API docstring checks when surface docs changed

Files:

  • crates/node/observability.d.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run Node.js formatting with npm run format --workspace=nemo-relay-node

Include SPDX license header in all JavaScript and TypeScript source files using double-slash comment syntax

Files:

  • crates/node/observability.d.ts
  • crates/wasm/wrappers/esm/observability.d.ts
crates/node/**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Node.js public entry points include the main runtime package plus nemo-relay-node/typed, nemo-relay-node/plugin, and nemo-relay-node/adaptive.

Files:

  • crates/node/observability.d.ts
**/*.{py,txt,toml,cfg,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update Python package names and top-level module imports during coordinated rename operations

Files:

  • crates/python/Cargo.toml
  • crates/node/Cargo.toml
  • crates/ffi/Cargo.toml
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • crates/cli/Cargo.toml
  • python/nemo_relay/__init__.py
  • crates/core/Cargo.toml
  • python/nemo_relay/observability.py
**/Cargo.toml

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update WebAssembly crate names and generated package names during coordinated rename operations

Confirm or infer the target release version from upstream/main:Cargo.toml. Derive the release branch as release/<major>.<minor>.

Files:

  • crates/python/Cargo.toml
  • crates/node/Cargo.toml
  • crates/ffi/Cargo.toml
  • crates/cli/Cargo.toml
  • crates/core/Cargo.toml
**/*.toml

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license header in TOML configuration files using hash comment syntax

Files:

  • crates/python/Cargo.toml
  • crates/node/Cargo.toml
  • crates/ffi/Cargo.toml
  • crates/cli/Cargo.toml
  • crates/core/Cargo.toml
crates/wasm/{wrappers,tests-js,scripts}/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.agents/skills/test-wasm-binding/SKILL.md)

Format changed WebAssembly JS/TS wrapper files with npm run precommit:format --workspace=nemo-relay-node -- crates/wasm/wrappers crates/wasm/tests-js crates/wasm/scripts

Files:

  • crates/wasm/wrappers/esm/observability.d.ts
crates/wasm/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run WebAssembly formatting with npm run precommit:format --workspace=nemo-relay-node -- crates/wasm/wrappers crates/wasm/tests-js crates/wasm/scripts

Files:

  • crates/wasm/wrappers/esm/observability.d.ts
crates/ffi/**

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Rebuild the FFI crate in release mode so the shared library and header stay in sync when making changes to crates/ffi

Files:

  • crates/ffi/Cargo.toml
  • crates/ffi/nemo_relay.h
  • crates/ffi/tests/integration/api_tests.rs
  • crates/ffi/src/api/observability.rs
**/*.{h,c}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update C header names and symbol prefixes during coordinated rename operations

Files:

  • crates/ffi/nemo_relay.h
crates/ffi/nemo_relay.h

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Check the generated header diff when any exported symbol or type changed in the FFI surface

Ensure FFI header sync for crates/ffi/nemo_relay.h through Cargo/build.rs

Files:

  • crates/ffi/nemo_relay.h
crates/ffi/*.h

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Prefix C FFI exports with nemo_relay_

Files:

  • crates/ffi/nemo_relay.h
**/test_*.{py,py}

📄 CodeRabbit inference engine (.agents/skills/add-integration/SKILL.md)

Relevant integration tests or smoke coverage must exist for the integration path

Files:

  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
{pyproject.toml,**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Maintain consistency between Python package names in pyproject.toml and import paths used throughout the codebase

Files:

  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • python/nemo_relay/__init__.py
  • python/nemo_relay/observability.py
python/**/*test*.py

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

python/**/*test*.py: Do not add @pytest.mark.asyncio to any test in Python test files
Do not add a -> None return type annotation to test functions
When mocking a class, use unittest.mock.MagicMock or unittest.mock.AsyncMock with the spec constructor argument when necessary, rather than defining a new class
Prefix mocked class names with mock, not fake
Prefer pytest fixtures over helper methods in Python tests
Prefer pytest.mark.parametrize over creating individual tests for different input types

Files:

  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
python/**/{conftest.py,*test*.py}

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

When creating a fixture follow the pattern: @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) def <fixture_name>_fixture() -> <return_type>: and only specify the scope argument when the value is something other than "function"

Files:

  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
**/*.py

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*.py: Run Python formatting with uv run ruff format python
Run Python testing with uv run pytest -k "<pattern>"

**/*.py: Use Ruff with rule sets E, F, W, I for Python linting
Use Ruff formatter with line length 120 and double quotes for Python code formatting
Run ty for Python type checking
Use Python snake_case naming convention for Python identifiers
Include SPDX license header in all Python source files using hash comment syntax
Validate Python code with uv run pre-commit run --all-files to enforce Ruff linting and formatting, and ty type checking

Files:

  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • python/nemo_relay/__init__.py
  • python/nemo_relay/observability.py
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/Cargo.toml
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

crates/core/**/*.rs: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths. Keep errors explicit and binding-appropriate at the wrapper layer.

Files:

  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
crates/{core,adaptive}/**

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full matrix across Rust, Python, Go, Node.js, and WebAssembly

Files:

  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/Cargo.toml
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/atof_tests.rs
  • crates/core/src/observability/atof.rs
  • crates/core/src/observability/plugin_component.rs
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use snake_case naming convention for Python identifiers (e.g., nemo_relay.tools.call)

Format changed Python wrapper and test files with uv run ruff format python

Python wrapper modules live under python/nemo_relay/; the native extension is built from crates/python with maturin.

Files:

  • python/nemo_relay/__init__.py
  • python/nemo_relay/observability.py
crates/ffi/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/ffi, also use test-ffi-surface for validation

Files:

  • crates/ffi/tests/integration/api_tests.rs
  • crates/ffi/src/api/observability.rs
crates/ffi/src/api/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

crates/ffi/src/api/**/*.rs: Add or update FFI wrappers in relevant crates/ffi/src/api/*.rs modules, re-export through crates/ffi/src/api/mod.rs, and ensure generated crates/ffi/nemo_relay.h stays correct
Use nemo_relay_ prefix for C FFI function names (e.g., nemo_relay_tool_call)

Files:

  • crates/ffi/src/api/observability.rs
crates/node/src/api/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update Node.js binding in crates/node/src/api/mod.rs for language-native bindings

Files:

  • crates/node/src/api/mod.rs
crates/node/src/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Use camelCase naming convention for Node.js identifiers (e.g., toolCall)

Files:

  • crates/node/src/api/mod.rs
**/*.{md,rst,html,txt}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

**/*.{md,rst,html,txt}: Always spell NVIDIA in all caps. Do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun because the name starts with an 'en' sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company.
Use trademark symbols with product names only when the document type or legal guidance requires them.
Verify official capitalization, spacing, and hyphenation for product names.
Precede NVIDIA product names with NVIDIA on first mention when it is natural and accurate.
Do not rewrite product names for grammar or title-case rules.
Preserve third-party product names according to the owner's spelling.
Include the company name and full model qualifier on first use when it helps identify the model.
Preserve the official capitalization and punctuation of model names.
Use shorter family names only after the full name is established.
Spell out a term on first use and put the acronym in parentheses unless the acronym is widely understood by the intended audience.
Use the acronym on later mentions after it has been defined.
For long documents, reintroduce the full term if readers might lose context.
Form plurals of acronyms with s, not an apostrophe, such as GPUs.
In headings, common acronyms can remain abbreviated. Spell out the term in the first or second sentence of the body.
Common terms such as CPU, GPU, PC, API, and UI usually do not need to be spelled out for developer audiences.

Files:

  • ATTRIBUTIONS-Rust.md
**/*.{md,rst,html}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

Link the first mention of a product name when the destination helps the reader.

Files:

  • ATTRIBUTIONS-Rust.md
**/*.md

📄 CodeRabbit inference engine (.agents/skills/contribute-integration/SKILL.md)

Documentation must be updated if activation or usage changed

**/*.md: Use title case consistently in technical documentation headings
Avoid quotation marks, ampersands, and exclamation marks in headings
Keep product, event, research, and whitepaper names in their official title case
Use title case for table headers
Do not force social-media sentence case into technical docs
Format code elements, commands, parameters, package names, and expressions in monospace
Format directories, file names, and paths in monospace using backticks
Use angle brackets inside monospace for variables inside paths, such as /home/<username>/.login
Format error messages and strings in quotation marks, keeping literal code strings in code formatting when clearer
Format UI buttons, menus, fields, and labels in bold
Use angle brackets between UI labels for menu paths, such as File > Save As
Use italics for new terms on first use, sparingly and only when introducing the term
Use italics for publication titles
Format keyboard shortcuts in plain text, such as Press Ctrl+Alt+Delete
Use owner/repo link text for GitHub repositories, preferring [NVIDIA/NeMo](link) over prose references like 'the GitHub repo'
Introduce every code block with a complete sentence
Do not make a code block complete the grammar of the previous sentence
Do not continue a sentence after a code block
Use syntax highlighting when the format supports it for code blocks
Avoid the word 'snippet' unless the surrounding docs already use it as a term of art
Keep inline method, function, and class references consistent with nearby docs, omitting empty parentheses for prose readability when no call is shown
Use descriptive anchor text that matches the destination title when possible for links
Avoid raw URLs in running text
Avoid generic anchor text such as 'here,' 'this page,' and 'read more'
Include acronyms in link text when a linked term includes an acronym
Do not link long sentences or multiple sentences
Avoid links ...

Files:

  • ATTRIBUTIONS-Rust.md
**/{docs,examples,**/*.md,*.patch,*.diff,.github,*.sh,*.yaml,*.yml}

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update documentation, examples, CI configuration, and patch artifacts when performing rename operations

Files:

  • ATTRIBUTIONS-Rust.md
**/*.{md,rst,txt}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

Spell NVIDIA in all caps. Do not use Nvidia, nvidia, or NV.

Files:

  • ATTRIBUTIONS-Rust.md
**/*.{md,rst}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

**/*.{md,rst}: Format commands, code elements, expressions, package names, file names, and paths as inline code.
Use descriptive link text. Avoid raw URLs and weak anchors such as "here" or "read more."
Use title case consistently for technical documentation headings.
Introduce code blocks, lists, tables, and images with complete sentences.
Write procedures as imperative steps. Keep steps parallel and split long procedures into smaller tasks.
Prefer active voice, present tense, short sentences, contractions, and plain English.
Use can for possibility and reserve may for permission.
Use after for temporal relationships instead of once.
Prefer refer to over see when the wording points readers to another resource.
Avoid culture-specific idioms, unnecessary Latinisms, jokes, and marketing exaggeration in technical docs.
Spell out months in body text, avoid ordinal dates, and use clear time zones.
Spell out whole numbers from zero through nine unless they are technical values, parameters, versions, or UI values.
Use numerals for 10 or greater and include commas in thousands.
Do not add trademark symbols to learning-oriented docs unless the source, platform, or legal guidance explicitly requires them.

Files:

  • ATTRIBUTIONS-Rust.md
🧠 Learnings (1)
📚 Learning: 2026-05-07T18:04:44.387Z
Learnt from: mnajafian-nv
Repo: NVIDIA/NeMo-Flow PR: 67
File: integrations/openclaw/src/modules.ts:1-2
Timestamp: 2026-05-07T18:04:44.387Z
Learning: In NVIDIA/NeMo-Flow, TypeScript source files should use `//` line comments for SPDX headers (e.g., `// SPDX-FileCopyrightText: ...` and `// SPDX-License-Identifier: ...`) rather than C-style block comments (`/* ... */`). The repo’s copyright checker enforces this mapping, so `//` SPDX headers in `.ts` files should not be flagged as a style violation.

Applied to files:

  • crates/node/observability.d.ts
  • crates/wasm/wrappers/esm/observability.d.ts
🪛 markdownlint-cli2 (0.22.1)
ATTRIBUTIONS-Rust.md

[warning] 3127-3127: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 3127-3127: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 7481-7481: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 7484-7484: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 7484-7484: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 7485-7485: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 7485-7485: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 8111-8111: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 8111-8111: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 8735-8735: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 8738-8738: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 8738-8738: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 8739-8739: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 8739-8739: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 8798-8798: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 8798-8798: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 32188-32188: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 32191-32191: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 32191-32191: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 32192-32192: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 32192-32192: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 35715-35715: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 35718-35718: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 35718-35718: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 35719-35719: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 35719-35719: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 37135-37135: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 37138-37138: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 37138-37138: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 37139-37139: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 37139-37139: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 38725-38725: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 38726-38726: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 38726-38726: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 38754-38754: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 38757-38757: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 38757-38757: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 38758-38758: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 38758-38758: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 38785-38785: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 38788-38788: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 38788-38788: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 38789-38789: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 38789-38789: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 38806-38806: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 38809-38809: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 38809-38809: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 38810-38810: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 38810-38810: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 39015-39015: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 39018-39018: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 39018-39018: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 39019-39019: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 39019-39019: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 39599-39599: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 39599-39599: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

Comment thread crates/cli/src/doctor.rs
Comment thread crates/core/src/observability/atof.rs
Comment thread crates/ffi/src/api/observability.rs
Signed-off-by: Will Killian <wkillian@nvidia.com>
Signed-off-by: Will Killian <wkillian@nvidia.com>
Copy link
Copy Markdown
Contributor

@mnajafian-nv mnajafian-nv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Copy Markdown
Collaborator

@Salonijain27 Salonijain27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved from a dependency point of view

@willkill07
Copy link
Copy Markdown
Member Author

/merge

@rapids-bot rapids-bot Bot merged commit 02e4daa into NVIDIA:main Jun 5, 2026
70 of 71 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature a new feature lang:go PR changes/introduces Go code lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code size:XL PR is extra large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants