Skip to content

feat: expose event sanitizers through C and Go [5/6]#375

Merged
rapids-bot[bot] merged 5 commits into
NVIDIA:mainfrom
willkill07:feat/relay-409-go-event-sanitizers
Jul 9, 2026
Merged

feat: expose event sanitizers through C and Go [5/6]#375
rapids-bot[bot] merged 5 commits into
NVIDIA:mainfrom
willkill07:feat/relay-409-go-event-sanitizers

Conversation

@willkill07

@willkill07 willkill07 commented Jul 7, 2026

Copy link
Copy Markdown
Member

Overview

Exposes the mark and scope event sanitizer registries from PRs #371 and #372 through the raw C FFI and experimental Go binding.

  • 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

  • Adds the C callback type plus global, scope-local, and plugin-context exports.
  • Regenerates the public FFI header through the repository workflow.
  • Adds Go EventSanitizeFields, callbacks, registrations, plugin methods, and guardrail shorthands.
  • Adds Go PII mark configuration parity.
  • Tests all fields and phases, inheritance, cleanup, rollback, invalid callback fail-open, invalid UUIDs, duplicate registration, and callback release.
  • Contains no documentation changes; documentation is isolated in a final independent PR.
  • Breaking changes: none.

Validation: FFI unit/integration tests, FFI coverage (94.26% line; no uncovered executable lines in the new registry file), just build-go, just test-go, CI Go coverage (95% main package; 100% guardrail shorthand package), just test-rust, formatting, Clippy, vet, and pre-commit.

Where should the reviewer start?

Start with crates/ffi/src/api/event_registry.rs, go/nemo_relay/event_sanitizers_test.go, and the FFI registry coverage test.

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

  • Relates to: RELAY-409

Summary by CodeRabbit

  • New Features
    • Added event sanitizer guardrails for mark, scope-start, and scope-end events, with global, scope-local, and plugin-context registration/deregistration.
    • Introduced a sanitizer callback that can rewrite event observability fields.
    • Updated Go/C bindings to support registering and removing these guardrails.
    • Enabled mark-based behavior in the default PII redaction configuration.
  • Bug Fixes
    • Invalid or null sanitizer output no longer overwrites existing event fields.
    • Failed sanitizer registrations now clean up correctly without leaks.

@coderabbitai

coderabbitai Bot commented Jul 7, 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: 18f76662-2898-4b9b-b712-25159ac229c2

📥 Commits

Reviewing files that changed from the base of the PR and between e823920 and 54f153c.

📒 Files selected for processing (1)
  • go/nemo_relay/callbacks.go
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (12)
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/callbacks.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/callbacks.go
**/*.{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:

  • go/nemo_relay/callbacks.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/callbacks.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/callbacks.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/callbacks.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/callbacks.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/callbacks.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:

  • go/nemo_relay/callbacks.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:

  • go/nemo_relay/callbacks.go
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

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

Project Overview

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

The shared runtime model is:

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

Repository Structure

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

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

Prerequisites

Insta...

Files:

  • go/nemo_relay/callbacks.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/callbacks.go
🔇 Additional comments (1)
go/nemo_relay/callbacks.go (1)

209-214: LGTM! This resolves the prior review comment about documenting the replace-not-patch and zero-value-clears semantics for EventSanitizeFields.


Walkthrough

This PR adds event-sanitizer guardrails across the C FFI, Rust, and Go bindings for mark and scope start/end events. It also adds a Mark default to PII redaction config and updates tests.

Changes

Event Sanitizer Guardrails

Layer / File(s) Summary
FFI callback contract
crates/ffi/nemo_relay.h, crates/ffi/src/callable.rs, go/nemo_relay/callbacks.go, go/nemo_relay/plugin.go, go/nemo_relay/nemo_relay.go, crates/ffi/src/api/mod.rs
Declares the sanitizer callback type and wires the new callback symbols into the Rust and Go FFI surfaces.
Rust event registry
crates/ffi/src/api/event_registry.rs, crates/ffi/tests/unit/api/registry_tests.rs
Implements global and scope-local sanitizer registration and deregistration, including scope UUID parsing and exported FFI entry points.
Rust plugin context registration
crates/ffi/src/api/plugin.rs
Adds plugin-context sanitizer registration helpers and exported C-callable entry points for mark, scope-start, and scope-end surfaces.
Go sanitizer bindings
go/nemo_relay/callbacks.go, go/nemo_relay/nemo_relay.go, go/nemo_relay/guardrails/guardrails.go, go/nemo_relay/plugin.go, go/nemo_relay/adaptive_plugin_test.go
Adds the Go sanitizer field model, trampoline, top-level registration helpers, and plugin-context methods for the new guardrails.
Go sanitizer tests
crates/ffi/tests/unit/callable_tests.rs, go/nemo_relay/event_sanitizers_test.go, go/nemo_relay/guardrails/guardrails_test.go, go/nemo_relay/adaptive_plugin_test.go
Exercises sanitizer registration, inheritance, lifecycle behavior, closed-context failures, and callback wrapping across the Go and Rust APIs.

PII Redaction Mark Default

Layer / File(s) Summary
PII redaction Mark field
go/nemo_relay/pii_redaction.go, go/nemo_relay/pii_redaction_test.go
Adds the Mark boolean field to the PII redaction config, sets its default to true, and updates tests to expect the serialized value.

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

Sequence Diagram(s)

sequenceDiagram
  participant PluginContext
  participant goEventSanitizeTrampoline
  participant event_registry
  participant CHeader

  PluginContext->>goEventSanitizeTrampoline: register EventSanitizeFunc
  goEventSanitizeTrampoline->>event_registry: bridge sanitized fields JSON
  event_registry->>CHeader: expose NemoRelayEventSanitizeCb
Loading

Possibly related PRs

  • NVIDIA/NeMo-Relay#371: Adds the sanitizer registry and callback plumbing that these FFI, Go, and plugin-context registrations extend.
  • NVIDIA/NeMo-Relay#372: Uses the event-sanitizer infrastructure to implement built-in PII redaction behavior for mark and scope events.

Suggested labels: DO NOT MERGE

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is descriptive, related to the changeset, and follows Conventional Commits with a valid feat prefix.
Description check ✅ Passed The description matches the template sections and includes overview, details, reviewer start, and related issue information.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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

@github-actions github-actions Bot added the size:XL PR is extra large label Jul 7, 2026
@github-actions github-actions Bot added Feature a new feature lang:go PR changes/introduces Go code lang:rust PR changes/introduces Rust code labels Jul 7, 2026
@willkill07 willkill07 added this to the 0.6 milestone Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

@willkill07 willkill07 self-assigned this Jul 7, 2026
@willkill07 willkill07 changed the title feat: expose event sanitizers through C and Go feat: expose event sanitizers through C and Go [5/6] Jul 7, 2026
@willkill07
willkill07 changed the base branch from fix/relay-409-pii-event-redaction to main July 8, 2026 21:36
@willkill07
willkill07 force-pushed the feat/relay-409-go-event-sanitizers branch from 0007f5a to b80fece Compare July 8, 2026 21:40
Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07
willkill07 force-pushed the feat/relay-409-go-event-sanitizers branch from b80fece to d970470 Compare July 9, 2026 02:10
@willkill07
willkill07 marked this pull request as ready for review July 9, 2026 11:58
@willkill07
willkill07 requested a review from a team as a code owner July 9, 2026 11:58

@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/ffi/src/api/plugin.rs`:
- Around line 369-397: The `plugin_register_event_sanitizer` dispatch is using
raw `u8` magic values for `surface`, which can silently route unknown inputs to
the end-surface handler. Replace the `surface: u8` parameter with the existing
exhaustive `Surface` enum used by `event_registry.rs` (or a local equivalent in
`plugin.rs`), and update the `match` in `plugin_register_event_sanitizer` to be
enum-based and exhaustive so invalid values are handled at compile time instead
of falling through to the default arm. Also update the related sanitizer
registration path that follows the same pattern to use the typed surface value
consistently.
🪄 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: 8067ae54-cbcb-4818-b938-4f7030563eac

📥 Commits

Reviewing files that changed from the base of the PR and between c83c8f2 and d970470.

📒 Files selected for processing (16)
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/src/api/mod.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/nemo_relay.go
  • go/nemo_relay/pii_redaction.go
  • go/nemo_relay/pii_redaction_test.go
  • go/nemo_relay/plugin.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (22)
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/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_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/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/nemo_relay.go
**/*.{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:

  • go/nemo_relay/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • crates/ffi/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/nemo_relay.h
**/*.{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/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • crates/ffi/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.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/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • 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/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/nemo_relay.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/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_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/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • 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:

  • go/nemo_relay/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • crates/ffi/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/nemo_relay.h
**/*.{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:

  • go/nemo_relay/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • crates/ffi/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

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

Project Overview

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

The shared runtime model is:

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

Repository Structure

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

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

Prerequisites

Insta...

Files:

  • go/nemo_relay/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • crates/ffi/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/nemo_relay.go
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/nemo_relay.h
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/pii_redaction.go
  • go/nemo_relay/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/guardrails/guardrails.go
  • go/nemo_relay/plugin.go
  • go/nemo_relay/adaptive_plugin_test.go
  • go/nemo_relay/nemo_relay.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/guardrails/guardrails_test.go
  • go/nemo_relay/event_sanitizers_test.go
  • go/nemo_relay/pii_redaction_test.go
  • crates/ffi/tests/unit/callable_tests.rs
  • go/nemo_relay/adaptive_plugin_test.go
  • crates/ffi/tests/unit/api/registry_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/ffi/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.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/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/nemo_relay.h
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/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • crates/ffi/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.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/ffi/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.rs
{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/mod.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/nemo_relay.h
{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/mod.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/src/api/event_registry.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/src/api/mod.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/ffi/src/api/plugin.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/nemo_relay.h
crates/ffi/nemo_relay.h

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

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

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
🔇 Additional comments (15)
go/nemo_relay/pii_redaction.go (1)

36-36: LGTM!

Also applies to: 60-60

go/nemo_relay/pii_redaction_test.go (1)

10-10: LGTM!

Also applies to: 33-35

crates/ffi/nemo_relay.h (1)

209-227: LGTM!

Also applies to: 558-671, 1455-1490

crates/ffi/src/callable.rs (1)

26-33: LGTM!

Also applies to: 143-150, 837-854

crates/ffi/tests/unit/callable_tests.rs (1)

9-9: LGTM!

Also applies to: 247-282, 535-559

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

1-301: LGTM!

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

17-27: LGTM!

Also applies to: 68-68, 80-80

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

7-33: LGTM!

Also applies to: 35-429

go/nemo_relay/callbacks.go (1)

39-39: LGTM!

Also applies to: 208-217, 426-441

go/nemo_relay/nemo_relay.go (1)

164-181: LGTM!

Also applies to: 270-270, 1207-1258, 2142-2203

go/nemo_relay/guardrails/guardrails.go (1)

37-96: LGTM!

go/nemo_relay/guardrails/guardrails_test.go (1)

252-267: LGTM!

Also applies to: 292-307

go/nemo_relay/event_sanitizers_test.go (1)

1-189: LGTM!

go/nemo_relay/plugin.go (1)

16-16: LGTM!

Also applies to: 38-40, 56-56, 350-385

go/nemo_relay/adaptive_plugin_test.go (1)

9-9: LGTM!

Also applies to: 45-62, 374-423, 496-504

Comment thread crates/ffi/src/api/plugin.rs
Signed-off-by: Will Killian <wkillian@nvidia.com>
@bbednarski9

Copy link
Copy Markdown
Contributor

P2 Closure-leak-on-registration-error: In the Go registration helpers, registerClosure runs before the C call; a non-Ok status (e.g. duplicate name) leaks the registry entry + token because the C side never invokes free_fn. The new code faithfully copies the existing RegisterToolSanitizeRequestGuardrail pattern, so it's not a regression — but the same latent leak now exists on six more paths.

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.

Caution

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

⚠️ Outside diff range comments (1)
go/nemo_relay/callbacks.go (1)

427-441: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Do not fail open in goEventSanitizeTrampoline On unmarshal/marshal error this returns nil, and the Rust wrapper falls back to the original fields, so a sanitizer failure emits the unsanitized payload instead of blocking it. If that posture is intentional, document it; otherwise surface an error and fail closed.

🤖 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 `@go/nemo_relay/callbacks.go` around lines 427 - 441, The
goEventSanitizeTrampoline callback currently returns nil on JSON
unmarshal/marshal errors, which lets the Rust wrapper fall back to the original
unsanitized fields. Update goEventSanitizeTrampoline to fail closed by surfacing
an error instead of returning nil, or explicitly document the intentional
fail-open behavior if that is desired; use the existing lookupClosure,
setLastErrorMessage, and EventSanitizeFunc flow as the fix point.
🤖 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 `@go/nemo_relay/callbacks.go`:
- Around line 427-441: The goEventSanitizeTrampoline callback currently returns
nil on JSON unmarshal/marshal errors, which lets the Rust wrapper fall back to
the original unsanitized fields. Update goEventSanitizeTrampoline to fail closed
by surfacing an error instead of returning nil, or explicitly document the
intentional fail-open behavior if that is desired; use the existing
lookupClosure, setLastErrorMessage, and EventSanitizeFunc flow as the fix point.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 09571ca1-2c2a-4587-a54f-ceff1db24d0b

📥 Commits

Reviewing files that changed from the base of the PR and between a115634 and f027293.

📒 Files selected for processing (3)
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/mod.rs
  • go/nemo_relay/callbacks.go
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (21)
**/*.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/src/api/mod.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/src/api/mod.rs
  • crates/ffi/nemo_relay.h
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/src/api/mod.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • crates/ffi/src/api/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:

  • crates/ffi/src/api/mod.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/src/api/mod.rs
  • go/nemo_relay/callbacks.go
  • crates/ffi/nemo_relay.h
**/*.{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/src/api/mod.rs
  • go/nemo_relay/callbacks.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/mod.rs
  • crates/ffi/nemo_relay.h
{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/mod.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/ffi/src/api/mod.rs
  • go/nemo_relay/callbacks.go
  • crates/ffi/nemo_relay.h
**/*.{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/src/api/mod.rs
  • go/nemo_relay/callbacks.go
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

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

Project Overview

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

The shared runtime model is:

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

Repository Structure

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

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

Prerequisites

Insta...

Files:

  • crates/ffi/src/api/mod.rs
  • go/nemo_relay/callbacks.go
  • crates/ffi/nemo_relay.h
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/src/api/mod.rs
  • crates/ffi/nemo_relay.h
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/callbacks.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/callbacks.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/callbacks.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/callbacks.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/callbacks.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/callbacks.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/callbacks.go
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
🔇 Additional comments (5)
crates/ffi/nemo_relay.h (2)

468-472: LGTM!


448-473: 🗄️ Data Integrity & Integration

Sanitizer APIs are already present in the generated header. crates/ffi/nemo_relay.h includes NemoRelayEventSanitizeCb and the related register/deregister entrypoints, matching the Rust FFI exports, so there isn’t a header-sync issue here.

			> Likely an incorrect or invalid review comment.
go/nemo_relay/callbacks.go (1)

39-39: LGTM!

Also applies to: 208-217, 243-247, 601-606

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

17-27: LGTM!

Also applies to: 69-69, 81-81


58-58: LGTM!

Also applies to: 264-378

Comment thread go/nemo_relay/callbacks.go

@bbednarski9 bbednarski9 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.

Approved with nit contract documentation request

@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 (1)
go/nemo_relay/event_sanitizers_test.go (1)

167-197: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing duplicate-registration leak coverage for the new scope-local guardrails.

The registration-error test exercises duplicate-name failures only for the global RegisterMarkSanitizeGuardrail and RegisterToolSanitizeRequestGuardrail paths, and exercises the new scope-local guardrails (ScopeRegisterMarkSanitizeGuardrail, ScopeRegisterScopeSanitizeStartGuardrail, ScopeRegisterScopeSanitizeEndGuardrail) only for the invalid-UUID case, not for duplicate-name registration. Since this PR adds six new registration paths, a duplicate-name-no-leak assertion for at least the scope-local mark/start/end guardrails would close the coverage gap on the newly introduced error path.

As per path instructions, "Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant."

🤖 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 `@go/nemo_relay/event_sanitizers_test.go` around lines 167 - 197, The
registration-error test is missing duplicate-name leak coverage for the new
scope-local guardrail paths. Extend the existing duplicate-registration
assertions in event_sanitizers_test.go to cover
ScopeRegisterMarkSanitizeGuardrail, ScopeRegisterScopeSanitizeStartGuardrail,
and ScopeRegisterScopeSanitizeEndGuardrail the same way it already covers
RegisterMarkSanitizeGuardrail and RegisterToolSanitizeRequestGuardrail: verify
the second registration fails, then check the closureRegistry size does not
increase. Use the existing baseline/closureRegistryMu pattern and the relevant
guardrail function names to locate the affected test block.

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.

Outside diff comments:
In `@go/nemo_relay/event_sanitizers_test.go`:
- Around line 167-197: The registration-error test is missing duplicate-name
leak coverage for the new scope-local guardrail paths. Extend the existing
duplicate-registration assertions in event_sanitizers_test.go to cover
ScopeRegisterMarkSanitizeGuardrail, ScopeRegisterScopeSanitizeStartGuardrail,
and ScopeRegisterScopeSanitizeEndGuardrail the same way it already covers
RegisterMarkSanitizeGuardrail and RegisterToolSanitizeRequestGuardrail: verify
the second registration fails, then check the closureRegistry size does not
increase. Use the existing baseline/closureRegistryMu pattern and the relevant
guardrail function names to locate the affected test block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: c48481ca-85ff-4fbd-867a-259599f1d275

📥 Commits

Reviewing files that changed from the base of the PR and between f027293 and e823920.

📒 Files selected for processing (1)
  • go/nemo_relay/event_sanitizers_test.go
📜 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)
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/event_sanitizers_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/event_sanitizers_test.go
**/*.{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:

  • go/nemo_relay/event_sanitizers_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/event_sanitizers_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/event_sanitizers_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/event_sanitizers_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/event_sanitizers_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/event_sanitizers_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:

  • go/nemo_relay/event_sanitizers_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:

  • go/nemo_relay/event_sanitizers_test.go
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

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

Project Overview

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

The shared runtime model is:

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

Repository Structure

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

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

Prerequisites

Insta...

Files:

  • go/nemo_relay/event_sanitizers_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/event_sanitizers_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/event_sanitizers_test.go
🔇 Additional comments (1)
go/nemo_relay/event_sanitizers_test.go (1)

167-181: LGTM!

Signed-off-by: Will Killian <wkillian@nvidia.com>
@coderabbitai coderabbitai Bot added the DO NOT MERGE PR should not be merged; see PR for details label Jul 9, 2026
@willkill07 willkill07 removed the DO NOT MERGE PR should not be merged; see PR for details label Jul 9, 2026
@willkill07

Copy link
Copy Markdown
Member Author

/merge

@rapids-bot
rapids-bot Bot merged commit d274e9a into NVIDIA:main Jul 9, 2026
40 checks passed
rapids-bot Bot pushed a commit that referenced this pull request Jul 15, 2026
#### Overview

Documents the event sanitizer and PII redaction behavior implemented by the merged RELAY-409 stack (#371#375). This docs-only PR is the final part of that six-PR stack.

- [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

- Adds a dedicated event sanitizer reference covering callback replacement semantics, ordering, registration lifetimes, cross-language APIs, and dynamic-plugin protocol values.
- Explains which mark and scope event fields sanitizers can replace and which lifecycle and identity fields remain immutable.
- Documents PII `mark = true` defaults, opt-out behavior, generic scope `input` and `output` coverage, and subscriber/exporter privacy guarantees.
- Updates related middleware, event, scope/mark, plugin-authoring, Python, Node.js, Go, and observability-skill guidance.
- Contains no implementation changes.
- Breaking changes: none.

Validation:

- `just docs`
- `just docs-linkcheck`
- `uv run pre-commit run --all-files`

#### Where should the reviewer start?

Start with `docs/reference/event-sanitizers.mdx`, then review `docs/configure-plugins/pii-redaction/configuration.mdx` for the privacy contract.

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

- Relates to: RELAY-409
- Relates to: #371, #372, #373, #374, and #375



## Summary by CodeRabbit

- **New Features**
  - Added observability sanitization for **mark** and **scope** events (including scope-start and scope-end) before delivery to subscribers/exporters.
  - Sanitizers can rewrite observability fields (`data`, `category_profile`, `metadata`) while keeping core identity/context fields immutable.
  - Expanded privacy redaction support with a configurable `mark` option (default enabled) for mark and generic scope events.

- **Documentation**
  - Added a new Event Sanitizers reference covering registration levels, ordering, callback contracts, and examples.
  - Updated middleware and language-binding guidance plus Node.js/Go/Python overviews to reflect mark/scope sanitizer capabilities.

Authors:
  - Will Killian (https://github.com/willkill07)

Approvers:
  - https://github.com/lvojtku
  - Bryan Bednarski (https://github.com/bbednarski9)

URL: #376
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: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