feat(observability): expose projected mark payloads to OpenInference#403
Conversation
WalkthroughTool-projected marks now emit standard OpenInference tool identity, serialized output, MIME type, and metadata attributes when payloads exist. Generic mark events remain unchanged, and absent payloads omit standard output and metadata fields. ChangesProjected mark OpenInference attributes
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
#### Overview
Adds a single lifecycle-level `session.start` ATOF Mark and stable root-only session correlation across OpenTelemetry, OpenInference, and ATIF.
Today, Relay can carry logical session metadata such as `session_id` and `user_id`, but downstream trace tables do not have a stable Relay-owned value that correlates multiple exporter-native traces created during one CLI session lifecycle. This change reuses the existing `ScopeStack.root_uuid` as that instance identifier instead of introducing a new session context, cache, or generated ID.
The resulting identity model is:
* `session.id`: harness-provided logical session ID.
* `user.id`: optional top-level string `user_id`.
* `nemo_relay.session.instance_id`: Relay's existing root scope UUID for one CLI session lifecycle.
* `trace_id`: exporter-native trace identity used to join child rows after locating trace roots.
* `nemo_relay.uuid`: existing Relay event/scope identity, unchanged.
#### Contribution Confirmation
- [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
**CLI session lifecycle**
* Merges configured and hook-provided session metadata through the existing session metadata path.
* Adds a non-overridable `session_instance_id` whose value is the session's existing `ScopeStack.root_uuid`.
* Preserves `session_id` as the harness-provided logical ID and accepts optional top-level string `user_id`.
* Stamps the merged identity onto session Agent scopes and root turn scopes.
* Emits exactly one `session.start` Mark when the first `AgentStarted` begins a lifecycle.
* Parents the Mark to the session Agent scope when available; turn-root-only harnesses retain the existing orphan zero-duration representation without forcing a turn scope open.
* Allows repeated start hooks to merge later metadata without emitting duplicate startup Marks.
**OpenTelemetry and OpenInference**
* Adds shared canonical identity projection for `session.id`, `user.id`, and `nemo_relay.session.instance_id`.
* Projects those fields only onto spans without a valid exported parent context, yielding one canonical copy per trace root.
* Keeps `session.start` correlated whether it is exported as an active-parent span event or a standalone Mark span.
* Preserves raw metadata and existing custom `attribute_mappings`; custom mappings cannot replace canonical projected fields.
* Ignores non-string `session_id` and `user_id` for canonical aliases while retaining the raw metadata.
* Leaves OpenTelemetry and OpenInference trace/span ID generation independent while sharing Relay UUID and session instance identity.
**ATIF compatibility**
* Leaves `AtifTrajectory.session_id`, `trajectory_id`, `{session_id}` filename expansion, and the remote session header unchanged.
* Merges correlation into existing trajectory `extra` instead of overwriting it.
* Adds `extra.nemo_relay.session_id`, `extra.nemo_relay.session_instance_id`, and optional `extra.nemo_relay.user_id`.
* Records the full startup Mark once in `extra.observed_events` without creating an ATIF step or duplicating the complete metadata object.
**Documentation**
* Documents `--session-metadata '{"user_id":"alice"}'`.
* Explains logical session ID, Relay instance ID, exporter trace ID, and Relay UUID.
* Documents root-only filtering and downstream child-row correlation by `trace_id`.
* Documents ATIF's compatibility-preserving fields under `extra.nemo_relay`.
**Validation**
Focused lifecycle and exporter tests cover duplicate starts, Agent-backed and turn-root-only parentage, UUIDv7 instance IDs, recreated and concurrent sessions, multiple trace roots, root-only canonical projection, orphan and active-parent Marks, custom mapping precedence, ATIF compatibility, and cross-exporter identity parity.
Post-rebase validation:
* `just test-rust`
* `just test-python` — 528 passed
* `just test-node` — 264 passed
* `just test-go`
* `cargo fmt --all -- --check`
* `cargo clippy --workspace --all-targets -- -D warnings`
* `just docs` — passed with the expected unauthenticated Fern redirect warning
* `uv run pre-commit run --all-files` — every hook passed except `attributions-rust`, which rewrites the unchanged `md-5 0.10.6` entry from dual MIT/Apache licensing to Apache-only under local `cargo-about 0.9.0`; that unrelated generated diff was reverted
No public binding API or configuration type changes are introduced. No breaking changes are expected.
Coordination note: open PR #403 also modifies OpenInference Mark projection and some adjacent tests, but it addresses projected Mark payload aliases rather than session lifecycle correlation. This PR keeps the shared exporter changes narrowly scoped to canonical session identity.
#### Where should the reviewer start?
1. `crates/cli/src/sessions/mod.rs` for session metadata enrichment and one-shot `session.start` emission.
2. `crates/core/src/observability/mod.rs` for the shared canonical identity projection.
3. `crates/core/src/observability/otel.rs` and `openinference.rs` for trace-root and Mark handling.
4. `crates/core/src/observability/plugin_component.rs` for compatibility-preserving ATIF `extra.nemo_relay` enrichment.
5. The CLI lifecycle and exporter parity tests for the intended contract and edge cases.
#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
* Closes #434
## Summary by CodeRabbit
* **New Features**
* Added consistent session, user, and session-instance correlation identifiers across traces and exported trajectories.
* Added a single `session.start` event for each session, with stable correlation metadata.
* Preserved correlation data in ATIF exports and raw event metadata.
* **Documentation**
* Documented session-start events, correlation fields, trace behavior, and session metadata usage across observability integrations.
## Summary by CodeRabbit
* **New Features**
* Added a single `session.start` event for each session, with stable session-instance correlation data.
* Added session and user identity attributes to relevant trace roots and session markers.
* Added session correlation details to ATIF exports while preserving observed event data.
* **Documentation**
* Documented session-start markers, identity fields, trace correlation, and export behavior across observability integrations.
## Summary by CodeRabbit
* **New Features**
* Added a single `session.start` event for each session, with stable session-instance correlation metadata.
* Added session and user identity fields to trace roots and session-start marks across supported observability exports.
* Added session correlation details to ATIF output while preserving existing trajectory data.
* **Documentation**
* Documented session-start events, correlation fields, and export behavior across observability integrations.
Authors:
- Bryan Bednarski (https://github.com/bbednarski9)
Approvers:
- Will Killian (https://github.com/willkill07)
URL: #437
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
426f2fe to
c7a6b2e
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/openinference.rs`:
- Around line 729-732: Remove the orphan computation and conditional
`nemo_relay.mark.orphan` attribute from the Tool-projected mark path around
`push_projected_mark_attributes`; retain orphan tagging on the native
generic-mark path. Update the existing orphan test fixture to assert that
Tool-projected marks omit this attribute.
In `@crates/core/tests/unit/observability/openinference_tests.rs`:
- Around line 2637-2671: Extend the Tool projection tests around
tool_projection_omits_standard_payload_attributes_when_mark_payload_is_absent
with separate data-only and metadata-only mark cases. Assert that data-only
emits the output.* attributes without metadata, while metadata-only emits
metadata without output.*, preserving the existing assertions for both-present
and both-absent marks.
🪄 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: 71407ff9-fdfa-45d1-83ba-3fa371719c3c
📒 Files selected for processing (2)
crates/core/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
- GitHub Check: Check / Run
- GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (14)
**/*.rs
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
**/*.rs: Any Rust change must runjust test-rust
Any Rust change must runcargo fmt --all
Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allfor all FFI work since it is Rust work
Runjust test-rustto validate FFI changes
Runcargo clippy --workspace --all-targets -- -D warningsto enforce strict linting on FFI workWhen Rust files changed as part of Go work, also run
cargo fmt --all,just test-rust, andcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allwhen Rust files are changed as part of Node work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files are changed as part of Node work
Runjust test-rustwhen Rust files are changed as part of Node workWhen changing the core Rust runtime or Rust-facing API surface, format Rust code with
cargo fmt(rustfmt defaults), keepcargo clippy -- -D warningsclean, and satisfycargo deny checkperdeny.toml.
**/*.rs: If any Rust code changed, always runjust test-rust.
If any Rust code changed, also runcargo fmt --all.
If any Rust code changed, also runcargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, runcargo fmt --allandcargo clippy --workspace --all-targets -- -D warningseven if relying on pre-commit.
Files:
crates/core/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_tests.rs
{crates/core,crates/adaptive}/**/*
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
Changes to
crates/coreorcrates/adaptivemust run the full language matrix
Files:
crates/core/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_tests.rs
crates/core/**/*.rs
📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)
If the change touched
crates/coreor shared runtime semantics, also usevalidate-changefor broader validation
Files:
crates/core/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_tests.rs
**/*.{rs,py}
📄 CodeRabbit inference engine (AGENTS.md)
Follow binding naming conventions in Rust and Python: use
snake_case.
Files:
crates/core/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_tests.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,py,js,mjs,cjs,ts,tsx}: UseJson = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads.
UseResult<T>withFlowErrorin 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/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_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 prefixednemo_relay_, GoPascalCase, Node.jscamelCase, and Pythonsnake_case.
Files:
crates/core/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_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/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_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 cratetests/trees, and Python SDK tests belong underpython/tests.
Files:
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/openinference.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, usemaintain-dynamic-pluginsand 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, preferuv run pre-commit run --files <changed files...>.
Before review or handoff, runuv run pre-commit run --all-files.
Files:
crates/core/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_tests.rs
crates/{core,adaptive}/**/*
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
If
crates/coreorcrates/adaptivechanged, run the full validation matrix across Rust, Python, Go, and Node.js.
Files:
crates/core/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_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/src/observability/openinference.rscrates/core/tests/unit/observability/openinference_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/openinference.rscrates/core/tests/unit/observability/openinference_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/openinference_tests.rs
🔇 Additional comments (3)
crates/core/src/observability/openinference.rs (2)
729-729: 📐 Maintainability & Code QualityRun the required Rust/core validation matrix before merge.
Run
cargo fmt --all,just test-rust,cargo clippy --workspace --all-targets -- -D warnings,cargo deny check,validate-change, the full Rust/Python/Go/Node matrix, anduv run pre-commit run --all-files. Also verify SPDX headers on both changed Rust files.As per coding guidelines, “Any Rust change must run
just test-rust,”cargo fmt --all, andcargo clippy --workspace --all-targets -- -D warnings;crates/corechanges require broader and full-language validation.Source: Coding guidelines
1535-1547: LGTM!crates/core/tests/unit/observability/openinference_tests.rs (1)
1064-1068: LGTM!Also applies to: 2549-2550, 2594-2624
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
|
/ok to test c1675d5 |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
crates/core/src/observability/openinference.rs (1)
717-732: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winOrphan tagging still present on the Tool-projection path.
Lines 719 and 730-732 still compute
orphanand conditionally pushnemo_relay.mark.orphan, even thoughpush_projected_mark_attributes(line 729) was added specifically to carry the Tool-projection contract, which per the PR objectives keeps orphan tagging only on the native generic-mark path. Theline_range_change_detailsfor this segment describes the conditional as already replaced, but the code shown still contains it — this looks unresolved from the prior review round. Note the existing test (Context snippet 1) always establishes a parent span, soorphanis nevertruethere and the removal is not actually exercised/verified.🐛 Proposed fix
fn process_mark_as_tool(&mut self, event: &Event) { let timestamp = to_system_time(*event.timestamp()); - let orphan = self.find_parent_span(event).is_none(); let mut attributes = mark_attributes(event); if event.name() == "session.start" { push_session_identity_attributes(&mut attributes, event); } attributes.push(KeyValue::new("nemo_relay.mark.projection", "tool")); attributes.push(KeyValue::new( oi::OPENINFERENCE_SPAN_KIND, OpenInferenceSpanKind::Tool, )); push_projected_mark_attributes(&mut attributes, event); - if orphan { - attributes.push(KeyValue::new("nemo_relay.mark.orphan", true)); - } apply_attribute_mappings(&mut attributes, &self.attribute_mappings);Also worth adding an orphan-mark test case (no parent) to confirm
nemo_relay.mark.orphanis truly absent on the Tool path.🤖 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 717 - 732, Remove the orphan computation and conditional nemo_relay.mark.orphan attribute from process_mark_as_tool, leaving orphan tagging only in the native generic-mark path while preserving push_projected_mark_attributes. Add or update a Tool-projection test with no parent span and assert that nemo_relay.mark.orphan is absent.
🤖 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.
Duplicate comments:
In `@crates/core/src/observability/openinference.rs`:
- Around line 717-732: Remove the orphan computation and conditional
nemo_relay.mark.orphan attribute from process_mark_as_tool, leaving orphan
tagging only in the native generic-mark path while preserving
push_projected_mark_attributes. Add or update a Tool-projection test with no
parent span and assert that nemo_relay.mark.orphan is absent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 9b5f1333-1d35-4486-8c03-a9dc69eb14ae
📒 Files selected for processing (1)
crates/core/src/observability/openinference.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
- GitHub Check: Check / Run
- GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (13)
**/*.rs
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
**/*.rs: Any Rust change must runjust test-rust
Any Rust change must runcargo fmt --all
Any Rust change must runcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allfor all FFI work since it is Rust work
Runjust test-rustto validate FFI changes
Runcargo clippy --workspace --all-targets -- -D warningsto enforce strict linting on FFI workWhen Rust files changed as part of Go work, also run
cargo fmt --all,just test-rust, andcargo clippy --workspace --all-targets -- -D warnings
**/*.rs: Runcargo fmt --allwhen Rust files are changed as part of Node work
Runcargo clippy --workspace --all-targets -- -D warningswhen Rust files are changed as part of Node work
Runjust test-rustwhen Rust files are changed as part of Node workWhen changing the core Rust runtime or Rust-facing API surface, format Rust code with
cargo fmt(rustfmt defaults), keepcargo clippy -- -D warningsclean, and satisfycargo deny checkperdeny.toml.
**/*.rs: If any Rust code changed, always runjust test-rust.
If any Rust code changed, also runcargo fmt --all.
If any Rust code changed, also runcargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, runcargo fmt --allandcargo clippy --workspace --all-targets -- -D warningseven if relying on pre-commit.
Files:
crates/core/src/observability/openinference.rs
{crates/core,crates/adaptive}/**/*
📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
Changes to
crates/coreorcrates/adaptivemust run the full language matrix
Files:
crates/core/src/observability/openinference.rs
crates/core/**/*.rs
📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)
If the change touched
crates/coreor shared runtime semantics, also usevalidate-changefor broader validation
Files:
crates/core/src/observability/openinference.rs
**/*.{rs,py}
📄 CodeRabbit inference engine (AGENTS.md)
Follow binding naming conventions in Rust and Python: use
snake_case.
Files:
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}: UseJson = serde_json::Valuein Rust-facing runtime APIs where the existing code expects JSON payloads.
UseResult<T>withFlowErrorin 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/src/observability/openinference.rs
**/*.{rs,py,go,js,ts,c,h}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Use language-appropriate naming conventions: Rust
snake_case, C FFI exports prefixednemo_relay_, GoPascalCase, Node.jscamelCase, and Pythonsnake_case.
Files:
crates/core/src/observability/openinference.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/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 cratetests/trees, and Python SDK tests belong underpython/tests.
Files:
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/openinference.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, usemaintain-dynamic-pluginsand 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, preferuv run pre-commit run --files <changed files...>.
Before review or handoff, runuv run pre-commit run --all-files.
Files:
crates/core/src/observability/openinference.rs
crates/{core,adaptive}/**/*
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
If
crates/coreorcrates/adaptivechanged, run the full validation matrix across Rust, Python, Go, and Node.js.
Files:
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:
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/src/observability/openinference.rs
🔇 Additional comments (1)
crates/core/src/observability/openinference.rs (1)
1056-1061: LGTM!
|
/merge |
Overview
Expose Relay Mark payloads through standard OpenInference attributes when a Mark is projected as a TOOL span. This lets OpenInference consumers such as Phoenix display a canonical tool name, structured output, and metadata while retaining Relay's typed Mark attributes for filtering and inspection.
This builds on the optional TOOL projection introduced by #382 and the typed Mark attribute projection now present on
main. It does not change native Mark event projection or introduce OpenTelemetry-specific concepts into Relay's core event model.Details
For TOOL-projected Marks, the OpenInference exporter now adds:
tool.nameandtool_call.function.namefrom the canonical Mark name.output.valuecontaining the complete serialized Mark data when data is present.output.mime_type = application/jsonwhenoutput.valueis emitted.metadatacontaining the complete serialized Mark metadata when metadata is present.The existing Relay attributes remain authoritative and are preserved, including typed
nemo_relay.mark.data.*andnemo_relay.mark.metadata.*attributes plus the Mark category, UUID, parent, and orphan information. Missing data or metadata does not create empty OpenInference payload attributes.Native span-event Marks remain unchanged: the OpenInference TOOL aliases are added only by the explicit TOOL-projection path. Generic OTEL, ATOF, ATIF, plugin emission, parentage, and timing behavior are unchanged. This is additive and has no public configuration, wire-schema, or plugin API changes.
Validation on the rebased branch:
cargo fmt --all -- --checkgit diff --check origin/main...HEADcargo test -p nemo-relay 'observability::openinference::tests::tool_projection_' -- --nocapture— 5 passedcargo test -p nemo-relay registered_subscriber_emits_spans_for_scope_push_pop_and_marks -- --nocapture— 2 passedcargo test -p nemo-relaycargo clippy -p nemo-relay --all-targets -- -D warningsAGENTparent with a zero-durationTOOLMark child, correct parentage, standard OpenInference tool/output/metadata fields, and preserved typednemo_relay.mark.*attributes.Breaking changes: none.
Where should the reviewer start?
Start with
process_mark_as_toolandpush_projected_mark_attributesincrates/core/src/observability/openinference.rs. The focused behavior and regression coverage are incrates/core/tests/unit/observability/openinference_tests.rs, including nested payloads, missing optional payloads, typed Relay attributes, parented TOOL spans, and unchanged native event projection.Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
Summary by CodeRabbit
application/jsonMIME type, and metadata when mark data/metadata are available.