feat: add streaming ATOF endpoints#232
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds 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. ChangesATOF Streaming Endpoint Implementation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 winAlign endpoint support diagnostics with actual runtime support.
This currently warns/errors on
atof.endpointsfor any wasm config, even whenatof.enabled = false, but native builds withoutatof-streamingnever 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 bystart_endpoint_workers. Gate this onsection.enabledand add the missingnot(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
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (37)
ATTRIBUTIONS-Rust.mdcrates/cli/Cargo.tomlcrates/cli/src/doctor.rscrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/core/Cargo.tomlcrates/core/src/observability/atof.rscrates/core/src/observability/plugin_component.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/Cargo.tomlcrates/ffi/nemo_relay.hcrates/ffi/src/api/observability.rscrates/node/Cargo.tomlcrates/node/observability.d.tscrates/node/src/api/mod.rscrates/node/tests/atof_tests.mjscrates/node/tests/observability_plugin_tests.mjscrates/python/Cargo.tomlcrates/python/src/py_types/mod.rscrates/python/src/py_types/observability.rscrates/wasm/wrappers/esm/observability.d.tsdocs/build-plugins/plugin-configuration-files.mdxdocs/observability-plugin/atof.mdxdocs/observability-plugin/configuration.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxgo/nemo_relay/atof_test.gogo/nemo_relay/nemo_relay.gogo/nemo_relay/observability_plugin.gogo/nemo_relay/observability_plugin_test.gopython/nemo_relay/__init__.pypython/nemo_relay/__init__.pyipython/nemo_relay/_native.pyipython/nemo_relay/observability.pypython/nemo_relay/observability.pyipython/tests/test_observability_plugin.pypython/tests/test_types.py
5eccee2 to
2f9f2bb
Compare
There was a problem hiding this comment.
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 winReport
atof.endpointsas unsupported when this build lacks streaming.On non-
wasm32builds compiled withoutatof-streaming, validation currently emits no diagnostic foratof.endpoints, sovalidate_plugin_configcan pass a config thatregister_atof_exporterlater 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 winDo not register with silently dropped endpoints.
This
filter_maplogsbuild_atof_endpoint_configfailures 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
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (38)
ATTRIBUTIONS-Rust.mdcrates/cli/Cargo.tomlcrates/cli/src/doctor.rscrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/core/Cargo.tomlcrates/core/src/observability/atof.rscrates/core/src/observability/plugin_component.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/Cargo.tomlcrates/ffi/nemo_relay.hcrates/ffi/src/api/observability.rscrates/ffi/tests/integration/api_tests.rscrates/node/Cargo.tomlcrates/node/observability.d.tscrates/node/src/api/mod.rscrates/node/tests/atof_tests.mjscrates/node/tests/observability_plugin_tests.mjscrates/python/Cargo.tomlcrates/python/src/py_types/mod.rscrates/python/src/py_types/observability.rscrates/wasm/wrappers/esm/observability.d.tsdocs/build-plugins/plugin-configuration-files.mdxdocs/observability-plugin/atof.mdxdocs/observability-plugin/configuration.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxgo/nemo_relay/atof_test.gogo/nemo_relay/nemo_relay.gogo/nemo_relay/observability_plugin.gogo/nemo_relay/observability_plugin_test.gopython/nemo_relay/__init__.pypython/nemo_relay/__init__.pyipython/nemo_relay/_native.pyipython/nemo_relay/observability.pypython/nemo_relay/observability.pyipython/tests/test_observability_plugin.pypython/tests/test_types.py
mnajafian-nv
left a comment
There was a problem hiding this comment.
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.
2f9f2bb to
d264902
Compare
There was a problem hiding this comment.
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 winReturn a caller error for
InvalidEndpoint.The new JSON constructor can now hit
AtofExporterError::InvalidEndpoint, butstatus_from_atof_errormaps every non-runtime ATOF error toInternal. Bad endpoint config from the caller will therefore surface as a library fault over FFI. MapInvalidEndpointtoInvalidArgso 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 winValidate unknown keys inside
atof.endpointsentries.Endpoint validation only checks a few known values after deserialization. Since these structs do not reject unknown fields, typos like
timeout_msorheaderinside 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
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (38)
ATTRIBUTIONS-Rust.mdcrates/cli/Cargo.tomlcrates/cli/src/doctor.rscrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/core/Cargo.tomlcrates/core/src/observability/atof.rscrates/core/src/observability/plugin_component.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/Cargo.tomlcrates/ffi/nemo_relay.hcrates/ffi/src/api/observability.rscrates/ffi/tests/integration/api_tests.rscrates/node/Cargo.tomlcrates/node/observability.d.tscrates/node/src/api/mod.rscrates/node/tests/atof_tests.mjscrates/node/tests/observability_plugin_tests.mjscrates/python/Cargo.tomlcrates/python/src/py_types/mod.rscrates/python/src/py_types/observability.rscrates/wasm/wrappers/esm/observability.d.tsdocs/build-plugins/plugin-configuration-files.mdxdocs/observability-plugin/atof.mdxdocs/observability-plugin/configuration.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxgo/nemo_relay/atof_test.gogo/nemo_relay/nemo_relay.gogo/nemo_relay/observability_plugin.gogo/nemo_relay/observability_plugin_test.gopython/nemo_relay/__init__.pypython/nemo_relay/__init__.pyipython/nemo_relay/_native.pyipython/nemo_relay/observability.pypython/nemo_relay/observability.pyipython/tests/test_observability_plugin.pypython/tests/test_types.py
Signed-off-by: Will Killian <wkillian@nvidia.com>
d264902 to
977e8d7
Compare
Signed-off-by: Will Killian <2007799+willkill07@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
crates/core/src/observability/atof.rs (2)
648-657:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftDo not acknowledge WebSocket
FlushorCloseafter a timed-out drain.
drain_websocket_pendingalready reports whether the timeout path leftpendingnon-empty, but both branches ignore that result and send the ack anyway. That letsforce_flush()/shutdown()report success with unsent events still buffered, and theClosebranch 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 liftReplace 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
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (38)
ATTRIBUTIONS-Rust.mdcrates/cli/Cargo.tomlcrates/cli/src/doctor.rscrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/core/Cargo.tomlcrates/core/src/observability/atof.rscrates/core/src/observability/plugin_component.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/Cargo.tomlcrates/ffi/nemo_relay.hcrates/ffi/src/api/observability.rscrates/ffi/tests/integration/api_tests.rscrates/node/Cargo.tomlcrates/node/observability.d.tscrates/node/src/api/mod.rscrates/node/tests/atof_tests.mjscrates/node/tests/observability_plugin_tests.mjscrates/python/Cargo.tomlcrates/python/src/py_types/mod.rscrates/python/src/py_types/observability.rscrates/wasm/wrappers/esm/observability.d.tsdocs/build-plugins/plugin-configuration-files.mdxdocs/observability-plugin/atof.mdxdocs/observability-plugin/configuration.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxgo/nemo_relay/atof_test.gogo/nemo_relay/nemo_relay.gogo/nemo_relay/observability_plugin.gogo/nemo_relay/observability_plugin_test.gopython/nemo_relay/__init__.pypython/nemo_relay/__init__.pyipython/nemo_relay/_native.pyipython/nemo_relay/observability.pypython/nemo_relay/observability.pyipython/tests/test_observability_plugin.pypython/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.mjscrates/node/tests/observability_plugin_tests.mjscrates/node/observability.d.tscrates/wasm/wrappers/esm/observability.d.tscrates/node/Cargo.tomlcrates/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.mjscrates/node/tests/observability_plugin_tests.mjsgo/nemo_relay/observability_plugin_test.gocrates/node/observability.d.tsgo/nemo_relay/atof_test.gocrates/wasm/wrappers/esm/observability.d.tscrates/node/Cargo.tomlgo/nemo_relay/observability_plugin.gogo/nemo_relay/nemo_relay.gocrates/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.mjscrates/node/tests/observability_plugin_tests.mjscrates/python/src/py_types/mod.rscrates/node/observability.d.tscrates/python/Cargo.tomlcrates/wasm/wrappers/esm/observability.d.tscrates/node/Cargo.tomlcrates/ffi/Cargo.tomlcrates/ffi/nemo_relay.hcrates/ffi/tests/integration/api_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/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.mjscrates/node/tests/observability_plugin_tests.mjsgo/nemo_relay/observability_plugin_test.gogo/nemo_relay/atof_test.gopython/tests/test_observability_plugin.pypython/tests/test_types.pycrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/tests/integration/api_tests.rscrates/cli/tests/coverage/doctor_tests.rscrates/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:
- Scope stacks decide where work belongs and which scope-local behavior is visible.
- Middleware registries decide what guardrails and intercepts run around managed calls.
- Plugins install reusable runtime behavior from configuration.
- Events record runtime behavior in ATOF form.
- 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.mjscrates/node/tests/observability_plugin_tests.mjsdocs/build-plugins/plugin-configuration-files.mdxpython/nemo_relay/__init__.pyigo/nemo_relay/observability_plugin_test.gocrates/python/src/py_types/mod.rscrates/node/observability.d.tsgo/nemo_relay/atof_test.gocrates/python/Cargo.tomlcrates/wasm/wrappers/esm/observability.d.tscrates/node/Cargo.tomldocs/resources/troubleshooting/trace-incident-runbook.mdxcrates/ffi/Cargo.tomlcrates/ffi/nemo_relay.hpython/tests/test_observability_plugin.pypython/tests/test_types.pycrates/core/tests/unit/observability/plugin_component_tests.rscrates/cli/Cargo.tomlpython/nemo_relay/__init__.pycrates/ffi/tests/integration/api_tests.rspython/nemo_relay/observability.pyidocs/observability-plugin/configuration.mdxcrates/core/Cargo.tomlpython/nemo_relay/_native.pyipython/nemo_relay/observability.pycrates/cli/src/plugins/editor_model.rsgo/nemo_relay/observability_plugin.gogo/nemo_relay/nemo_relay.gocrates/cli/tests/coverage/doctor_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/node/src/api/mod.rsdocs/observability-plugin/atof.mdxcrates/cli/src/doctor.rsATTRIBUTIONS-Rust.mdcrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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. Usejust docsfor docs-site builds andjust docs-linkcheckwhen links changed
Run docs site build withjust docs
Files:
docs/build-plugins/plugin-configuration-files.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/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-linkcheckwhen links change
Files:
docs/build-plugins/plugin-configuration-files.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/observability-plugin/atof.mdxATTRIBUTIONS-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.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/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.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/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.mdxcrates/python/Cargo.tomlcrates/node/Cargo.tomldocs/resources/troubleshooting/trace-incident-runbook.mdxcrates/ffi/Cargo.tomlcrates/cli/Cargo.tomldocs/observability-plugin/configuration.mdxcrates/core/Cargo.tomldocs/observability-plugin/atof.mdxATTRIBUTIONS-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.mdxcrates/python/Cargo.tomlcrates/node/Cargo.tomldocs/resources/troubleshooting/trace-incident-runbook.mdxcrates/ffi/Cargo.tomlpython/tests/test_observability_plugin.pypython/tests/test_types.pycrates/cli/Cargo.tomlpython/nemo_relay/__init__.pydocs/observability-plugin/configuration.mdxcrates/core/Cargo.tomlpython/nemo_relay/observability.pydocs/observability-plugin/atof.mdxATTRIBUTIONS-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.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/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.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/observability-plugin/atof.mdxATTRIBUTIONS-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.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/observability-plugin/atof.mdx
docs/**
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Run
just docsor./scripts/build-docs.sh htmlto regenerate ignored Fern API reference pages before validation for documentation site changes
Files:
docs/build-plugins/plugin-configuration-files.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/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.mdxdocs/resources/troubleshooting/trace-incident-runbook.mdxdocs/observability-plugin/configuration.mdxdocs/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.rswith Python wrapper docstring inpython/nemo_relay/<module>.pyand type stubs inpython/nemo_relay/*.pyimodules
Files:
python/nemo_relay/__init__.pyipython/nemo_relay/__init__.pypython/nemo_relay/observability.pyipython/nemo_relay/_native.pyipython/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__.pyipython/nemo_relay/__init__.pypython/nemo_relay/observability.pyipython/nemo_relay/_native.pyipython/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.gowith doc comment and shorthand package if the capability belongs there
go/nemo_relay/**/*.go: Format changed Go packages withcd go/nemo_relay && go fmt ./...
Run Go tests withjust test-goto build and test the NeMo Relay Go binding
Usejust build-gowhen you want an explicit build-only pass or need the artifact for other work
Usejust ci=true test-gowhen you need the CI-style coverage and JUnit path
On macOS, setDYLD_LIBRARY_PATHto the../../target/releasedirectory before running the rawgo testcommand directly
Files:
go/nemo_relay/observability_plugin_test.gogo/nemo_relay/atof_test.gogo/nemo_relay/observability_plugin.gogo/nemo_relay/nemo_relay.go
go/**/*.go
📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)
Use
PascalCasenaming 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.gogo/nemo_relay/atof_test.gogo/nemo_relay/observability_plugin.gogo/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.gogo/nemo_relay/atof_test.gopython/tests/test_observability_plugin.pypython/tests/test_types.pycrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/tests/integration/api_tests.rscrates/cli/tests/coverage/doctor_tests.rscrates/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.modmatches import statements in Go source files
Files:
go/nemo_relay/observability_plugin_test.gogo/nemo_relay/atof_test.gogo/nemo_relay/observability_plugin.gogo/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: Usegofmtfor Go code formatting
Rungo 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 withuv run pre-commit run --all-filesto enforce gofmt formatting and go vet static analysis
Files:
go/nemo_relay/observability_plugin_test.gogo/nemo_relay/atof_test.gogo/nemo_relay/observability_plugin.gogo/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.gocrates/python/src/py_types/mod.rscrates/node/observability.d.tsgo/nemo_relay/atof_test.gocrates/python/Cargo.tomlcrates/wasm/wrappers/esm/observability.d.tscrates/node/Cargo.tomlcrates/ffi/Cargo.tomlpython/tests/test_observability_plugin.pypython/tests/test_types.pycrates/core/tests/unit/observability/plugin_component_tests.rscrates/cli/Cargo.tomlpython/nemo_relay/__init__.pycrates/ffi/tests/integration/api_tests.rscrates/core/Cargo.tomlpython/nemo_relay/observability.pycrates/cli/src/plugins/editor_model.rsgo/nemo_relay/observability_plugin.gogo/nemo_relay/nemo_relay.gocrates/cli/tests/coverage/doctor_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/node/src/api/mod.rscrates/cli/src/doctor.rsATTRIBUTIONS-Rust.mdcrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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 prefixednemo_relay_, Go usesPascalCasefor public APIs, Node.js usescamelCase.
Files:
go/nemo_relay/observability_plugin_test.gocrates/python/src/py_types/mod.rscrates/node/observability.d.tsgo/nemo_relay/atof_test.gocrates/wasm/wrappers/esm/observability.d.tspython/tests/test_observability_plugin.pypython/tests/test_types.pycrates/core/tests/unit/observability/plugin_component_tests.rspython/nemo_relay/__init__.pycrates/ffi/tests/integration/api_tests.rspython/nemo_relay/observability.pycrates/cli/src/plugins/editor_model.rsgo/nemo_relay/observability_plugin.gogo/nemo_relay/nemo_relay.gocrates/cli/tests/coverage/doctor_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/node/src/api/mod.rscrates/cli/src/doctor.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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.gogo/nemo_relay/atof_test.gogo/nemo_relay/observability_plugin.gogo/nemo_relay/nemo_relay.go
**/*.rs
📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)
Use
snake_casenaming convention for Rust identifiers (e.g.,nemo_relay_tool_call)
**/*.rs: Any Rust change must runjust test-rust
Any Rust change must runcargo fmt --all
Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allfor all FFI work since it is Rust work
Runjust test-rustto validate FFI changes
Runcargo clippy --workspace --all-targets -- -D warningsto enforce strict linting on FFI workWhen Rust files changed as part of Go work, also run
cargo fmt --all,just test-rust, andcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allwhen Rust files are changed as part of Node work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files are changed as part of Node work
Runjust test-rustwhen Rust files are changed as part of Node work
**/*.rs: Runcargo fmt --allto format all Rust code
Runcargo clippy --workspace --all-targets -- -D warningsto enforce all clippy lints as errors
**/*.rs: Runcargo fmt --allwhen Rust files changed as part of WebAssembly work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files changed as part of WebAssembly work
**/*.rs: If any Rust code changed, always runjust test-rust
If any Rust code changed, also runcargo fmt --all
If any Rust code changed, also runcargo clippy --workspace --all-targets -- -D warnings
Run Rust formatting withcargo fmt --all
Run Rust linting withcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Usecargo fmtfor Rust code formatting
Runcargo clippy -- -D warningsto 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 withuv run pre-commit run --all-filesto enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...
Files:
crates/python/src/py_types/mod.rscrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/tests/integration/api_tests.rscrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/node/src/api/mod.rscrates/cli/src/doctor.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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.tomland their actual usage across the codebase
Files:
crates/python/src/py_types/mod.rscrates/python/Cargo.tomlcrates/node/Cargo.tomlcrates/ffi/Cargo.tomlcrates/core/tests/unit/observability/plugin_component_tests.rscrates/cli/Cargo.tomlcrates/ffi/tests/integration/api_tests.rscrates/core/Cargo.tomlcrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/node/src/api/mod.rscrates/cli/src/doctor.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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.rscrates/ffi/nemo_relay.hcrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/tests/integration/api_tests.rscrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/node/src/api/mod.rscrates/cli/src/doctor.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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.rscrates/python/Cargo.tomlcrates/node/Cargo.tomlcrates/ffi/Cargo.tomlcrates/core/tests/unit/observability/plugin_component_tests.rscrates/cli/Cargo.tomlcrates/ffi/tests/integration/api_tests.rscrates/core/Cargo.tomlcrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/node/src/api/mod.rscrates/cli/src/doctor.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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.rscrates/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.
UseJson = serde_json::Valuein Rust-facing runtime APIs for JSON payload handling.
Files:
crates/python/src/py_types/mod.rscrates/core/tests/unit/observability/plugin_component_tests.rscrates/ffi/tests/integration/api_tests.rscrates/cli/src/plugins/editor_model.rscrates/cli/tests/coverage/doctor_tests.rscrates/ffi/src/api/observability.rscrates/python/src/py_types/observability.rscrates/node/src/api/mod.rscrates/cli/src/doctor.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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.tscrates/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-nodeto 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-nodeInclude SPDX license header in all JavaScript and TypeScript source files using double-slash comment syntax
Files:
crates/node/observability.d.tscrates/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, andnemo-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.tomlcrates/node/Cargo.tomlcrates/ffi/Cargo.tomlpython/tests/test_observability_plugin.pypython/tests/test_types.pycrates/cli/Cargo.tomlpython/nemo_relay/__init__.pycrates/core/Cargo.tomlpython/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 asrelease/<major>.<minor>.
Files:
crates/python/Cargo.tomlcrates/node/Cargo.tomlcrates/ffi/Cargo.tomlcrates/cli/Cargo.tomlcrates/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.tomlcrates/node/Cargo.tomlcrates/ffi/Cargo.tomlcrates/cli/Cargo.tomlcrates/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.tomlcrates/ffi/nemo_relay.hcrates/ffi/tests/integration/api_tests.rscrates/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.hthrough 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.pypython/tests/test_types.py
{pyproject.toml,**/*.py}
📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)
Maintain consistency between Python package names in
pyproject.tomland import paths used throughout the codebase
Files:
python/tests/test_observability_plugin.pypython/tests/test_types.pypython/nemo_relay/__init__.pypython/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.asyncioto any test in Python test files
Do not add a-> Nonereturn type annotation to test functions
When mocking a class, useunittest.mock.MagicMockorunittest.mock.AsyncMockwith thespecconstructor argument when necessary, rather than defining a new class
Prefix mocked class names withmock, notfake
Prefer pytest fixtures over helper methods in Python tests
Preferpytest.mark.parametrizeover creating individual tests for different input types
Files:
python/tests/test_observability_plugin.pypython/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.pypython/tests/test_types.py
**/*.py
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
**/*.py: Run Python formatting withuv run ruff format python
Run Python testing withuv 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
Runtyfor 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 withuv run pre-commit run --all-filesto enforce Ruff linting and formatting, and ty type checking
Files:
python/tests/test_observability_plugin.pypython/tests/test_types.pypython/nemo_relay/__init__.pypython/nemo_relay/observability.py
{crates/core,crates/adaptive}/**/*
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
Changes to
crates/coreorcrates/adaptivemust run the full language matrix
Files:
crates/core/tests/unit/observability/plugin_component_tests.rscrates/core/Cargo.tomlcrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/core/src/observability/plugin_component.rs
crates/core/**/*.rs
📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)
If the change touched
crates/coreor shared runtime semantics, also usevalidate-changefor broader validation
crates/core/**/*.rs: UseJson = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads.
UseResult<T>withFlowErrorin core runtime paths. Keep errors explicit and binding-appropriate at the wrapper layer.
Files:
crates/core/tests/unit/observability/plugin_component_tests.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/core/src/observability/plugin_component.rs
crates/{core,adaptive}/**
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
If
crates/coreorcrates/adaptivechanged, run the full matrix across Rust, Python, Go, Node.js, and WebAssembly
Files:
crates/core/tests/unit/observability/plugin_component_tests.rscrates/core/Cargo.tomlcrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/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.rscrates/core/tests/unit/observability/atof_tests.rscrates/core/src/observability/atof.rscrates/core/src/observability/plugin_component.rs
python/nemo_relay/**/*.py
📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)
Use
snake_casenaming convention for Python identifiers (e.g.,nemo_relay.tools.call)Format changed Python wrapper and test files with
uv run ruff format pythonPython wrapper modules live under
python/nemo_relay/; the native extension is built fromcrates/pythonwithmaturin.
Files:
python/nemo_relay/__init__.pypython/nemo_relay/observability.py
crates/ffi/**/*.rs
📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)
If the change touched
crates/ffi, also usetest-ffi-surfacefor validation
Files:
crates/ffi/tests/integration/api_tests.rscrates/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 relevantcrates/ffi/src/api/*.rsmodules, re-export throughcrates/ffi/src/api/mod.rs, and ensure generatedcrates/ffi/nemo_relay.hstays correct
Usenemo_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.rsfor 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
camelCasenaming 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 spellNVIDIAin all caps. Do not useNvidia,nvidia,nVidia,nVIDIA, orNV.
Usean NVIDIAbefore a noun because the name starts with an 'en' sound.
Do not add a registered trademark symbol afterNVIDIAwhen 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 withNVIDIAon 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 withs, not an apostrophe, such asGPUs.
In headings, common acronyms can remain abbreviated. Spell out the term in the first or second sentence of the body.
Common terms such asCPU,GPU,PC,API, andUIusually 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
NVIDIAin all caps. Do not useNvidia,nvidia, orNV.
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.
Usecanfor possibility and reservemayfor permission.
Useafterfor temporal relationships instead ofonce.
Preferrefer tooverseewhen 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.tscrates/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)
Signed-off-by: Will Killian <wkillian@nvidia.com>
Signed-off-by: Will Killian <wkillian@nvidia.com>
Salonijain27
left a comment
There was a problem hiding this comment.
Approved from a dependency point of view
|
/merge |
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 doctorprobes configured endpoints with a synthetic ATOF mark event.Details
http_post,websocket, andndjson.Where should the reviewer start?
Start with
crates/core/src/observability/atof.rsfor exporter lifecycle and transport behavior, thencrates/core/src/observability/plugin_component.rsandcrates/cli/src/doctor.rsfor config validation and doctor probing.Validation run:
cargo test -p nemo-relay observability::atof --libcargo test -p nemo-relay observability::plugin_component --libcargo test -p nemo-relay-cli collect_observability_probes_atof_streaming_endpointcargo test -p nemo-relay-cli --test cli_testsjust build-pythonjust build-nodejust build-gouv run pytest python/tests/test_observability_plugin.py python/tests/test_types.py -qnode --test tests/atof_tests.mjs tests/observability_plugin_tests.mjsgo test -v -run 'Test(NewAtofExporterConfigDefaults|AtofExporterLifecycleWritesRawJSONL|AtofExporterAppendAndOverwriteModes|ObservabilityConfigHelpers|ObservabilityPluginAtofAndAtifFiles|ObservabilityPluginValidationRejectsBadValues)' ./...cargo check -p nemo-relay --no-default-featuresjust docscargo 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
Tests
Documentation
Chores