Skip to content

feat(observability): Add OpenTelemetry GenAI Semantic Projection#490

Draft
Mluckydwyer wants to merge 1 commit into
NVIDIA:mainfrom
Mluckydwyer:feat/opentelemetry-genai
Draft

feat(observability): Add OpenTelemetry GenAI Semantic Projection#490
Mluckydwyer wants to merge 1 commit into
NVIDIA:mainfrom
Mluckydwyer:feat/opentelemetry-genai

Conversation

@Mluckydwyer

Copy link
Copy Markdown

Overview

Add opt-in OpenTelemetry GenAI 1.37+ semantic-convention support to NeMo Relay’s existing OTLP exporter.

Generic OpenTelemetry remains the default, OpenInference behavior is unchanged, and no backend-specific transport or intake code is added.

  • 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

  • Add a gen_ai projection for agent, LLM, tool, embedding, retrieval, and reranking scopes.
  • Map available provider, model, token usage, message, tool, conversation, status, and error data to GenAI attributes while retaining nemo_relay.*
    correlation attributes.
  • Preserve the existing exporter, transport, batching, and lifecycle implementation.
  • Add configuration and API support across Rust, Python, Node.js, C FFI, and Go.
  • Keep content capture disabled by default and emit sensitive content only from sanitized events when explicitly enabled.
  • Add compatibility, privacy, configuration, binding, and exporter tests. There are no intentional breaking changes.

Validation passed: Rust formatting, Clippy, the serial Rust workspace suite, 35 focused OpenTelemetry tests, 536 Python tests, 266 Node.js tests, Go and FFI
suites, documentation checks, and the complete pre-commit suite.

Reviewer considerations:

  • OpenTelemetry 1.37 has no first-class reranking operation, so Relay uses the documented rerank operation.
  • The updated observability skill should receive a refreshed signature through /nvskills-ci.

Where should the reviewer start?

Start with crates/core/src/observability/otel.rs, the projection in otel_genai.rs, and the coverage in crates/core/tests/unit/observability/ otel_tests.rs.

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

  • Relates to: none

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 3121f506-49e1-41fc-89f2-afba77d4dea3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

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

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

@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 20, 2026
@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

License Diff

Compared against origin/main.

Lockfile license changes

Lockfile License Changes

Rust

Added

  • opentelemetry-semantic-conventions 0.31.0 (Apache-2.0)

Removed

  • None

Updated/Changed

  • None

Node

Added

  • None

Removed

  • None

Updated/Changed

  • None

Python

Added

  • None

Removed

  • None

Updated/Changed

  • None
Status output
[license-diff] selected languages: rust, node, python
[license-diff] generating current inventory
[license-diff] current: generating Rust inventory
[license-diff] current: Rust inventory complete (427 packages)
[license-diff] current: generating Node inventory
[license-diff] current: Node inventory complete (367 packages)
[license-diff] current: generating Python inventory
[license-diff] current: Python inventory complete (105 packages)
[license-diff] current inventory complete
[license-diff] checking out base ref origin/main into a temporary worktree
[license-diff] base: generating Rust inventory
[license-diff] base: Rust inventory complete (426 packages)
[license-diff] base: generating Node inventory
[license-diff] base: Node inventory complete (367 packages)
[license-diff] base: generating Python inventory
[license-diff] base: Python inventory complete (105 packages)
[license-diff] base inventory complete
[license-diff] removing temporary base worktree
[license-diff] comparing inventories
[license-diff] rendering Markdown output
[license-diff] done

Signed-off-by: Matt Dwyer <10600905+Mluckydwyer@users.noreply.github.com>
@Mluckydwyer

Copy link
Copy Markdown
Author

Local tests in e2e environments are still WIP.

@willkill07 willkill07 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some minor comments. Also happy to get this across the finish line for you.

/// # Safety
/// Any non-null C strings must be valid and `out` must be non-null.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn nemo_relay_otel_subscriber_create_with_attribute_mappings(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can get rid of this and only keep with_options.

Although a breaking API change, we haven't stabalized the FFI/Go APIs yet.

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum OpenTelemetrySemanticConvention {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we do keep generic and also add GenAI, why not just create a new OtelGenAISemanticConvention exporter type? That way we keep all existing behavior/APIs and introduce the new one:

  1. as a new exporter type
  2. as a new configuration option under the observability plugin

Each exporter projects `usage`/`cost` differently. Projections do not change the
canonical fields above.

| | ATOF | ATIF step / `final_metrics` | OpenInference | OpenTelemetry |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This table supports my suggestion on splitting out as a new type rather than overload OpenTelemetry

@willkill07 willkill07 added this to the 0.7 milestone Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants