Skip to content

feat(observability)!: project typed OTLP attributes#414

Merged
rapids-bot[bot] merged 10 commits into
NVIDIA:mainfrom
willkill07:wkk_typed-otlp-attribute-projection
Jul 14, 2026
Merged

feat(observability)!: project typed OTLP attributes#414
rapids-bot[bot] merged 10 commits into
NVIDIA:mainfrom
willkill07:wkk_typed-otlp-attribute-projection

Conversation

@willkill07

@willkill07 willkill07 commented Jul 14, 2026

Copy link
Copy Markdown
Member

Warning

BREAKING CHANGE: [Observability schema] OpenTelemetry and OpenInference exporters no longer emit raw *_json event-payload attributes. They now project top-level fields as typed OTLP attributes (nested values remain JSON strings). Update trace queries and dashboards to the new attribute paths; use attribute_mappings where a backend needs a compatibility alias.

Overview

Project OpenTelemetry and OpenInference event payloads into typed OTLP attributes, with optional backend-specific aliases.

  • 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

  • Added OtlpAttributeMapping and mapping validation to the Rust core and observability plugin configuration.
  • Projected top-level event, handle, mark, input, output, metadata, and category-profile values as typed OTLP attributes across OpenTelemetry and OpenInference.
  • Made attribute_mappings an interactive typed list in the built-in configuration editor, with editable key and alias fields.
  • Added mapping support and coverage for the FFI, Node, Python, and Go bindings and regenerated the C header.

Where should the reviewer start?

Start with crates/core/src/observability/mod.rs, then review the OpenTelemetry and OpenInference exporters, the configuration component editor schema, and their parity tests.

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

Closes RELAY-472

Summary by CodeRabbit

  • New Features

    • Added configurable OTLP attribute mappings for OpenTelemetry and OpenInference, with typed projection from top-level JSON fields and alias-based copying across span and mark attributes.
    • Exposed attribute_mappings in Python, Node.js, Go, and C APIs, including new subscriber constructors, plus editor/UI support.
  • Bug Fixes

    • Mapping validation now rejects blank entries and duplicate aliases, and alias mappings no longer overwrite intrinsic/projection fields.
    • Updated exported attribute keys to the new typed scheme (removed legacy *_json attributes).
  • Tests

    • Updated/expanded observability tests for the new typed attribute key layout and mapping semantics.

@willkill07
willkill07 requested review from a team and lvojtku as code owners July 14, 2026 00:51
@willkill07 willkill07 added the breaking PR introduces a breaking change label Jul 14, 2026 — with ChatGPT Codex Connector
@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 lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code labels Jul 14, 2026
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: c9342f0c-f77e-4814-b876-349a4f69e914

📥 Commits

Reviewing files that changed from the base of the PR and between e24f692 and 15e1656.

📒 Files selected for processing (1)
  • crates/core/tests/unit/observability/attribute_projection_tests.rs
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (12)
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
{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/attribute_projection_tests.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

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
crates/{core,adaptive}/**/*

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

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

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.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/attribute_projection_tests.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/core/tests/unit/observability/attribute_projection_tests.rs
🔇 Additional comments (1)
crates/core/tests/unit/observability/attribute_projection_tests.rs (1)

61-67: LGTM! The typed opentelemetry::Value::Bool(true) assertion correctly addresses the earlier feedback that to_string() couldn't distinguish Value::Bool from Value::String.


Walkthrough

Adds typed OTLP attribute projection and configurable aliases across OpenTelemetry and OpenInference. Validation, plugin configuration, FFI, Node, Python, and Go bindings are updated, while tests adopt flattened typed attribute keys.

Changes

OTLP attribute mapping

Layer / File(s) Summary
Projection and mapping contracts
crates/core/src/observability/mod.rs, crates/core/tests/unit/observability/attribute_projection_tests.rs
Defines mapping types, validates keys and aliases, projects top-level JSON fields, and propagates aliases.
Exporter processing
crates/core/src/observability/openinference.rs, crates/core/src/observability/otel.rs
Stores projected attributes, merges span lifecycle data, applies mappings to marks, and emits flattened OTLP attributes.
Plugin configuration and validation
crates/core/src/observability/plugin_component.rs, crates/cli/tests/coverage/*, crates/core/tests/unit/observability/plugin_component_tests.rs
Adds mapping configuration, editor schema fields, exporter wiring, defaults, and validation diagnostics.
Behavior validation
crates/core/tests/integration/*, crates/core/tests/unit/observability/*, scripts/test-support/otel_test_utils.mjs
Updates flattened attribute assertions and tests alias precedence, orphan marks, parsing, invalid mappings, and encoded OTLP values.

Language and FFI integrations

Layer / File(s) Summary
C and Go APIs
crates/ffi/*, go/nemo_relay/*
Adds mapping-aware constructors, JSON parsing, Go configuration fields, CGo forwarding, and lifecycle coverage.
Node API
crates/node/*
Adds mapping types, configuration fields, validation, forwarding, and OTLP payload tests.
Python API
crates/python/*, python/nemo_relay/*, python/tests/*
Adds mapping properties, conversion, validation, serialization, stubs, and export tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Config as Exporter configuration
  participant Subscriber as OTLP subscriber
  participant Processor as OtelEventProcessor
  participant Exporter as OTLP exporter
  Config->>Subscriber: validate and pass attribute_mappings
  Subscriber->>Processor: store mappings
  Processor->>Processor: project attributes and apply aliases
  Processor->>Exporter: emit typed OTLP attributes
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the breaking observability change.
Description check ✅ Passed The description includes the required Overview, Details, reviewer start point, and related issue sections, with the checklist items filled.
Docstring Coverage ✅ Passed Docstring coverage is 85.63% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@willkill07 willkill07 self-assigned this Jul 14, 2026
@willkill07 willkill07 added this to the 0.6 milestone Jul 14, 2026
@willkill07
willkill07 force-pushed the wkk_typed-otlp-attribute-projection branch from 0620b48 to 328a0fa Compare July 14, 2026 00:56
@github-actions

Copy link
Copy Markdown

@willkill07
willkill07 removed the request for review from lvojtku July 14, 2026 00:57
@willkill07
willkill07 force-pushed the wkk_typed-otlp-attribute-projection branch from 328a0fa to 7fadff2 Compare July 14, 2026 01:05
@NVIDIA NVIDIA deleted a comment from copy-pr-bot Bot Jul 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 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/core/tests/unit/observability/exporter_parity_tests.rs`:
- Around line 697-710: Remove the stale
`otel.get("nemo_relay.start.input_json")` deserialization and equality assertion
from the OTel parity test. Keep validating the projected
`nemo_relay.start.input.headers` attribute and the existing end-output keys in
the test.

In `@crates/ffi/src/api/observability.rs`:
- Around line 533-551: Update parse_attribute_mappings_json so syntactically
valid JSON that fails deserialization due to an invalid mapping shape, including
{} null or a non-string key, returns NemoRelayStatus::InvalidArg while
preserving InvalidJson for malformed JSON syntax. Keep the existing error
reporting and validation behavior consistent with parse_string_map_json.

In `@crates/ffi/tests/unit/api/plugin_tests.rs`:
- Around line 1302-1381: Extend the positive mapping coverage so configured
aliases are observed in exported spans: in
crates/ffi/tests/unit/api/plugin_tests.rs lines 1302-1381, emit an event through
each subscriber created by
test_ffi_typed_attribute_mapping_constructors_validate_and_accept_mappings and
assert tenant.id contains the expected typed value; in
crates/node/tests/openinference_tests.mjs line 35 and
crates/node/tests/otel_tests.mjs line 31, add runtime/exporter assertions for
the respective aliases; in go/nemo_relay/openinference_test.go lines 55-58 and
go/nemo_relay/otel_test.go lines 48-51, add focused projection assertions for
tenant.id. Preserve the existing constructor validation assertions.

In `@crates/node/tests/observability_plugin_tests.mjs`:
- Line 34: Update the Node-facing configuration around attribute_mappings to
expose the camelCase option attributeMappings, and translate it to Rust’s
internal attribute_mappings field before constructing the plugin configuration.
Remove or avoid exposing the snake_case key in the JavaScript API while
preserving the existing empty-mapping behavior.

In `@docs/configure-plugins/observability/opentelemetry.mdx`:
- Around line 108-112: The OpenTelemetry `attribute_mappings` example is missing
its required configuration scope. Place it inside the
`[components.config.opentelemetry]` table, or provide the complete table header
with the snippet, while keeping the alias mapping unchanged.

In `@go/nemo_relay/nemo_relay.go`:
- Around line 1840-1846: Add focused tests for both the OpenTelemetry and
OpenInference mapping paths using AttributeMappings and OtlpAttributeMapping.
Assert that a valid mapping projects the source attribute to the alias while
preserving its correct typed value, and that invalid mappings are rejected.
Verify these behaviors through the Go API/FFI path so an ignored
AttributeMappings argument cannot pass unnoticed.
🪄 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: 5cde0522-c538-446e-b972-cdfc94cc4cc6

📥 Commits

Reviewing files that changed from the base of the PR and between b329343 and 0620b48.

📒 Files selected for processing (30)
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/node/src/api/mod.rs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/openinference_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/python/src/py_types/observability.rs
  • docs/configure-plugins/observability/openinference.mdx
  • docs/configure-plugins/observability/opentelemetry.mdx
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • 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
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (39)
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • python/tests/test_observability_plugin.py
  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • python/nemo_relay/observability.py
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • python/tests/test_types.py
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • python/tests/test_observability_plugin.py
  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • python/nemo_relay/observability.py
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • python/tests/test_types.py
  • crates/node/tests/openinference_tests.mjs
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/tests/test_observability_plugin.py
  • python/nemo_relay/observability.py
  • python/tests/test_types.py
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • python/tests/test_observability_plugin.py
  • go/nemo_relay/observability_plugin.go
  • crates/ffi/nemo_relay.h
  • go/nemo_relay/otel_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • python/nemo_relay/observability.py
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • python/tests/test_types.py
  • go/nemo_relay/openinference_test.go
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • python/tests/test_observability_plugin.py
  • python/nemo_relay/observability.py
  • python/tests/test_types.py
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/src/observability/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{py,go,js,ts}

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

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • python/tests/test_observability_plugin.py
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/otel_test.go
  • python/nemo_relay/observability.py
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • python/tests/test_types.py
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/nemo_relay.go
python/tests/**/*.py

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

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

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

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • python/tests/test_observability_plugin.py
  • go/nemo_relay/observability_plugin.go
  • crates/ffi/nemo_relay.h
  • go/nemo_relay/otel_test.go
  • python/nemo_relay/observability.pyi
  • docs/configure-plugins/observability/opentelemetry.mdx
  • docs/configure-plugins/observability/openinference.mdx
  • python/nemo_relay/_native.pyi
  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • python/nemo_relay/observability.py
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • python/tests/test_types.py
  • crates/node/tests/openinference_tests.mjs
  • go/nemo_relay/openinference_test.go
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • python/tests/test_observability_plugin.py
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/otel_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • python/nemo_relay/observability.py
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • python/tests/test_types.py
  • go/nemo_relay/openinference_test.go
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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:

  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • python/tests/test_types.py
  • crates/node/tests/openinference_tests.mjs
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
go/nemo_relay/**/*.go

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

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

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/nemo_relay.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/nemo_relay.go
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/otel_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • go/nemo_relay/openinference_test.go
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

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

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/otel_test.go
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
  • go/nemo_relay/openinference_test.go
  • crates/node/src/api/mod.rs
  • go/nemo_relay/nemo_relay.go
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

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

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/otel_test.go
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/nemo_relay.go
go/nemo_relay/**

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

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/nemo_relay.go
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.go
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/nemo_relay.go
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/nemo_relay.h
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
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

Update generated or generated-from-build surfaces such as crates/ffi/nemo_relay.h through the proper build step.

Files:

  • crates/ffi/nemo_relay.h
{crates/ffi/src/api/*.rs,crates/ffi/nemo_relay.h}

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

Add or update the shared C/FFI surface in the relevant crates/ffi/src/api/*.rs module, re-export it through crates/ffi/src/api/mod.rs, and keep the generated crates/ffi/nemo_relay.h header correct.

Files:

  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: 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/ffi/nemo_relay.h
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/node/tests/openinference_tests.mjs
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
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/observability.pyi
  • python/nemo_relay/_native.pyi
  • python/nemo_relay/observability.py
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

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

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

Files:

  • docs/configure-plugins/observability/opentelemetry.mdx
  • docs/configure-plugins/observability/openinference.mdx
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Update README.md, fern/, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change.

**/*.{md,mdx}: Prefer the documented public API, not internal shortcuts
Keep package names, repo references, and build commands current
Keep release-process and release-notes guidance in repo-maintainer docs such as RELEASING.md, not as user-facing docs pages or CHANGELOG.md
Keep stable user-facing wrappers at scripts/ root in docs and examples; only point at namespaced helper paths when documenting internal maintenance work
When detailed dynamic plugin guides exist, keep Rust native plugin examples, Python worker plugin examples, and grpc-v1 protocol details on separate pages

If links in documentation change, run just docs-linkcheck.

Files:

  • docs/configure-plugins/observability/opentelemetry.mdx
  • docs/configure-plugins/observability/openinference.mdx
**/*.{md,markdown,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Markdown/MDX documentation files using the HTML comment block form.

Files:

  • docs/configure-plugins/observability/opentelemetry.mdx
  • docs/configure-plugins/observability/openinference.mdx
{docs,examples}/**/*

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

Update docs and examples.

Files:

  • docs/configure-plugins/observability/opentelemetry.mdx
  • docs/configure-plugins/observability/openinference.mdx
docs/**/*

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

If documentation examples or commands under docs/ change, run the targeted docs checks appropriate to the change.

Files:

  • docs/configure-plugins/observability/opentelemetry.mdx
  • docs/configure-plugins/observability/openinference.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/configure-plugins/observability/opentelemetry.mdx
  • docs/configure-plugins/observability/openinference.mdx
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{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/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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

Files:

  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
crates/{core,adaptive}/**/*

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

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

Files:

  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
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

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • python/nemo_relay/observability.py
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/node/tests/openinference_tests.mjs
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/observability.js
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
crates/core/src/observability/{atif,otel,openinference}.rs

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

When changing the core event model, emitted fields, exporter behavior, subscriber config, or lifecycle, keep the ATIF, OpenTelemetry, and OpenInference implementations in sync.

Files:

  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
🧠 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
🔇 Additional comments (34)
docs/configure-plugins/observability/openinference.mdx (1)

66-66: LGTM!

Also applies to: 116-127

docs/configure-plugins/observability/opentelemetry.mdx (1)

60-60: LGTM!

crates/core/src/observability/plugin_component.rs (2)

54-56: LGTM!

Also applies to: 387-389, 426-426, 508-508


1378-1379: 🗄️ Data Integrity & Integration

No mapping-validation gap here validate_otlp_values already rejects invalid attribute_mappings before both builders consume them; unsupported_value only affects diagnostic severity.

			> Likely an incorrect or invalid review comment.
crates/node/observability.d.ts (2)

73-77: LGTM!


61-61: 📐 Maintainability & Code Quality

Keep observability config in snake_case. This module is a schema-shaped helper, and its public API already uses attribute_mappings; camelCase is used on the subscriber constructors instead.

			> Likely an incorrect or invalid review comment.
crates/node/src/api/mod.rs (1)

112-127: LGTM!

Also applies to: 187-187, 297-297, 3474-3480

go/nemo_relay/nemo_relay.go (1)

254-254: LGTM!

Also applies to: 263-263, 1910-1918, 1939-1950, 2086-2094, 2115-2125

go/nemo_relay/observability_plugin.go (1)

146-146: LGTM!

crates/ffi/nemo_relay.h (1)

1324-1343: LGTM!

Also applies to: 1398-1417

crates/ffi/src/api/observability.rs (1)

19-19: LGTM!

Also applies to: 689-725, 783-787, 920-956, 1014-1018

crates/node/tests/openinference_tests.mjs (1)

72-78: LGTM!

crates/node/tests/otel_tests.mjs (1)

68-74: LGTM!

crates/python/src/py_types/observability.rs (1)

17-30: LGTM!

Also applies to: 398-398, 436-436, 455-455, 487-503, 597-597, 635-635, 654-654, 686-702

python/nemo_relay/_native.pyi (1)

967-974: LGTM!

Also applies to: 1047-1054

python/nemo_relay/observability.py (1)

201-201: LGTM!

Also applies to: 219-219

python/nemo_relay/observability.pyi (1)

78-78: LGTM!

python/tests/test_observability_plugin.py (1)

43-43: LGTM!

python/tests/test_types.py (1)

572-573: LGTM!

Also applies to: 585-586, 666-667, 679-680

crates/core/src/observability/mod.rs (1)

9-31: LGTM!

Also applies to: 75-95, 97-169, 170-205, 357-435

crates/core/src/observability/openinference.rs (3)

24-28: LGTM!

Also applies to: 82-84, 112-129, 217-236, 257-353


501-512: LGTM!

Also applies to: 535-569, 591-629


631-691: LGTM!

Also applies to: 780-877, 1447-1515, 1532-1533

crates/core/src/observability/otel.rs (3)

24-28: LGTM!

Also applies to: 43-44, 75-77, 96-123, 211-230, 246-347


494-506: LGTM!

Also applies to: 528-562, 584-623


625-678: LGTM!

Also applies to: 763-799, 979-1044

crates/core/tests/integration/middleware_tests.rs (1)

958-969: LGTM!

Also applies to: 989-1000

crates/core/tests/unit/observability/exporter_parity_tests.rs (2)

445-448: LGTM!

Also applies to: 622-623, 655-668


711-722: LGTM!

crates/core/tests/unit/observability/openinference_tests.rs (2)

690-794: LGTM!


851-878: LGTM!

Also applies to: 979-984, 1054-1075, 1254-1254, 1962-1967, 2400-2409, 2771-2776, 2818-2823, 2960-2963, 3035-3040, 3947-3953, 4043-4061

crates/core/tests/unit/observability/otel_tests.rs (2)

587-691: LGTM!


742-758: LGTM!

Also applies to: 855-861, 1085-1095, 1348-1353, 1442-1442, 1567-1589, 1881-1887

crates/core/tests/unit/observability/plugin_component_tests.rs (1)

225-225: LGTM!

Also applies to: 246-254, 300-315, 339-339

Comment thread crates/core/tests/unit/observability/exporter_parity_tests.rs Outdated
Comment thread crates/ffi/src/api/observability.rs
Comment thread crates/ffi/tests/unit/api/plugin_tests.rs
Comment thread crates/node/tests/observability_plugin_tests.mjs
Comment thread docs/configure-plugins/observability/opentelemetry.mdx Outdated
Comment thread go/nemo_relay/nemo_relay.go
Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07
willkill07 force-pushed the wkk_typed-otlp-attribute-projection branch from 7fadff2 to 0c33e81 Compare July 14, 2026 01:18
@copy-pr-bot

copy-pr-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (1)
python/tests/test_observability_plugin.py (1)

39-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test non-empty mapping serialization.

This assertion covers only the default []. Add an OtlpConfig(attribute_mappings=[...]) case and assert the mapping survives to_dict() so the new Python serialization path is protected.

As per path instructions, Python tests should cover behavior promised by the changed API 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 `@python/tests/test_observability_plugin.py` around lines 39 - 43, Add a
separate assertion in the OtlpConfig serialization tests using a non-empty
attribute_mappings value, and verify that OtlpConfig.to_dict() preserves the
supplied mapping unchanged while retaining the existing default-configuration
assertion.

Source: Path instructions

♻️ Duplicate comments (2)
crates/ffi/tests/unit/api/plugin_tests.rs (1)

1302-1382: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Constructor validation is covered, but the mapping is never observed on an exported span.

Same gap flagged previously: this test only checks NemoRelayStatus::Ok/InvalidArg from the constructors, never emits an event through the created subscribers to confirm tenant.id (or similar) actually appears with the expected typed value in the exported span. A regression in attribute-mapping forwarding through the FFI layer could still pass this test.

🤖 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/tests/unit/api/plugin_tests.rs` around lines 1302 - 1382, Extend
test_ffi_typed_attribute_mapping_constructors_validate_and_accept_mappings to
emit an event through each successfully created subscriber and inspect the
exported span, asserting the configured tenant.id mapping appears with the
expected typed value. Reuse the existing test helpers and export/flush
inspection path, and retain the current valid and invalid constructor status
assertions.
crates/core/tests/unit/observability/exporter_parity_tests.rs (1)

697-721: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Stale nemo_relay.start.input_json key is still present and will panic.

This is the same issue flagged on a previous commit: start_attributes() now only projects event.input() into dotted top-level keys (e.g. nemo_relay.start.input.headers, confirmed two lines below in this same block); it no longer emits a nemo_relay.start.input_json envelope. Line 701's otel.get("nemo_relay.start.input_json").unwrap() will panic with None.unwrap().

🐛 Proposed fix
-    let otel_input: Json =
-        serde_json::from_str(otel.get("nemo_relay.start.input_json").unwrap()).unwrap();
-    assert_eq!(otel_input, request_content);
-    assert_eq!(
+    assert_eq!(
         otel.get("nemo_relay.start.input.headers"),
         Some(&"{}".to_string())
     );
🤖 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/tests/unit/observability/exporter_parity_tests.rs` around lines
697 - 721, Update the OTel assertions in the model-call parity test to stop
reading the removed nemo_relay.start.input_json envelope and instead validate
the projected dotted start-input attributes produced by start_attributes(),
including the existing nemo_relay.start.input.headers assertion. Remove the
serde_json parsing and request_content equality check tied to that stale key
while preserving the output-attribute assertions.
🤖 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/core/src/observability/mod.rs`:
- Around line 75-95: Update validate_attribute_mappings so duplicate detection
uses each mapping.alias trimmed of surrounding whitespace, while preserving the
existing blank-alias validation and duplicate error behavior. Store or compare
the normalized alias in aliases so values such as "foo" and " foo" are rejected
as duplicates.

In `@crates/core/tests/unit/observability/otel_tests.rs`:
- Around line 742-748: Correct the assertion in the affected observability test
to use the input attribute key for the task value: update the key from
nemo_relay.start.data.task to nemo_relay.start.input.task, while leaving the
metadata.phase assertion unchanged.
- Around line 1567-1573: Update the query assertion in the event observability
test to use the nemo_relay.start.data.query attribute key, matching the event’s
BaseEvent::builder().data(...) construction; leave the metadata assertion
unchanged.

In `@crates/node/observability.d.ts`:
- Line 61: Rename the public option from attribute_mappings to attributeMappings
in crates/node/observability.d.ts at line 61 and initialize the same camelCase
property in crates/node/observability.js at line 62; preserve the single
translation to the internal snake_case schema at the native boundary.

In `@crates/node/tests/openinference_tests.mjs`:
- Line 35: Extend the positive binding tests to verify emitted attributes, not
just successful construction: in crates/node/tests/openinference_tests.mjs at
line 35, assert the OpenInference span contains the mapped tenant alias and its
typed value; in crates/node/tests/otel_tests.mjs at line 31, assert the emitted
OpenTelemetry span contains tenant.id and its typed value. Use each test’s
existing event/export assertion helpers and preserve the current mapping setup.

In `@python/nemo_relay/observability.py`:
- Line 201: Preserve positional dataclass constructor compatibility by moving
attribute_mappings after timeout_millis in both
python/nemo_relay/observability.py lines 201-201 and
python/nemo_relay/observability.pyi lines 78-78. Keep the runtime declaration
and public stub field order identical so existing positional arguments continue
binding to transport and subsequent fields correctly.

In `@python/tests/test_types.py`:
- Around line 572-573: Extend the existing OpenTelemetry and OpenInference
collector-backed tests around their attribute-mapping setup to verify
binding-level forwarding: configure the mapping with the tenant key and alias,
emit telemetry, and assert the alias appears in the resulting OTLP attributes.
Keep the existing local getter/setter round-trip assertions, and apply the same
forwarding assertion to both referenced test locations.
- Around line 585-587: Add test cases in the attribute_mappings validation tests
for both native configuration types, covering a blank alias and duplicate
aliases in addition to the existing blank key case. Assert each raises
ValueError with the corresponding validation message, preserving coverage of the
shared mapping contract.

---

Outside diff comments:
In `@python/tests/test_observability_plugin.py`:
- Around line 39-43: Add a separate assertion in the OtlpConfig serialization
tests using a non-empty attribute_mappings value, and verify that
OtlpConfig.to_dict() preserves the supplied mapping unchanged while retaining
the existing default-configuration assertion.

---

Duplicate comments:
In `@crates/core/tests/unit/observability/exporter_parity_tests.rs`:
- Around line 697-721: Update the OTel assertions in the model-call parity test
to stop reading the removed nemo_relay.start.input_json envelope and instead
validate the projected dotted start-input attributes produced by
start_attributes(), including the existing nemo_relay.start.input.headers
assertion. Remove the serde_json parsing and request_content equality check tied
to that stale key while preserving the output-attribute assertions.

In `@crates/ffi/tests/unit/api/plugin_tests.rs`:
- Around line 1302-1382: Extend
test_ffi_typed_attribute_mapping_constructors_validate_and_accept_mappings to
emit an event through each successfully created subscriber and inspect the
exported span, asserting the configured tenant.id mapping appears with the
expected typed value. Reuse the existing test helpers and export/flush
inspection path, and retain the current valid and invalid constructor status
assertions.
🪄 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: ff608ed6-2dc2-4a9d-9daf-b674c823c5cd

📥 Commits

Reviewing files that changed from the base of the PR and between 0620b48 and 7fadff2.

📒 Files selected for processing (28)
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/node/src/api/mod.rs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/openinference_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/python/src/py_types/observability.rs
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • 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
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (33)
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_observability_plugin.py
  • crates/core/tests/integration/middleware_tests.rs
  • python/tests/test_types.py
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • python/nemo_relay/observability.py
  • crates/node/tests/observability_plugin_tests.mjs
  • python/tests/test_observability_plugin.py
  • crates/node/tests/otel_tests.mjs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • crates/node/tests/openinference_tests.mjs
  • python/tests/test_types.py
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • python/nemo_relay/observability.py
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/node/observability.js
  • go/nemo_relay/observability_plugin.go
  • crates/node/observability.d.ts
  • python/tests/test_types.py
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/core/src/observability/mod.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • go/nemo_relay/nemo_relay.go
  • crates/core/src/observability/openinference.rs
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

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

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • python/nemo_relay/_native.pyi
  • crates/node/src/api/mod.rs
  • go/nemo_relay/nemo_relay.go
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

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

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • python/nemo_relay/_native.pyi
  • go/nemo_relay/nemo_relay.go
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • crates/core/src/observability/mod.rs
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{py,go,js,ts}

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

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/node/observability.js
  • go/nemo_relay/observability_plugin.go
  • crates/node/observability.d.ts
  • python/tests/test_types.py
  • go/nemo_relay/nemo_relay.go
**/*

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • python/nemo_relay/observability.py
  • crates/node/tests/observability_plugin_tests.mjs
  • python/nemo_relay/observability.pyi
  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • crates/node/tests/otel_tests.mjs
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/node/observability.js
  • go/nemo_relay/observability_plugin.go
  • crates/node/observability.d.ts
  • crates/node/tests/openinference_tests.mjs
  • python/tests/test_types.py
  • python/nemo_relay/_native.pyi
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/core/src/observability/mod.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • go/nemo_relay/nemo_relay.go
  • crates/core/src/observability/openinference.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/node/observability.js
  • go/nemo_relay/observability_plugin.go
  • crates/node/observability.d.ts
  • python/tests/test_types.py
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • go/nemo_relay/nemo_relay.go
  • crates/core/src/observability/openinference.rs
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/observability.py
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/_native.pyi
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • crates/node/tests/openinference_tests.mjs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: 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/observability_plugin_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • crates/node/tests/openinference_tests.mjs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.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/observability_plugin_tests.mjs
  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • crates/node/tests/otel_tests.mjs
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/node/tests/openinference_tests.mjs
  • python/tests/test_types.py
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
python/tests/**/*.py

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

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

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

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

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

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/node/observability.js
  • go/nemo_relay/observability_plugin.go
  • crates/node/observability.d.ts
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • go/nemo_relay/nemo_relay.go
  • crates/core/src/observability/openinference.rs
go/nemo_relay/**

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

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
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/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/integration/middleware_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/node/src/api/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{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/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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

Files:

  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
crates/{core,adaptive}/**/*

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

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

Files:

  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
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.js
  • 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
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/tests/unit/api/plugin_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
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

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
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

Update generated or generated-from-build surfaces such as crates/ffi/nemo_relay.h through the proper build step.

Files:

  • crates/ffi/nemo_relay.h
{crates/ffi/src/api/*.rs,crates/ffi/nemo_relay.h}

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

Add or update the shared C/FFI surface in the relevant crates/ffi/src/api/*.rs module, re-export it through crates/ffi/src/api/mod.rs, and keep the generated crates/ffi/nemo_relay.h header correct.

Files:

  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
crates/core/src/observability/{atif,otel,openinference}.rs

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

When changing the core event model, emitted fields, exporter behavior, subscriber config, or lifecycle, keep the ATIF, OpenTelemetry, and OpenInference implementations in sync.

Files:

  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
🧠 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
🔇 Additional comments (43)
crates/node/tests/observability_plugin_tests.mjs (1)

34-34: The previously reported Node casing issue remains.

This assertion still codifies attribute_mappings in the Node-facing helper shape. Update it when the public helper and native-boundary translation are changed.

crates/node/observability.d.ts (1)

73-77: LGTM!

crates/node/src/api/mod.rs (1)

112-126: LGTM!

Also applies to: 187-187, 297-297, 3470-3480, 3505-3506

crates/node/tests/openinference_tests.mjs (1)

72-78: LGTM!

crates/node/tests/otel_tests.mjs (1)

68-74: LGTM!

crates/ffi/src/api/observability.rs (2)

533-551: 🎯 Functional Correctness | ⚡ Quick win

Shape errors still return InvalidJson instead of InvalidArg.

serde_json::from_str deserializes directly into Vec<OtlpAttributeMapping>, so syntactically valid JSON with the wrong shape ({}, null, a non-string key/alias) is classified as InvalidJson rather than InvalidArg, inconsistent with parse_string_map_json's documented status codes. This was already flagged on a previous revision and remains unresolved.

🐛 Proposed fix
-    let mappings: Vec<OtlpAttributeMapping> =
-        serde_json::from_str(&json_string).map_err(|error| {
-            set_last_error(&format!("invalid attribute_mappings JSON: {error}"));
-            NemoRelayStatus::InvalidJson
-        })?;
+    let value: serde_json::Value = serde_json::from_str(&json_string).map_err(|error| {
+        set_last_error(&format!("invalid attribute_mappings JSON: {error}"));
+        NemoRelayStatus::InvalidJson
+    })?;
+    let mappings: Vec<OtlpAttributeMapping> = serde_json::from_value(value).map_err(|error| {
+        set_last_error(&format!("invalid attribute_mappings: {error}"));
+        NemoRelayStatus::InvalidArg
+    })?;

689-725: LGTM!

Also applies to: 783-787, 920-956, 1014-1018

go/nemo_relay/nemo_relay.go (2)

1840-1846: 📐 Maintainability & Code Quality | 🏗️ Heavy lift

Go tests still lack behavior assertions for attribute mappings.

AttributeMappings is wired through correctly, but neither Go test (otel_test.go, openinference_test.go) asserts the mapped alias is actually emitted with the correct value, or that an invalid mapping is rejected — only subscriber lifecycle success is proven. Flagged previously and still unresolved.

As per path instructions, "Any API change should include focused Go tests and consider race-test behavior."

Source: Path instructions


254-254: LGTM!

Also applies to: 263-263, 1910-1949, 2086-2125

go/nemo_relay/openinference_test.go (1)

55-58: 📐 Maintainability & Code Quality | 🏗️ Heavy lift

Mapping is configured but never asserted.

AttributeMappings is set but the test only checks lifecycle success, not that tenant.id alias is actually emitted or that malformed mappings are rejected via this Go→FFI path. Same gap flagged previously on nemo_relay.go.

go/nemo_relay/otel_test.go (1)

48-51: 📐 Maintainability & Code Quality | 🏗️ Heavy lift

Mapping is configured but never asserted.

Same gap as the OpenInference test: AttributeMappings is set but no assertion proves the aliased attribute is emitted correctly or that invalid mappings are rejected through this path. Same gap flagged previously on nemo_relay.go.

crates/core/src/observability/plugin_component.rs (1)

54-56: LGTM!

Also applies to: 387-389, 426-426, 508-508, 1378-1379, 1418-1419, 2129-2138

crates/ffi/nemo_relay.h (1)

1324-1343: LGTM!

Also applies to: 1398-1417

go/nemo_relay/observability_plugin.go (1)

142-156: LGTM!

crates/python/src/py_types/observability.rs (1)

17-30: LGTM!

Also applies to: 398-398, 436-436, 455-455, 487-503, 597-597, 635-635, 654-654, 686-702

python/nemo_relay/_native.pyi (1)

967-974: LGTM!

Also applies to: 1047-1054

python/nemo_relay/observability.py (1)

219-219: LGTM!

crates/core/src/observability/mod.rs (4)

9-31: LGTM!


97-169: LGTM! Top-level-only projection with typed scalar handling (including the u64-overflow-to-string fallback) matches the documented contract and is covered by the accompanying test.


170-205: LGTM! Alias resolution correctly protects existing keys and prefers the most recently pushed match, consistent with the doc comment and cross-file lifecycle tests.


357-435: LGTM!

crates/core/src/observability/openinference.rs (7)

24-28: LGTM!

Also applies to: 82-84, 112-129, 217-236


257-266: LGTM! Validation is correctly enforced at every construction path that accepts mappings, and empty-vec forwarding is consistent for the mapping-less convenience constructors.

Also applies to: 280-280, 295-295, 315-353


501-501: LGTM!

Also applies to: 512-512, 535-567


599-609: LGTM! Storing projected_attributes at start and merging with end attributes before alias resolution correctly ensures aliases can reference either start- or end-projected fields while never overriding an already-present key, matching the OpenInference span lifecycle tests.

Also applies to: 618-625


640-646: LGTM! Mappings are applied after intrinsic mark fields (orphan flag, span kind) are pushed, so intrinsic fields can't be clobbered by a misconfigured alias.

Also applies to: 659-665, 681-681


780-797: LGTM! The expanded LLM detection and metadata retain-filter correctly ensure the final span reflects the completed event's metadata for LLM spans.

Also applies to: 832-840


1447-1477: LGTM!

Also applies to: 1512-1512

crates/core/src/observability/otel.rs (6)

24-28: LGTM!

Also applies to: 43-44, 75-77, 96-123, 211-230


246-260: LGTM! Mirrors the OpenInference subscriber's validation-on-construction pattern.

As per coding guidelines, crates/core/src/observability/{atif,otel,openinference}.rs should keep ATIF/OTel/OpenInference implementations in sync when the core event model or lifecycle changes — this segment stays consistent with openinference.rs.

Also applies to: 270-291, 313-347

Source: Coding guidelines


494-506: LGTM!

Also applies to: 528-562


584-602: LGTM! Same start/end attribute-merge-then-alias pattern as openinference.rs, verified by the OTel unit tests for typed alias copying and precedence.

Also applies to: 605-619


625-640: LGTM! Mapping application ordering (after intrinsic fields are pushed) matches openinference.rs, preserving intrinsic-field protection parity.

Also applies to: 653-656, 659-669


763-784: LGTM!

Also applies to: 979-1012

crates/core/tests/integration/middleware_tests.rs (1)

958-969: LGTM!

Also applies to: 987-1000

crates/core/tests/unit/observability/exporter_parity_tests.rs (2)

445-448: LGTM!


655-668: 🎯 Functional Correctness

Use nemo_relay.end.output.reasoning here. llm_end() stores reasoning in event.output(), and both exporters flatten that payload under nemo_relay.end.output.*; nemo_relay.end.data.reasoning is not emitted for this case.

			> Likely an incorrect or invalid review comment.
crates/core/tests/unit/observability/openinference_tests.rs (1)

690-794: LGTM!

Also applies to: 851-880, 979-984, 1054-1075, 1254-1254, 1962-1967, 2400-2409, 2771-2776, 2818-2823, 2960-2963, 3035-3040, 3947-3953, 4043-4061

crates/core/tests/unit/observability/otel_tests.rs (4)

587-691: LGTM!


855-861: LGTM!


1085-1094: LGTM!

Also applies to: 1348-1353, 1442-1442


1587-1589: LGTM!

Also applies to: 1881-1887

crates/core/tests/unit/observability/plugin_component_tests.rs (1)

225-225: LGTM!

Also applies to: 246-254, 300-315, 339-339

Comment thread crates/core/src/observability/mod.rs
Comment thread crates/core/tests/unit/observability/otel_tests.rs Outdated
Comment thread crates/core/tests/unit/observability/otel_tests.rs Outdated
Comment thread crates/node/observability.d.ts
Comment thread crates/node/tests/openinference_tests.mjs
Comment thread python/nemo_relay/observability.py Outdated
Comment thread python/tests/test_types.py
Comment thread python/tests/test_types.py
Signed-off-by: Will Killian <wkillian@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
crates/core/tests/unit/observability/openinference_tests.rs (1)

3987-4053: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the data key for status_code
status_code is supplied via .data(...), so this assertion should check nemo_relay.end.data.status_code instead of nemo_relay.end.output.status_code.

🤖 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/tests/unit/observability/openinference_tests.rs` around lines
3987 - 4053, The assertion for the status code in the observability test uses
the wrong attribute namespace. Update the assertion near the end of the test to
check nemo_relay.end.data.status_code, matching the status_code field supplied
through BaseEvent::builder().data(...), while leaving the expected value
unchanged.
♻️ Duplicate comments (4)
python/nemo_relay/observability.py (1)

201-201: ⚠️ Potential issue | 🟠 Major

Preserve positional OtlpConfig compatibility. Inserting attribute_mappings before transport shifts all later positional arguments in the public dataclass constructor.

  • python/nemo_relay/observability.py#L201-L201: move attribute_mappings after timeout_millis.
  • python/nemo_relay/observability.pyi#L78-L78: mirror the corrected field order.
    As per path instructions, binding changes are public API changes and should preserve existing behavior unless a migration path is provided.
🤖 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 `@python/nemo_relay/observability.py` at line 201, Restore positional
OtlpConfig compatibility by moving attribute_mappings after timeout_millis in
python/nemo_relay/observability.py at lines 201-201, and mirror the same field
order in python/nemo_relay/observability.pyi at lines 78-78. Preserve all
existing later positional constructor arguments and behavior.

Source: Path instructions

crates/ffi/tests/unit/api/plugin_tests.rs (1)

1302-1381: ⚠️ Potential issue | 🟡 Minor

Assert positive attribute projection, not just constructor acceptance. A mapping can be accepted but lost during forwarding or export serialization without these tests detecting it.

  • crates/ffi/tests/unit/api/plugin_tests.rs#L1302-L1381: emit through the created subscribers and assert the configured tenant.id alias.
  • crates/node/tests/otel_tests.mjs#L31-L31: add a runtime/exporter assertion that the alias is emitted with the expected typed value.
    As per path instructions, tests should cover the behavior promised by the changed API 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/ffi/tests/unit/api/plugin_tests.rs` around lines 1302 - 1381, Extend
test_ffi_typed_attribute_mapping_constructors_validate_and_accept_mappings in
crates/ffi/tests/unit/api/plugin_tests.rs (lines 1302-1381) to emit through both
created subscribers and assert that the configured tenant.id alias is
forwarded/exported with the expected value. Add a runtime/exporter assertion in
crates/node/tests/otel_tests.mjs (line 31) verifying the alias is emitted with
the expected typed value; retain the existing constructor validation checks.

Source: Path instructions

crates/core/tests/unit/observability/otel_tests.rs (2)

740-758: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Still asserting the wrong attribute key: task came from .input(...), not .data(...).

Unresolved from a previous review round. .data(json!({"scope": true})) and .input(json!({"task": "scope-start"})) are separate fields; per start_attributes(), task should project under nemo_relay.start.input.task, not nemo_relay.start.data.task. As written this assertion will fail.

🐛 Proposed fix
     let attributes = attr_map(&span.attributes);
     assert_eq!(
-        attributes.get("nemo_relay.start.data.task"),
+        attributes.get("nemo_relay.start.input.task"),
         Some(&"scope-start".to_string())
     );
🤖 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/tests/unit/observability/otel_tests.rs` around lines 740 - 758,
Update the span attribute assertion in the observability test to use
nemo_relay.start.input.task for the task value from .input(...), while
preserving the expected scope-start value and leaving the data and event
attribute assertions unchanged.

Source: Path instructions


1565-1573: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Still asserting the wrong attribute key (mirror-image of the L742 bug): query came from .data(...), no .input() was called.

Unresolved from a previous review round. tool_event is built with .data(json!({"query": "hello"})) only; event.input() is None, so no nemo_relay.start.input.* keys are emitted. The assertion should target nemo_relay.start.data.query.

🐛 Proposed fix
     let start_attributes = attr_map(&start_attributes(&tool_event));
     assert_eq!(
-        start_attributes.get("nemo_relay.start.input.query"),
+        start_attributes.get("nemo_relay.start.data.query"),
         Some(&"hello".to_string())
     );
🤖 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/tests/unit/observability/otel_tests.rs` around lines 1565 - 1573,
Update the query assertion in the tool_event observability test to use the
emitted key nemo_relay.start.data.query, since tool_event is populated via data
rather than input. Leave the metadata assertion unchanged.
🤖 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/core/src/observability/otel.rs`:
- Around line 611-619: Guard every alias/application mapping call in the
event-processing methods with !self.attribute_mappings.is_empty(). Update both
call sites in process_mark and the call site in process_mark_as_tool, including
the shown span-end path, so attribute_mapping_aliases/apply_attribute_mappings
is skipped when no mappings are configured while preserving existing behavior
when mappings exist.

In `@go/nemo_relay/otel_test.go`:
- Around line 48-51: Extend the lifecycle test in go/nemo_relay/otel_test.go at
lines 48-51 to send spans through a mock OTLP collector and assert the emitted
span contains the tenant.id alias attribute; reuse any existing collector-test
helper. Apply the same collector-backed assertion to
go/nemo_relay/openinference_test.go at lines 55-58, ensuring both exporter paths
verify forwarding rather than only local AttributeMappings configuration.

---

Outside diff comments:
In `@crates/core/tests/unit/observability/openinference_tests.rs`:
- Around line 3987-4053: The assertion for the status code in the observability
test uses the wrong attribute namespace. Update the assertion near the end of
the test to check nemo_relay.end.data.status_code, matching the status_code
field supplied through BaseEvent::builder().data(...), while leaving the
expected value unchanged.

---

Duplicate comments:
In `@crates/core/tests/unit/observability/otel_tests.rs`:
- Around line 740-758: Update the span attribute assertion in the observability
test to use nemo_relay.start.input.task for the task value from .input(...),
while preserving the expected scope-start value and leaving the data and event
attribute assertions unchanged.
- Around line 1565-1573: Update the query assertion in the tool_event
observability test to use the emitted key nemo_relay.start.data.query, since
tool_event is populated via data rather than input. Leave the metadata assertion
unchanged.

In `@crates/ffi/tests/unit/api/plugin_tests.rs`:
- Around line 1302-1381: Extend
test_ffi_typed_attribute_mapping_constructors_validate_and_accept_mappings in
crates/ffi/tests/unit/api/plugin_tests.rs (lines 1302-1381) to emit through both
created subscribers and assert that the configured tenant.id alias is
forwarded/exported with the expected value. Add a runtime/exporter assertion in
crates/node/tests/otel_tests.mjs (line 31) verifying the alias is emitted with
the expected typed value; retain the existing constructor validation checks.

In `@python/nemo_relay/observability.py`:
- Line 201: Restore positional OtlpConfig compatibility by moving
attribute_mappings after timeout_millis in python/nemo_relay/observability.py at
lines 201-201, and mirror the same field order in
python/nemo_relay/observability.pyi at lines 78-78. Preserve all existing later
positional constructor arguments and behavior.
🪄 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: 2023d811-9d24-40e4-9bdc-198426659382

📥 Commits

Reviewing files that changed from the base of the PR and between 7fadff2 and 0c33e81.

📒 Files selected for processing (29)
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/node/src/api/mod.rs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/tests/openinference_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • crates/python/src/py_types/observability.rs
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • 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 (33)
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • python/tests/test_observability_plugin.py
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • python/tests/test_types.py
  • python/nemo_relay/observability.py
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • python/tests/test_observability_plugin.py
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/observability.js
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/node/observability.d.ts
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • python/nemo_relay/observability.py
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/node/tests/openinference_tests.mjs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • python/nemo_relay/observability.py
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/node/observability.js
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/node/observability.d.ts
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/observability_plugin.go
  • python/tests/test_types.py
  • python/nemo_relay/observability.py
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/node/src/api/mod.rs
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/src/api/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • python/tests/test_observability_plugin.py
  • python/tests/test_types.py
  • python/nemo_relay/observability.py
  • crates/core/src/observability/mod.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{py,go,js,ts}

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

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • go/nemo_relay/observability_plugin.go
  • python/tests/test_types.py
  • python/nemo_relay/observability.py
  • go/nemo_relay/nemo_relay.go
python/tests/**/*.py

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

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

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

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • python/tests/test_observability_plugin.py
  • crates/node/tests/observability_plugin_tests.mjs
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/node/observability.js
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/node/observability.d.ts
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/observability_plugin.go
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/observability.py
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/nemo_relay.h
  • python/nemo_relay/_native.pyi
  • crates/node/tests/openinference_tests.mjs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/node/src/api/mod.rs
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/src/api/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • python/tests/test_observability_plugin.py
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/node/observability.js
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/node/observability.d.ts
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/observability_plugin.go
  • python/tests/test_types.py
  • python/nemo_relay/observability.py
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/node/src/api/mod.rs
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/src/api/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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:

  • python/tests/test_observability_plugin.py
  • crates/node/tests/observability_plugin_tests.mjs
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/node/tests/openinference_tests.mjs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • crates/node/tests/otel_tests.mjs
  • crates/node/tests/openinference_tests.mjs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: 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/observability_plugin_tests.mjs
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/tests/otel_tests.mjs
  • crates/ffi/nemo_relay.h
  • crates/node/tests/openinference_tests.mjs
  • crates/python/src/py_types/observability.rs
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
go/nemo_relay/**/*.go

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

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

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/node/observability.js
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/node/observability.d.ts
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/observability_plugin.go
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/node/src/api/mod.rs
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/src/api/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

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

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/observability.py
  • python/nemo_relay/_native.pyi
  • crates/node/src/api/mod.rs
  • go/nemo_relay/nemo_relay.go
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

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

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/observability.py
  • python/nemo_relay/_native.pyi
  • go/nemo_relay/nemo_relay.go
go/nemo_relay/**

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

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
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/otel_test.go
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
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.js
  • crates/node/observability.d.ts
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/python/src/py_types/observability.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
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
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/tests/unit/api/plugin_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
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

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
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/observability.pyi
  • python/nemo_relay/observability.py
  • python/nemo_relay/_native.pyi
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • 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/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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

Files:

  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
crates/{core,adaptive}/**/*

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

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

Files:

  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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/integration/middleware_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/exporter_parity_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
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

Update generated or generated-from-build surfaces such as crates/ffi/nemo_relay.h through the proper build step.

Files:

  • crates/ffi/nemo_relay.h
{crates/ffi/src/api/*.rs,crates/ffi/nemo_relay.h}

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

Add or update the shared C/FFI surface in the relevant crates/ffi/src/api/*.rs module, re-export it through crates/ffi/src/api/mod.rs, and keep the generated crates/ffi/nemo_relay.h header correct.

Files:

  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
crates/core/src/observability/{atif,otel,openinference}.rs

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

When changing the core event model, emitted fields, exporter behavior, subscriber config, or lifecycle, keep the ATIF, OpenTelemetry, and OpenInference implementations in sync.

Files:

  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
🧠 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
🔇 Additional comments (32)
python/tests/test_observability_plugin.py (1)

43-43: LGTM!

crates/node/observability.js (1)

62-62: LGTM!

crates/cli/tests/coverage/plugins_tests.rs (2)

9-11: LGTM!

Also applies to: 203-209, 230-230, 325-325, 1177-1178, 1191-1192, 1207-1208, 1223-1224, 2332-2333


2267-2320: LGTM!

Also applies to: 2348-2476

crates/node/tests/otel_tests.mjs (1)

68-74: LGTM!

python/nemo_relay/observability.py (1)

219-219: LGTM!

crates/core/tests/unit/observability/exporter_parity_tests.rs (1)

445-448: LGTM!

Also applies to: 622-624, 655-668, 697-725

crates/node/tests/observability_plugin_tests.mjs (1)

34-34: 📐 Maintainability & Code Quality | ⚡ Quick win

Still snake_case: attribute_mappings should be attributeMappings in the Node API.

This test still encodes the snake_case attribute_mappings shape. Per coding guidelines, Node.js public APIs must use camelCase; expose attributeMappings to JS and translate internally.

Source: Coding guidelines

crates/node/tests/openinference_tests.mjs (2)

35-35: 🎯 Functional Correctness | ⚡ Quick win

Still missing emitted-attribute assertions for attributeMappings.

Construction with attributeMappings succeeds but nothing asserts the mapped tenant.id alias and its typed value actually appear on the emitted OpenInference span, so a regression dropping the mapping before it reaches core config would go undetected.

Source: Path instructions


72-78: LGTM!

crates/core/tests/integration/middleware_tests.rs (1)

958-969: LGTM!

Also applies to: 989-1000

crates/core/tests/unit/observability/plugin_component_tests.rs (1)

60-96: LGTM!

Also applies to: 202-254, 257-328, 332-373, 652-714

crates/ffi/nemo_relay.h (1)

1324-1343: LGTM!

Also applies to: 1398-1417

python/nemo_relay/_native.pyi (1)

967-974: LGTM!

Also applies to: 1047-1054

crates/core/src/observability/openinference.rs (1)

24-28: LGTM!

Also applies to: 82-129, 217-353, 498-629, 631-877, 1447-1515

python/tests/test_types.py (2)

572-573: Still missing binding-level forwarding assertion (unresolved from prior review).

These assertions only prove local getter/setter round-tripping. The collector-backed end-to-end tests (test_subscriber_exports_scope_and_mark_events_end_to_end) never configure attribute_mappings or assert the alias appears in the emitted OTLP attributes. Also applies to lines 666-667.


585-587: Still missing blank-alias/duplicate-alias validation coverage (unresolved from prior review).

Only the blank-key branch of validate_attribute_mappings is exercised. Blank alias and duplicate-alias cases remain untested for both OpenTelemetryConfig and OpenInferenceConfig. Also applies to lines 679-681.

crates/ffi/src/api/observability.rs (2)

533-551: Unresolved from prior review: shape errors still misclassified as InvalidJson.

Syntactically-valid-but-wrong-shape payloads ({}, null, non-string key) still return NemoRelayStatus::InvalidJson instead of InvalidArg, inconsistent with parse_string_map_json's documented status codes.

🐛 Previously proposed fix (still applicable)
-    let mappings: Vec<OtlpAttributeMapping> =
-        serde_json::from_str(&json_string).map_err(|error| {
-            set_last_error(&format!("invalid attribute_mappings JSON: {error}"));
-            NemoRelayStatus::InvalidJson
-        })?;
+    let value: serde_json::Value = serde_json::from_str(&json_string).map_err(|error| {
+        set_last_error(&format!("invalid attribute_mappings JSON: {error}"));
+        NemoRelayStatus::InvalidJson
+    })?;
+    let mappings: Vec<OtlpAttributeMapping> = serde_json::from_value(value).map_err(|error| {
+        set_last_error(&format!("invalid attribute_mappings: {error}"));
+        NemoRelayStatus::InvalidArg
+    })?;

19-19: LGTM!

Also applies to: 689-726, 783-804, 909-936, 1014-1035

go/nemo_relay/observability_plugin.go (1)

142-156: LGTM!

crates/python/src/py_types/observability.rs (1)

17-31: LGTM!

Also applies to: 398-398, 436-436, 455-455, 487-503, 597-597, 635-635, 654-654, 686-702

crates/core/src/observability/otel.rs (1)

24-28: LGTM!

Also applies to: 57-81, 95-127, 129-231, 244-402, 491-562, 584-609, 763-799, 979-1013

crates/node/observability.d.ts (2)

61-61: 🗄️ Data Integrity & Integration | ⚡ Quick win

Still snake_case: attribute_mappings should be attributeMappings.

This is the same mismatch flagged previously — the public Node config still exposes attribute_mappings while the intended convention is camelCase, translated to the internal snake_case schema only at the native boundary. Unresolved since last review.

As per coding guidelines, Node.js public APIs use camelCase.

Source: Coding guidelines


73-78: LGTM!

crates/core/src/observability/mod.rs (2)

79-93: 🎯 Functional Correctness | ⚡ Quick win

Alias duplicate check still doesn't trim before comparing.

mapping.alias.trim().is_empty() validates the trimmed value, but aliases.insert(mapping.alias.as_str()) inserts the raw alias, so "foo" and " foo" still pass as distinct. This was flagged previously and remains unresolved.

🐛 Proposed fix
-        if !aliases.insert(mapping.alias.as_str()) {
+        if !aliases.insert(mapping.alias.trim()) {
             return Err(format!(
                 "attribute mapping alias {:?} is duplicated",
                 mapping.alias
             ));
         }

97-204: LGTM!

Also applies to: 357-435

go/nemo_relay/nemo_relay.go (2)

1840-1847: 📐 Maintainability & Code Quality | 🏗️ Heavy lift

Still no behavior test for either Go mapping path.

Per the referenced Go test evidence, TestOpenTelemetrySubscriberLifecycle/TestOpenInferenceSubscriberLifecycle only set AttributeMappings and assert lifecycle success — they don't verify the alias is actually projected with the correct typed value, nor that invalid mappings are rejected. Unresolved from the previous review round.

As per path instructions, "Any API change should include focused Go tests and consider race-test behavior."

Also applies to: 2022-2022

Source: Path instructions


1830-1847: LGTM on the CGo marshaling/FFI wiring itself — nil-checked marshaling, correctly scoped defer C.free, and FFI argument order matches the extern declarations.

Also applies to: 1910-1949, 2012-2023, 2086-2125

crates/core/tests/unit/observability/otel_tests.rs (1)

587-691: LGTM!

Also applies to: 855-860, 1085-1094, 1348-1353, 1442-1442, 1881-1886

crates/core/src/observability/plugin_component.rs (1)

54-56: LGTM!

Also applies to: 390-392, 429-429, 506-536, 707-731, 1401-1404, 1441-1444, 2154-2163

crates/node/src/api/mod.rs (1)

112-127: LGTM!

Also applies to: 187-187, 297-297, 3451-3481, 3486-3507

crates/core/tests/unit/observability/openinference_tests.rs (1)

690-794: LGTM!

Also applies to: 851-878, 979-985, 1054-1075, 1254-1254, 1962-1968, 2400-2410, 2771-2776, 2818-2824, 2960-2967, 3035-3041, 3947-3954

Comment thread crates/core/src/observability/otel.rs
Comment thread go/nemo_relay/otel_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/node/tests/otel_tests.mjs`:
- Around line 110-111: Replace the independent byte checks with decoded OTLP
request assertions that locate the tenant.id attribute and verify its exact
string value. Update crates/node/tests/otel_tests.mjs lines 110-111 to expect
the mapped node value; crates/node/tests/openinference_tests.mjs lines 114-115
to expect the scope-end tenant node value; go/nemo_relay/otel_test.go lines
171-173 to expect mark metadata go; and go/nemo_relay/openinference_test.go
lines 150-152 to expect scope-end tenant go.
🪄 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: 1c4d45eb-eb42-4241-8093-2ae3674408e6

📥 Commits

Reviewing files that changed from the base of the PR and between 0c33e81 and d14422c.

📒 Files selected for processing (6)
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/tests/openinference_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (21)
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/tests/otel_tests.mjs
  • crates/node/tests/openinference_tests.mjs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/node/tests/otel_tests.mjs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/tests/openinference_tests.mjs
  • crates/ffi/src/api/observability.rs
**/*

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/node/tests/otel_tests.mjs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/node/tests/openinference_tests.mjs
  • crates/ffi/src/api/observability.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: 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/otel_tests.mjs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/tests/openinference_tests.mjs
  • crates/ffi/src/api/observability.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/otel_tests.mjs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/node/tests/openinference_tests.mjs
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
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/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
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

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/ffi/src/api/observability.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/ffi/src/api/observability.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/ffi/src/api/observability.rs
go/nemo_relay/**/*.go

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

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

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

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

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

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

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
**/*.{py,go,js,ts}

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

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
go/nemo_relay/**

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

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
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/otel_test.go
  • go/nemo_relay/openinference_test.go
{crates/ffi/src/api/*.rs,crates/ffi/nemo_relay.h}

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

Add or update the shared C/FFI surface in the relevant crates/ffi/src/api/*.rs module, re-export it through crates/ffi/src/api/mod.rs, and keep the generated crates/ffi/nemo_relay.h header correct.

Files:

  • crates/ffi/src/api/observability.rs
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/ffi/src/api/observability.rs
🔇 Additional comments (4)
crates/node/tests/otel_tests.mjs (1)

16-18: LGTM!

Also applies to: 86-92

go/nemo_relay/otel_test.go (1)

88-95: 📐 Maintainability & Code Quality

Run the required Go binding validation before handoff.

No Go validation results are provided. Run cd go/nemo_relay && go fmt ./..., go vet ./..., just test-go, and uv run pre-commit run --all-files.

  • go/nemo_relay/otel_test.go#L88-L95: validate the OpenTelemetry invalid-mapping path.
  • go/nemo_relay/openinference_test.go#L94-L101: validate the OpenInference invalid-mapping path.

As per coding guidelines: “Format changed Go packages with cd go/nemo_relay && go fmt ./...” and “Run Go tests with just test-go.”

Source: Coding guidelines

crates/node/tests/openinference_tests.mjs (1)

86-103: LGTM!

crates/ffi/src/api/observability.rs (1)

540-547: 📐 Maintainability & Code Quality

Run the required Rust and FFI validation before handoff.

No validation results are provided for these Rust/FFI changes. Run cargo fmt --all, just test-rust, cargo clippy --workspace --all-targets -- -D warnings, cargo deny check, the repository’s test-ffi-surface target, a release FFI rebuild, and uv run pre-commit run --all-files.

  • crates/ffi/src/api/observability.rs#L540-L547: validate the public FFI parsing behavior and generated surface.
  • crates/ffi/tests/unit/api/plugin_tests.rs#L1381-L1415: validate the new invalid-shape coverage against the same FFI artifact.

As per coding guidelines: “Any Rust change must run just test-rust,” “run cargo fmt --all,” and “run cargo clippy --workspace --all-targets -- -D warnings”; crates/ffi changes also require FFI-surface validation and a release rebuild.

Source: Coding guidelines

Comment thread crates/node/tests/otel_tests.mjs Outdated
Signed-off-by: Will Killian <wkillian@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

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/mod.rs (1)

425-444: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing test for blank-alias branch.

rejects_invalid_attribute_mappings covers blank key, duplicate alias, and whitespace-duplicate alias, but never exercises the blank-alias validation branch (Line 84-86 of validate_attribute_mappings).

✅ Suggested addition
         assert!(
             super::validate_attribute_mappings(&[OtlpAttributeMapping::new("", "alias")]).is_err()
         );
+        assert!(
+            super::validate_attribute_mappings(&[OtlpAttributeMapping::new("key", "")]).is_err()
+        );
🤖 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/mod.rs` around lines 425 - 444, Add a case to
the rejects_invalid_attribute_mappings test that passes an OtlpAttributeMapping
with a valid key and blank alias, and assert that validate_attribute_mappings
returns an error, covering the blank-alias branch.
🤖 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/node/tests/openinference_tests.mjs`:
- Around line 20-28: The protobuf encoding helper assertStringAttribute is
duplicated across Node test files. Remove the duplicate definition from the
current test and reuse the shared or existing assertStringAttribute helper from
otel_tests.mjs, preserving all current assertions and call behavior.

In `@crates/node/tests/otel_tests.mjs`:
- Around line 20-28: Remove the duplicate assertStringAttribute
protobuf-encoding helper from the OTLP test and reuse the existing consolidated
helper for string attribute assertions. Update its call sites as needed while
preserving the current assertion behavior and error context.

In `@go/nemo_relay/otel_test.go`:
- Around line 16-25: Update assertOtlpStringAttribute to encode protobuf field
and length values using proper varint encoding rather than byte(len(...)) casts,
including the key length, value length, and enclosing attribute length. Preserve
the existing OTLP byte layout and assertion behavior while ensuring keys and
values of 128 bytes or more are encoded correctly.

In `@python/tests/test_types.py`:
- Line 602: Update the pytest.raises match patterns at both referenced locations
in test_types.py to raw strings, preserving the intentional .* wildcard while
satisfying Ruff RUF043.

---

Outside diff comments:
In `@crates/core/src/observability/mod.rs`:
- Around line 425-444: Add a case to the rejects_invalid_attribute_mappings test
that passes an OtlpAttributeMapping with a valid key and blank alias, and assert
that validate_attribute_mappings returns an error, covering the blank-alias
branch.
🪄 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: 5ad57d28-9691-4c12-8f7c-59e3998d9bf2

📥 Commits

Reviewing files that changed from the base of the PR and between d14422c and a9f926a.

📒 Files selected for processing (9)
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/node/tests/openinference_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • python/tests/test_types.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (26)
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • python/nemo_relay/observability.py
  • crates/node/tests/openinference_tests.mjs
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • python/nemo_relay/observability.py
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_types.py
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • python/nemo_relay/observability.py
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

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

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

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

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_types.py
  • crates/core/src/observability/mod.rs
**/*.{py,go,js,ts}

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

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • python/nemo_relay/observability.py
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • python/tests/test_types.py
**/*

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • crates/node/tests/openinference_tests.mjs
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • python/nemo_relay/observability.py
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
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/observability.py
  • python/nemo_relay/observability.pyi
go/nemo_relay/**/*.go

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

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

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
go/nemo_relay/**

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

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
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/openinference_test.go
  • go/nemo_relay/otel_test.go
{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:

  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • crates/node/tests/openinference_tests.mjs
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • crates/core/tests/unit/observability/otel_tests.rs
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/tests/openinference_tests.mjs
  • crates/node/tests/otel_tests.mjs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: 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/openinference_tests.mjs
  • crates/node/tests/otel_tests.mjs
python/tests/**/*.py

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

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

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

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
{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/otel_tests.rs
  • crates/core/src/observability/mod.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

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
crates/{core,adaptive}/**/*

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

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

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.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/otel_tests.rs
  • crates/core/src/observability/mod.rs
🪛 Ruff (0.15.21)
python/tests/test_types.py

[warning] 602-602: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)


[warning] 707-707: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)

🔇 Additional comments (11)
crates/core/tests/unit/observability/otel_tests.rs (2)

742-757: LGTM!

Also applies to: 855-860, 1085-1094, 1348-1353, 1442-1442, 1567-1572, 1587-1588, 1881-1886


587-691: 🎯 Functional Correctness

No change needed here. These assertions already inspect the raw span.attributes vector, so duplicate aliases would not be hidden by attr_map.

			> Likely an incorrect or invalid review comment.
crates/core/src/observability/mod.rs (2)

76-95: LGTM!


97-208: LGTM!

crates/node/tests/openinference_tests.mjs (1)

45-124: LGTM!

crates/node/tests/otel_tests.mjs (1)

45-120: LGTM!

python/nemo_relay/observability.py (1)

195-230: LGTM!

python/nemo_relay/observability.pyi (1)

74-88: LGTM!

python/tests/test_types.py (1)

95-104: LGTM!

Also applies to: 583-584, 596-607, 636-659, 688-689, 701-712, 748-775

go/nemo_relay/openinference_test.go (1)

94-102: LGTM!

Also applies to: 103-153

go/nemo_relay/otel_test.go (1)

59-62: LGTM! Strengthened assertion at Line 181 now verifies the mapped alias actually reaches the emitted OTLP body, resolving the previously flagged gap.

Also applies to: 132-135, 181-181

Comment thread crates/node/tests/openinference_tests.mjs Outdated
Comment thread crates/node/tests/otel_tests.mjs Outdated
Comment thread go/nemo_relay/otel_test.go
Comment thread python/tests/test_types.py Outdated
Signed-off-by: Will Killian <wkillian@nvidia.com>
Comment thread crates/core/src/observability/otel.rs Outdated
Comment thread crates/core/src/observability/otel.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
crates/core/tests/unit/observability/otel_tests.rs (1)

742-757: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert that the legacy *_json attributes are absent.

These updates only prove that the new flattened keys exist; a regression emitting both typed and legacy attributes would still pass. Add negative assertions for the removed raw JSON keys, following the explicit handle_attributes_json absence check at Lines 1348-1353.

As per path instructions, tests should cover the behavior promised by the changed API surface, including the flattened schema migration.

Also applies to: 855-860, 1085-1094, 1567-1572, 1587-1588, 1881-1886

🤖 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/tests/unit/observability/otel_tests.rs` around lines 742 - 757,
The observability tests only verify flattened attributes and must also verify
legacy JSON attributes are absent. Update the affected assertions in the
relevant test cases around the span and event attribute maps to add negative
checks for each corresponding *_json key, following the existing
handle_attributes_json absence assertion pattern and covering all listed
locations.

Source: Path instructions

🤖 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 `@python/tests/test_types.py`:
- Around line 95-103: Update _otlp_string_attribute to calculate field lengths
from the UTF-8 encoded key and value bytes, then encode all protobuf length
prefixes using varint encoding rather than single-byte bytes([...]) values.
Preserve the existing OTLP field structure while supporting non-ASCII strings
and payloads of 128 bytes or more.

---

Outside diff comments:
In `@crates/core/tests/unit/observability/otel_tests.rs`:
- Around line 742-757: The observability tests only verify flattened attributes
and must also verify legacy JSON attributes are absent. Update the affected
assertions in the relevant test cases around the span and event attribute maps
to add negative checks for each corresponding *_json key, following the existing
handle_attributes_json absence assertion pattern and covering all listed
locations.
🪄 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: de3b41b7-1453-407b-bd2e-9c0432caa7fe

📥 Commits

Reviewing files that changed from the base of the PR and between d14422c and a5e7b33.

📒 Files selected for processing (12)
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/node/tests/openinference_tests.mjs
  • crates/node/tests/otel_tests.mjs
  • go/nemo_relay/openinference_test.go
  • go/nemo_relay/otel_test.go
  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • python/tests/test_types.py
  • scripts/test-support/otel_test_utils.mjs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (28)
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

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

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • python/nemo_relay/observability.pyi
  • python/nemo_relay/observability.py
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

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

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • python/nemo_relay/observability.pyi
  • python/nemo_relay/observability.py
  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
**/*

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • python/nemo_relay/observability.pyi
  • python/nemo_relay/observability.py
  • scripts/test-support/otel_test_utils.mjs
  • go/nemo_relay/otel_test.go
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • go/nemo_relay/openinference_test.go
  • crates/node/tests/openinference_tests.mjs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
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/observability.pyi
  • python/nemo_relay/observability.py
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • python/nemo_relay/observability.py
  • scripts/test-support/otel_test_utils.mjs
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • crates/node/tests/openinference_tests.mjs
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • python/nemo_relay/observability.py
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_types.py
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • python/nemo_relay/observability.py
  • go/nemo_relay/otel_test.go
  • python/tests/test_types.py
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • python/nemo_relay/observability.py
  • python/tests/test_types.py
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
**/*.{py,go,js,ts}

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

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • python/nemo_relay/observability.py
  • go/nemo_relay/otel_test.go
  • python/tests/test_types.py
  • go/nemo_relay/openinference_test.go
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • python/nemo_relay/observability.py
  • go/nemo_relay/otel_test.go
  • python/tests/test_types.py
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{.github/**,.gitlab-ci.yml,.pre-commit-config.yaml,justfile,scripts/**}

⚙️ CodeRabbit configuration file

{.github/**,.gitlab-ci.yml,.pre-commit-config.yaml,justfile,scripts/**}: Review automation changes for reproducibility, pinned versions where appropriate, secret handling, and consistency with the documented validation matrix.
Pay attention to commands that need generated native artifacts, FFI libraries, or platform-specific environment variables.

Files:

  • scripts/test-support/otel_test_utils.mjs
go/nemo_relay/**/*.go

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

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

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
go/nemo_relay/**

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

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/otel_test.go
  • go/nemo_relay/openinference_test.go
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/otel_test.go
  • go/nemo_relay/openinference_test.go
{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:

  • go/nemo_relay/otel_test.go
  • python/tests/test_types.py
  • crates/node/tests/otel_tests.mjs
  • go/nemo_relay/openinference_test.go
  • crates/node/tests/openinference_tests.mjs
  • crates/core/tests/unit/observability/otel_tests.rs
python/tests/**/*.py

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

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

  • python/tests/test_types.py
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/tests/otel_tests.mjs
  • crates/node/tests/openinference_tests.mjs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: 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/otel_tests.mjs
  • crates/node/tests/openinference_tests.mjs
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
{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/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
crates/{core,adaptive}/**/*

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

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

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.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/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
crates/core/src/observability/{atif,otel,openinference}.rs

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

When changing the core event model, emitted fields, exporter behavior, subscriber config, or lifecycle, keep the ATIF, OpenTelemetry, and OpenInference implementations in sync.

Files:

  • crates/core/src/observability/otel.rs
  • crates/core/src/observability/openinference.rs
🔇 Additional comments (17)
crates/node/tests/openinference_tests.mjs (1)

7-7: LGTM!

Also applies to: 35-35, 72-79, 82-92, 103-103, 114-114

crates/node/tests/otel_tests.mjs (1)

7-7: LGTM!

Also applies to: 16-19, 35-35, 72-79, 81-92, 110-110

crates/core/tests/unit/observability/otel_tests.rs (1)

587-691: LGTM!

Also applies to: 1348-1353, 1442-1442

scripts/test-support/otel_test_utils.mjs (1)

8-34: LGTM!

Also applies to: 98-100, 118-120

go/nemo_relay/openinference_test.go (1)

55-58: LGTM!

Also applies to: 94-102, 103-150

go/nemo_relay/otel_test.go (1)

7-8: LGTM!

Also applies to: 17-30, 64-67, 103-111, 137-140, 183-186

python/nemo_relay/observability.py (1)

210-210: LGTM!

Also applies to: 219-219

python/nemo_relay/observability.pyi (1)

87-87: LGTM!

python/tests/test_types.py (1)

583-584: LGTM!

Also applies to: 596-607, 631-662, 688-689, 701-712, 743-778

crates/core/src/observability/mod.rs (3)

76-95: LGTM!


183-207: LGTM!


425-445: LGTM!

crates/core/src/observability/openinference.rs (3)

599-615: LGTM!


617-636: LGTM!


617-636: 🗄️ Data Integrity & Integration

ATIF does not need a parallel attribute_mappings surface. crates/core/src/observability/atif.rs only exposes AtifExporter::new(session_id, agent_info) and exports structured ATIF trajectory fields, so the OTLP mapping/config changes in OpenTelemetry/OpenInference have no ATIF counterpart here.

			> Likely an incorrect or invalid review comment.
crates/core/src/observability/otel.rs (2)

592-608: LGTM!


610-630: LGTM!

Comment thread python/tests/test_types.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (4)
crates/core/src/observability/otel.rs (2)

239-370: 📐 Maintainability & Code Quality | 🔵 Trivial

Constructor surface keeps growing alongside the new composable options API.

Same pattern as openinference.rs: from_tracer_provider_with_options is additive, but the four older constructors remain, continuing the constructor-sprawl concern raised in an earlier review round on this file (line 334).

See consolidated comment for the cross-file fix.

🤖 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/otel.rs` around lines 239 - 370, Consolidate
the OpenTelemetry subscriber constructors around
from_tracer_provider_with_options: remove the older convenience constructors
from_tracer_provider, from_tracer_provider_with_mark_projection,
from_tracer_provider_with_mark_projection_and_exclusions, and
from_tracer_provider_with_attribute_mappings, and have callers construct
OpenTelemetrySubscriberOptions explicitly while preserving their existing
defaults and validation behavior.

650-659: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

process_end clones the full end-attribute set instead of filtering like process_start does.

projected_attributes.extend(attributes.iter().cloned()) (line 653) clones every end attribute, undoing the exact memory-bloat fix (attribute_mapping_inputs) applied one call earlier on the start path (line 630).

See consolidated comment for the proposed fix (shared with openinference.rs).

🤖 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/otel.rs` around lines 650 - 659, Update
process_end so projected_attributes receives only the filtered attribute-mapping
inputs, matching process_start’s use of attribute_mapping_inputs, rather than
cloning every end attribute via attributes.iter().cloned(). Preserve alias
generation through attribute_mapping_aliases and continue applying the complete
end attributes to the span.
crates/core/src/observability/openinference.rs (2)

656-665: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

process_end clones the full end-attribute set instead of filtering like process_start does.

projected_attributes.extend(attributes.iter().cloned()) clones every end attribute (including large payloads) to compute aliases, undoing the memory-bloat fix (attribute_mapping_inputs) just applied on the start side.

See consolidated comment for the proposed fix (shared with otel.rs).

🤖 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/openinference.rs` around lines 656 - 665,
Update process_end to compute attribute-mapping aliases using only the required
attribute-mapping inputs, matching process_start, instead of cloning the full
end_attributes set into projected_attributes. Reuse the existing
attribute_mapping_inputs filtering approach and preserve setting the complete
end attribute set on the span.

245-375: 📐 Maintainability & Code Quality | 🔵 Trivial

Constructor surface keeps growing alongside the new composable options API.

from_tracer_provider_with_options is a good addition, but from_tracer_provider, _with_mark_projection, _with_mark_projection_and_exclusions, and _with_attribute_mappings remain as separate public constructors doing overlapping work. This is a continuation of the earlier open discussion about constructor sprawl.

See consolidated comment for the cross-file fix.

🤖 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/openinference.rs` around lines 245 - 375,
Consolidate the overlapping constructors into from_tracer_provider_with_options
and remove the separate public from_tracer_provider,
from_tracer_provider_with_mark_projection,
from_tracer_provider_with_mark_projection_and_exclusions, and
from_tracer_provider_with_attribute_mappings methods. Update their callers to
construct OpenInferenceSubscriberOptions with equivalent defaults and settings,
preserving validation and existing behavior.
🤖 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.

Outside diff comments:
In `@crates/core/src/observability/openinference.rs`:
- Around line 656-665: Update process_end to compute attribute-mapping aliases
using only the required attribute-mapping inputs, matching process_start,
instead of cloning the full end_attributes set into projected_attributes. Reuse
the existing attribute_mapping_inputs filtering approach and preserve setting
the complete end attribute set on the span.
- Around line 245-375: Consolidate the overlapping constructors into
from_tracer_provider_with_options and remove the separate public
from_tracer_provider, from_tracer_provider_with_mark_projection,
from_tracer_provider_with_mark_projection_and_exclusions, and
from_tracer_provider_with_attribute_mappings methods. Update their callers to
construct OpenInferenceSubscriberOptions with equivalent defaults and settings,
preserving validation and existing behavior.

In `@crates/core/src/observability/otel.rs`:
- Around line 239-370: Consolidate the OpenTelemetry subscriber constructors
around from_tracer_provider_with_options: remove the older convenience
constructors from_tracer_provider, from_tracer_provider_with_mark_projection,
from_tracer_provider_with_mark_projection_and_exclusions, and
from_tracer_provider_with_attribute_mappings, and have callers construct
OpenTelemetrySubscriberOptions explicitly while preserving their existing
defaults and validation behavior.
- Around line 650-659: Update process_end so projected_attributes receives only
the filtered attribute-mapping inputs, matching process_start’s use of
attribute_mapping_inputs, rather than cloning every end attribute via
attributes.iter().cloned(). Preserve alias generation through
attribute_mapping_aliases and continue applying the complete end attributes to
the span.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 390f9ef2-968e-4423-bb9e-bcfff6aa9575

📥 Commits

Reviewing files that changed from the base of the PR and between a5e7b33 and c98df7b.

📒 Files selected for processing (6)
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/tests/unit/observability/otel_tests.rs
  • python/tests/test_types.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/tests/test_types.py
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • python/tests/test_types.py
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
**/*.{py,go,js,ts}

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

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

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

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

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

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

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.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:

  • python/tests/test_types.py
  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
{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/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.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

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
crates/{core,adaptive}/**/*

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

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

Files:

  • crates/core/tests/unit/observability/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.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/otel_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/tests/unit/observability/openinference_tests.rs
  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
crates/core/src/observability/{atif,otel,openinference}.rs

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

When changing the core event model, emitted fields, exporter behavior, subscriber config, or lifecycle, keep the ATIF, OpenTelemetry, and OpenInference implementations in sync.

Files:

  • crates/core/src/observability/openinference.rs
  • crates/core/src/observability/otel.rs
🔇 Additional comments (6)
python/tests/test_types.py (1)

95-108: LGTM!

Also applies to: 588-612, 636-668, 694-718, 749-782

crates/core/tests/unit/observability/otel_tests.rs (1)

587-671: LGTM!

Also applies to: 764-779, 877-882, 1107-1116, 1370-1375, 1464-1464, 1589-1594, 1609-1610, 1903-1908

crates/core/src/observability/mod.rs (1)

178-198: LGTM! The new attribute_mapping_inputs filter is exactly the targeted retention fix requested in earlier feedback (only mapping-relevant start attributes are kept instead of the full attribute set), and the new test directly verifies it.

Also applies to: 384-494

crates/core/tests/unit/observability/openinference_tests.rs (1)

690-816: LGTM!

Also applies to: 873-900, 1001-1006, 1076-1097, 1276-1276, 1984-1989, 2422-2431, 2793-2798, 2840-2845, 2982-2985, 3057-3062, 3969-3975, 4065-4083

crates/core/src/observability/openinference.rs (1)

628-648: LGTM! process_start's filtered retention and the mark-projection paths' ordering (intrinsic fields pushed before apply_attribute_mappings) correctly prevent mapped aliases from overwriting intrinsic fields.

Also applies to: 671-731

crates/core/src/observability/otel.rs (1)

621-641: LGTM!

Also applies to: 665-718

@mnajafian-nv mnajafian-nv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

Comment thread crates/core/src/observability/mod.rs Outdated
Comment thread crates/core/src/observability/plugin_component.rs
Signed-off-by: Will Killian <wkillian@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/core/tests/unit/observability/attribute_projection_tests.rs`:
- Around line 36-112: Add coverage in
projects_typed_json_and_copies_configured_aliases for a top-level boolean
attribute and confirm a null-valued attribute is omitted from the projected
results. Extend rejects_invalid_attribute_mappings to assert that a
whitespace-only alias is rejected, using the existing validation and projection
helpers without changing their behavior.
🪄 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: e4657fee-10ea-4e2a-b084-de1d8bece58f

📥 Commits

Reviewing files that changed from the base of the PR and between c98df7b and b70841b.

📒 Files selected for processing (4)
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (13)
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
{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/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.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

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
**/*

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.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 validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.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/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.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/core/tests/unit/observability/attribute_projection_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/core/src/observability/mod.rs
  • crates/core/src/observability/plugin_component.rs
🔇 Additional comments (3)
crates/core/src/observability/mod.rs (1)

76-95: 📐 Maintainability & Code Quality

Run the required core validation matrix.

Please provide successful results for cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, cargo deny check, validate-change, the Rust/Python/Go/Node matrix, and final uv run pre-commit run --all-files. As per coding guidelines, “If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.”

Also applies to: 171-197

Source: Coding guidelines

crates/core/src/observability/plugin_component.rs (1)

392-392: LGTM!

Also applies to: 424-439, 506-536, 1390-1425, 1428-1465, 1561-1597, 2140-2165

crates/core/tests/unit/observability/plugin_component_tests.rs (1)

84-95: LGTM!

Also applies to: 233-237, 257-348

Comment thread crates/core/tests/unit/observability/attribute_projection_tests.rs
@willkill07

Copy link
Copy Markdown
Member Author

/ok to test e24f692

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/core/tests/unit/observability/attribute_projection_tests.rs`:
- Around line 73-77: Update the assertion for nemo_relay.start.metadata.enabled
in the observability attribute projection test to compare against
opentelemetry::Value::Bool(true) rather than a stringified "true" value. Keep
the existing unset-attribute assertion unchanged.
🪄 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: 62fb72a5-ae8b-41f2-af10-3baa1fdd03f1

📥 Commits

Reviewing files that changed from the base of the PR and between b70841b and dcea96c.

📒 Files selected for processing (1)
  • crates/core/tests/unit/observability/attribute_projection_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (12)
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
{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/attribute_projection_tests.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

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
crates/{core,adaptive}/**/*

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

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

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/tests/unit/observability/attribute_projection_tests.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/attribute_projection_tests.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/core/tests/unit/observability/attribute_projection_tests.rs
🔇 Additional comments (2)
crates/core/tests/unit/observability/attribute_projection_tests.rs (2)

44-45: LGTM!

Also applies to: 119-121


44-45: 📐 Maintainability & Code Quality

Verify the required Rust validation before handoff.

Run cargo fmt --all, just test-rust, cargo clippy --workspace --all-targets -- -D warnings, uv run pre-commit run --all-files, and the required crates/core broader validation/full language matrix. As per coding guidelines, Rust changes require these formatting, lint, test, and core validation checks.

Also applies to: 73-77, 119-121

Source: Coding guidelines

Comment thread crates/core/tests/unit/observability/attribute_projection_tests.rs Outdated
Signed-off-by: Will Killian <wkillian@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
crates/ffi/tests/unit/api/plugin_tests.rs (1)

1548-1582: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover malformed mapping JSON separately.

Line 1548 covers valid JSON with invalid shapes, but neither constructor is tested with malformed JSON and expected InvalidJson. Add "not-json" coverage for both APIs so the status distinction cannot regress.

🤖 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/tests/unit/api/plugin_tests.rs` around lines 1548 - 1582, Extend
the tests around nemo_relay_otel_subscriber_create_with_attribute_mappings and
nemo_relay_openinference_subscriber_create_with_attribute_mappings with a
separate malformed JSON input of "not-json". Assert both constructors return
NemoRelayStatus::InvalidJson, while preserving the existing InvalidArg
assertions for valid JSON with invalid shapes.

Source: Path instructions

python/tests/test_observability_plugin.py (1)

40-50: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Cover non-empty attribute-mapping serialization.

The current assertion verifies only the empty default. A regression that drops configured mappings from OtlpConfig.to_dict() would still pass.

Suggested test
         assert OtlpConfig().to_dict() == {
             "enabled": False,
             "mark_projection": "inherit",
             "mark_exclude_names": ["llm.chunk"],
             "attribute_mappings": [],
             "transport": "http_binary",
             "headers": {},
             "resource_attributes": {},
             "service_name": "nemo-relay",
             "timeout_millis": 3000,
         }
+        mappings = [{"key": "nemo_relay.start.metadata.tenant", "alias": "tenant.id"}]
+        assert OtlpConfig(attribute_mappings=mappings).to_dict()["attribute_mappings"] == mappings

As per path instructions, tests should cover the behavior promised by the changed API surface, including error paths.

🤖 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 `@python/tests/test_observability_plugin.py` around lines 40 - 50, Extend the
OtlpConfig serialization test around OtlpConfig.to_dict() with a configuration
containing at least one non-empty attribute mapping, then assert the serialized
attribute_mappings preserves that mapping and its values. Keep the existing
empty-default assertion unchanged.

Source: Path instructions

🤖 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 `@python/nemo_relay/observability.py`:
- Line 88: Update the sinks annotation in the relevant observability
configuration to remove the unnecessary quotes around the union type, using the
native union expression directly while preserving the existing list and None
typing.

---

Outside diff comments:
In `@crates/ffi/tests/unit/api/plugin_tests.rs`:
- Around line 1548-1582: Extend the tests around
nemo_relay_otel_subscriber_create_with_attribute_mappings and
nemo_relay_openinference_subscriber_create_with_attribute_mappings with a
separate malformed JSON input of "not-json". Assert both constructors return
NemoRelayStatus::InvalidJson, while preserving the existing InvalidArg
assertions for valid JSON with invalid shapes.

In `@python/tests/test_observability_plugin.py`:
- Around line 40-50: Extend the OtlpConfig serialization test around
OtlpConfig.to_dict() with a configuration containing at least one non-empty
attribute mapping, then assert the serialized attribute_mappings preserves that
mapping and its values. Keep the existing empty-default assertion unchanged.
🪄 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: 95527e4b-910e-4be8-b15b-90b5e3b15d0f

📥 Commits

Reviewing files that changed from the base of the PR and between dcea96c and e24f692.

📒 Files selected for processing (18)
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/node/src/api/mod.rs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/python/src/py_types/observability.rs
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • 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
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (32)
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • python/tests/test_observability_plugin.py
  • python/nemo_relay/observability.py
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/src/api/observability.rs
  • python/tests/test_types.py
  • crates/python/src/py_types/observability.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: 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, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • python/tests/test_observability_plugin.py
  • crates/node/observability.js
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/observability.d.ts
  • python/nemo_relay/observability.py
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/src/api/observability.rs
  • python/tests/test_types.py
  • crates/python/src/py_types/observability.rs
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/tests/test_observability_plugin.py
  • python/nemo_relay/observability.py
  • python/tests/test_types.py
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • python/tests/test_observability_plugin.py
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • python/nemo_relay/observability.py
  • go/nemo_relay/observability_plugin.go
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/src/api/observability.rs
  • python/tests/test_types.py
  • go/nemo_relay/nemo_relay.go
  • crates/python/src/py_types/observability.rs
{crates/**/src/**/*.rs,python/**/*.py}

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

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • python/tests/test_observability_plugin.py
  • python/nemo_relay/observability.py
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/ffi/src/api/observability.rs
  • python/tests/test_types.py
  • crates/python/src/py_types/observability.rs
**/*.{py,go,js,ts}

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

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • python/tests/test_observability_plugin.py
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • python/nemo_relay/observability.py
  • go/nemo_relay/observability_plugin.go
  • python/tests/test_types.py
  • go/nemo_relay/nemo_relay.go
python/tests/**/*.py

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

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

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

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

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • python/tests/test_observability_plugin.py
  • crates/node/observability.js
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/observability.d.ts
  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • go/nemo_relay/observability_plugin.go
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/src/api/mod.rs
  • python/nemo_relay/_native.pyi
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/src/api/observability.rs
  • python/tests/test_types.py
  • go/nemo_relay/nemo_relay.go
  • crates/python/src/py_types/observability.rs
**/*.{rs,py,go,js,ts}

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

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • python/tests/test_observability_plugin.py
  • crates/node/observability.js
  • crates/node/observability.d.ts
  • python/nemo_relay/observability.py
  • go/nemo_relay/observability_plugin.go
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/src/api/observability.rs
  • python/tests/test_types.py
  • go/nemo_relay/nemo_relay.go
  • crates/python/src/py_types/observability.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:

  • python/tests/test_observability_plugin.py
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • python/tests/test_types.py
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.js
  • crates/node/observability.d.ts
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/observability.js
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/observability.d.ts
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/node/observability.js
  • crates/node/observability.d.ts
  • go/nemo_relay/observability_plugin.go
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/src/api/observability.rs
  • go/nemo_relay/nemo_relay.go
  • crates/python/src/py_types/observability.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: 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/observability.js
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/node/observability.d.ts
  • crates/ffi/nemo_relay.h
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/src/api/mod.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
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
python/nemo_relay/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • python/nemo_relay/observability.py
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

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

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • go/nemo_relay/observability_plugin.go
  • crates/node/src/api/mod.rs
  • python/nemo_relay/_native.pyi
  • go/nemo_relay/nemo_relay.go
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

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

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • python/nemo_relay/observability.py
  • python/nemo_relay/observability.pyi
  • go/nemo_relay/observability_plugin.go
  • python/nemo_relay/_native.pyi
  • go/nemo_relay/nemo_relay.go
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/observability.py
  • python/nemo_relay/observability.pyi
  • python/nemo_relay/_native.pyi
go/nemo_relay/**/*.go

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

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

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
go/nemo_relay/**

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

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/observability_plugin.go
  • go/nemo_relay/nemo_relay.go
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.go
  • go/nemo_relay/nemo_relay.go
**/*.rs

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

**/*.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

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.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.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/src/api/mod.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/src/api/observability.rs
  • crates/python/src/py_types/observability.rs
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/nemo_relay.h
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
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

Update generated or generated-from-build surfaces such as crates/ffi/nemo_relay.h through the proper build step.

Files:

  • crates/ffi/nemo_relay.h
{crates/ffi/src/api/*.rs,crates/ffi/nemo_relay.h}

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

Add or update the shared C/FFI surface in the relevant crates/ffi/src/api/*.rs module, re-export it through crates/ffi/src/api/mod.rs, and keep the generated crates/ffi/nemo_relay.h header correct.

Files:

  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
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

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/ffi/src/api/observability.rs
{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/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.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

Files:

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

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

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

Files:

  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.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/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
🧠 Learnings (3)
📚 Learning: 2026-07-14T02:53:44.529Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Relay PR: 414
File: crates/node/tests/observability_plugin_tests.mjs:34-34
Timestamp: 2026-07-14T02:53:44.529Z
Learning: Do not flag camelCase style violations for keys returned by observability plugin configuration helpers (e.g., `observability.otlpConfig()` and similar helpers like `atofConfig()` / `atifConfig()`) in the Node observability module and its tests. These helpers intentionally return the snake_case plugin configuration schema consumed by `plugin.initialize()` and written/read via TOML. This is distinct from the Node public API / native subscriber options (e.g., fields like `attributeMappings`) which follow the camelCase guideline; only the plugin-config schema helpers should be exempt.

Applied to files:

  • crates/node/observability.js
  • crates/node/tests/observability_plugin_tests.mjs
📚 Learning: 2026-07-14T02:53:55.471Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Relay PR: 414
File: crates/node/observability.d.ts:61-61
Timestamp: 2026-07-14T02:53:55.471Z
Learning: In `crates/node/observability.d.ts` and `crates/node/observability.js`, treat `OtlpConfig`/`otlpConfig` and related helpers as an intentional mirror of the snake_case TOML/plugin configuration schema consumed by `plugin.initialize()`. Do not apply the usual “Node.js public APIs use camelCase” naming review expectation to this plugin-config schema surface. Instead, camelCase review expectations should apply to the native binding surface (e.g., `OpenTelemetrySubscriber`/`OpenInferenceSubscriber` constructors and their `attributeMappings`), which expose camelCase separately.

Applied to files:

  • crates/node/observability.js
  • crates/node/observability.d.ts
📚 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
🪛 ast-grep (0.44.1)
go/nemo_relay/nemo_relay.go

[warning] 347-347: Narrowing a non-constant integer to a smaller fixed-width type (int8/int16/int32, uint8/uint16/uint32) can silently overflow or wrap, yielding negative or truncated values that are dangerous in size, length, or index logic. Validate the source value is within the target type's range before converting (e.g. bounds-check, or use a checked helper), and avoid narrowing untrusted or len()/parsed values.
Context: int32(status)
Note: [CWE-190] Integer Overflow or Wraparound.

(integer-overflow-narrowing-conversion-go)

🪛 Ruff (0.15.21)
python/nemo_relay/observability.py

[warning] 88-88: Remove quotes from type annotation

Remove quotes

(UP037)

🔇 Additional comments (22)
crates/core/src/observability/plugin_component.rs (2)

38-46: 📐 Maintainability & Code Quality

Run the required Rust validation matrix before merge.

No validation output is included for this crates/core change. Confirm cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, cargo deny check, validate-change, the full language matrix, and final pre-commit pass. As per coding guidelines, “Changes to crates/core ... must run the full validation matrix across Rust, Python, Go, and Node.js.”

Source: Coding guidelines


152-220: LGTM!

Also applies to: 389-446, 492-571, 738-826, 1498-1499, 1538-1539, 1617-1704, 1734-1753, 1877-1889, 1926-2174, 2427-2436, 2490-2491

crates/core/tests/unit/observability/plugin_component_tests.rs (1)

21-90: LGTM!

Also applies to: 119-159, 265-330, 376-412, 417-488, 534-758, 760-986

crates/cli/tests/coverage/plugins_tests.rs (1)

12-14: LGTM!

Also applies to: 188-220, 659-679, 682-702, 775-801, 824-889, 1485-1513, 1992-2021, 2291-2304, 2582-2599

crates/ffi/nemo_relay.h (1)

1332-1350: LGTM!

Also applies to: 1406-1424

crates/ffi/src/api/observability.rs (1)

537-557: LGTM!

Also applies to: 790-794, 1021-1025

go/nemo_relay/nemo_relay.go (1)

254-263: LGTM!

Also applies to: 1873-1879, 1943-1952, 1972-1982, 2055-2055, 2119-2128, 2148-2158

go/nemo_relay/observability_plugin.go (1)

187-220: LGTM!

crates/node/observability.d.ts (1)

9-34: LGTM!

Also applies to: 72-72, 84-89

crates/node/observability.js (1)

15-18: LGTM!

Also applies to: 61-61

crates/node/src/api/mod.rs (5)

92-105: 🩺 Stability & Availability

Guard optional Symbol.asyncDispose support.

Module initialization currently errors if the host runtime does not expose Symbol.asyncDispose, preventing the native module from loading even when dynamic disposal is unused. Guard the property installation or enforce an engine requirement that guarantees the symbol exists.


235-248: 🩺 Stability & Availability

Reject blank stream URLs at the Node boundary.

This checks only that url is present, so an empty or whitespace-only URL reaches AtofStreamSinkConfig::new. The Python binding explicitly rejects blank URLs; mirror that validation here or verify that AtofExporter::new guarantees identical behavior.

Source: Path instructions


49-52: LGTM!

Also applies to: 131-145, 206-206, 324-324, 4066-4089, 4092-4306


215-234: LGTM!

Also applies to: 249-274, 3432-3437


3389-3412: 📐 Maintainability & Code Quality

No change needed — the JS-facing AtofExporter API already uses camelCase options (outputDirectory, timeoutMillis, headerEnv, fieldNamePolicy, attributeMappings); the snake_case Rust fields here are binding internals.

			> Likely an incorrect or invalid review comment.
crates/node/tests/observability_plugin_tests.mjs (1)

22-23: LGTM!

Also applies to: 34-34, 43-43, 54-60, 63-67, 77-79, 112-115, 164-164

crates/python/src/py_types/observability.rs (1)

17-31: LGTM!

Also applies to: 445-445, 483-483, 502-502, 534-550, 644-644, 682-682, 701-701, 733-749

python/nemo_relay/_native.pyi (1)

833-898: LGTM!

Also applies to: 975-982, 1055-1062, 2276-2312

python/nemo_relay/observability.py (1)

58-87: LGTM!

Also applies to: 89-120, 230-230, 239-239, 257-257, 297-301

python/nemo_relay/observability.pyi (1)

23-46: LGTM!

Also applies to: 94-94

python/tests/test_observability_plugin.py (1)

18-19: LGTM!

Also applies to: 31-39, 53-58, 60-75, 105-123, 145-155

python/tests/test_types.py (1)

94-109: LGTM!

Also applies to: 591-592, 604-615, 641-668, 697-698, 710-721, 754-785

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
crates/ffi/tests/unit/api/plugin_tests.rs (1)

1548-1582: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover malformed mapping JSON separately.

Line 1548 covers valid JSON with invalid shapes, but neither constructor is tested with malformed JSON and expected InvalidJson. Add "not-json" coverage for both APIs so the status distinction cannot regress.

🤖 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/tests/unit/api/plugin_tests.rs` around lines 1548 - 1582, Extend
the tests around nemo_relay_otel_subscriber_create_with_attribute_mappings and
nemo_relay_openinference_subscriber_create_with_attribute_mappings with a
separate malformed JSON input of "not-json". Assert both constructors return
NemoRelayStatus::InvalidJson, while preserving the existing InvalidArg
assertions for valid JSON with invalid shapes.

Source: Path instructions

python/tests/test_observability_plugin.py (1)

40-50: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Cover non-empty attribute-mapping serialization.

The current assertion verifies only the empty default. A regression that drops configured mappings from OtlpConfig.to_dict() would still pass.

Suggested test
         assert OtlpConfig().to_dict() == {
             "enabled": False,
             "mark_projection": "inherit",
             "mark_exclude_names": ["llm.chunk"],
             "attribute_mappings": [],
             "transport": "http_binary",
             "headers": {},
             "resource_attributes": {},
             "service_name": "nemo-relay",
             "timeout_millis": 3000,
         }
+        mappings = [{"key": "nemo_relay.start.metadata.tenant", "alias": "tenant.id"}]
+        assert OtlpConfig(attribute_mappings=mappings).to_dict()["attribute_mappings"] == mappings

As per path instructions, tests should cover the behavior promised by the changed API surface, including error paths.

🤖 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 `@python/tests/test_observability_plugin.py` around lines 40 - 50, Extend the
OtlpConfig serialization test around OtlpConfig.to_dict() with a configuration
containing at least one non-empty attribute mapping, then assert the serialized
attribute_mappings preserves that mapping and its values. Keep the existing
empty-default assertion unchanged.

Source: Path instructions

🤖 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 `@python/nemo_relay/observability.py`:
- Line 88: Update the sinks annotation in the relevant observability
configuration to remove the unnecessary quotes around the union type, using the
native union expression directly while preserving the existing list and None
typing.

---

Outside diff comments:
In `@crates/ffi/tests/unit/api/plugin_tests.rs`:
- Around line 1548-1582: Extend the tests around
nemo_relay_otel_subscriber_create_with_attribute_mappings and
nemo_relay_openinference_subscriber_create_with_attribute_mappings with a
separate malformed JSON input of "not-json". Assert both constructors return
NemoRelayStatus::InvalidJson, while preserving the existing InvalidArg
assertions for valid JSON with invalid shapes.

In `@python/tests/test_observability_plugin.py`:
- Around line 40-50: Extend the OtlpConfig serialization test around
OtlpConfig.to_dict() with a configuration containing at least one non-empty
attribute mapping, then assert the serialized attribute_mappings preserves that
mapping and its values. Keep the existing empty-default assertion unchanged.
🪄 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: 95527e4b-910e-4be8-b15b-90b5e3b15d0f

📥 Commits

Reviewing files that changed from the base of the PR and between dcea96c and e24f692.

📒 Files selected for processing (18)
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/core/src/observability/plugin_component.rs
  • crates/core/tests/unit/observability/plugin_component_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/observability.rs
  • crates/ffi/tests/unit/api/plugin_tests.rs
  • crates/node/observability.d.ts
  • crates/node/observability.js
  • crates/node/src/api/mod.rs
  • crates/node/tests/observability_plugin_tests.mjs
  • crates/python/src/py_types/observability.rs
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/observability_plugin.go
  • 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 comments (22)
crates/core/src/observability/plugin_component.rs (2)

38-46: 📐 Maintainability & Code Quality

Run the required Rust validation matrix before merge.

No validation output is included for this crates/core change. Confirm cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, cargo deny check, validate-change, the full language matrix, and final pre-commit pass. As per coding guidelines, “Changes to crates/core ... must run the full validation matrix across Rust, Python, Go, and Node.js.”

Source: Coding guidelines


152-220: LGTM!

Also applies to: 389-446, 492-571, 738-826, 1498-1499, 1538-1539, 1617-1704, 1734-1753, 1877-1889, 1926-2174, 2427-2436, 2490-2491

crates/core/tests/unit/observability/plugin_component_tests.rs (1)

21-90: LGTM!

Also applies to: 119-159, 265-330, 376-412, 417-488, 534-758, 760-986

crates/cli/tests/coverage/plugins_tests.rs (1)

12-14: LGTM!

Also applies to: 188-220, 659-679, 682-702, 775-801, 824-889, 1485-1513, 1992-2021, 2291-2304, 2582-2599

crates/ffi/nemo_relay.h (1)

1332-1350: LGTM!

Also applies to: 1406-1424

crates/ffi/src/api/observability.rs (1)

537-557: LGTM!

Also applies to: 790-794, 1021-1025

go/nemo_relay/nemo_relay.go (1)

254-263: LGTM!

Also applies to: 1873-1879, 1943-1952, 1972-1982, 2055-2055, 2119-2128, 2148-2158

go/nemo_relay/observability_plugin.go (1)

187-220: LGTM!

crates/node/observability.d.ts (1)

9-34: LGTM!

Also applies to: 72-72, 84-89

crates/node/observability.js (1)

15-18: LGTM!

Also applies to: 61-61

crates/node/src/api/mod.rs (5)

92-105: 🩺 Stability & Availability

Guard optional Symbol.asyncDispose support.

Module initialization currently errors if the host runtime does not expose Symbol.asyncDispose, preventing the native module from loading even when dynamic disposal is unused. Guard the property installation or enforce an engine requirement that guarantees the symbol exists.


235-248: 🩺 Stability & Availability

Reject blank stream URLs at the Node boundary.

This checks only that url is present, so an empty or whitespace-only URL reaches AtofStreamSinkConfig::new. The Python binding explicitly rejects blank URLs; mirror that validation here or verify that AtofExporter::new guarantees identical behavior.

Source: Path instructions


49-52: LGTM!

Also applies to: 131-145, 206-206, 324-324, 4066-4089, 4092-4306


215-234: LGTM!

Also applies to: 249-274, 3432-3437


3389-3412: 📐 Maintainability & Code Quality

No change needed — the JS-facing AtofExporter API already uses camelCase options (outputDirectory, timeoutMillis, headerEnv, fieldNamePolicy, attributeMappings); the snake_case Rust fields here are binding internals.

			> Likely an incorrect or invalid review comment.
crates/node/tests/observability_plugin_tests.mjs (1)

22-23: LGTM!

Also applies to: 34-34, 43-43, 54-60, 63-67, 77-79, 112-115, 164-164

crates/python/src/py_types/observability.rs (1)

17-31: LGTM!

Also applies to: 445-445, 483-483, 502-502, 534-550, 644-644, 682-682, 701-701, 733-749

python/nemo_relay/_native.pyi (1)

833-898: LGTM!

Also applies to: 975-982, 1055-1062, 2276-2312

python/nemo_relay/observability.py (1)

58-87: LGTM!

Also applies to: 89-120, 230-230, 239-239, 257-257, 297-301

python/nemo_relay/observability.pyi (1)

23-46: LGTM!

Also applies to: 94-94

python/tests/test_observability_plugin.py (1)

18-19: LGTM!

Also applies to: 31-39, 53-58, 60-75, 105-123, 145-155

python/tests/test_types.py (1)

94-109: LGTM!

Also applies to: 591-592, 604-615, 641-668, 697-698, 710-721, 754-785

🛑 Comments failed to post (1)
python/nemo_relay/observability.py (1)

88-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unnecessary quoted union annotation.

Ruff UP037 flags this annotation. Use the native union expression directly:

Suggested fix
-    sinks: list["AtofFileSinkConfig | AtofStreamSinkConfig"] | None = None
+    sinks: list[AtofFileSinkConfig | AtofStreamSinkConfig] | None = None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    sinks: list[AtofFileSinkConfig | AtofStreamSinkConfig] | None = None
🧰 Tools
🪛 Ruff (0.15.21)

[warning] 88-88: Remove quotes from type annotation

Remove quotes

(UP037)

🤖 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 `@python/nemo_relay/observability.py` at line 88, Update the sinks annotation
in the relevant observability configuration to remove the unnecessary quotes
around the union type, using the native union expression directly while
preserving the existing list and None typing.

Source: Linters/SAST tools

@willkill07

Copy link
Copy Markdown
Member Author

/ok to test 15e1656

@willkill07

Copy link
Copy Markdown
Member Author

/merge

@rapids-bot
rapids-bot Bot merged commit a10738b into NVIDIA:main Jul 14, 2026
68 of 69 checks passed
rapids-bot Bot pushed a commit that referenced this pull request Jul 16, 2026
#### Overview

Documents the typed OTLP attribute projection implemented by merged PR #414 for the OpenTelemetry and OpenInference exporters.

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

#### Details

- Documents the ordered `attribute_mappings` configuration for remapping fully qualified projected attributes to backend aliases without changing their OTLP types.
- Describes typed dotted projection for lifecycle metadata, start/end fields, handle attributes, and mark data.
- Explains top-level JSON encoding for nested objects and arrays, omission of `null` fields, and the retained OpenInference metadata compatibility attribute.
- Updates the OpenTelemetry and OpenInference documentation and their observability-skill references.
- Contains no implementation changes.
- Breaking changes: none.

Validation:

- `just docs`
- Targeted pre-commit hooks for all four changed documentation and skill files
- `git diff --check`

#### Where should the reviewer start?

Start with `docs/configure-plugins/observability/opentelemetry.mdx`, then compare the compatibility behavior in `docs/configure-plugins/observability/openinference.mdx`.

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

- Relates to: #414

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - Maryam Najafian (https://github.com/mnajafian-nv)

URL: #415
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking PR introduces a breaking change 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