feat(plugin): add owned dynamic host activation#364
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: 📒 Files selected for processing (1)
📜 Recent review details⏰ Context from checks skipped due to timeout. (20)
🧰 Additional context used📓 Path-based instructions (12)**/*.rs📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
Files:
{crates/core,crates/adaptive}/**/*📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)
Files:
crates/core/**/*.rs📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)
Files:
**/*.{rs,py}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{rs,py,js,mjs,cjs,ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{rs,py,go,js,ts,c,h}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*.{rs,go,js,ts}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Files:
crates/{core,adaptive}/**/*📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Files:
**/*.{rs,py,go,js,ts}📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
Files:
crates/{core,adaptive}/**/*.rs⚙️ CodeRabbit configuration file
Files:
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}⚙️ CodeRabbit configuration file
Files:
🔇 Additional comments (2)
WalkthroughThe plugin system now uses ownership-tracked registrations, serialized mutation transactions, dynamic host activation handles, checked teardown, and expanded lifecycle tests. CLI startup supports static and dynamic activation paths, while built-in plugins use explicit builtin ownership. ChangesPlugin host lifecycle
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant PluginHostActivation
participant PluginRegistry
participant NativeOrWorkerRuntime
CLI->>PluginHostActivation: initialize dynamic plugin specifications
PluginHostActivation->>PluginRegistry: acquire host lease and register built-ins
PluginHostActivation->>NativeOrWorkerRuntime: load native or worker plugins
NativeOrWorkerRuntime->>PluginRegistry: register tracked plugin kinds
PluginHostActivation->>PluginRegistry: initialize owned configuration
CLI->>PluginHostActivation: clear activation during shutdown
PluginHostActivation->>PluginRegistry: clear callbacks and configuration
PluginHostActivation->>NativeOrWorkerRuntime: deregister and stop runtimes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
License DiffCompared against Lockfile license changesLockfile License ChangesRustAdded
Removed
Updated/Changed
NodeAdded
Removed
Updated/Changed
PythonAdded
Removed
Updated/Changed
Status output |
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
f73370e to
277f24e
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/plugin.rs`:
- Around line 902-908: Update ensure_builtin_plugins_registered and the three
builtin registration helpers to use a read-lock fast path that confirms each
expected builtin is already correctly registered, returning without acquiring
write locks when all are present. Only escalate to the existing write-lock
registration and revalidation path when a builtin is missing, while preserving
replacement rejection and retry behavior for ownership conflicts.
In `@crates/core/src/plugin/dynamic/native.rs`:
- Around line 91-116: The checked teardown loops in
crates/core/src/plugin/dynamic/native.rs:91-116 and
crates/core/src/plugin/dynamic/worker.rs:137-162 duplicate
registration-accounting logic; extract it into a shared helper such as
deregister_tracked_registrations_checked that accepts the tracked registrations
and the “native” or “worker” label, then have deregister_plugin_kinds_checked in
both activation types call that helper while preserving all existing outcomes
and error handling.
In `@crates/core/tests/unit/plugin_tests.rs`:
- Around line 1236-1310: Make the PLUGIN_MUTATION_OWNER cleanup panic-safe in
test_legacy_clear_retains_mutation_owner_after_incomplete_teardown and
test_legacy_replace_retains_mutation_owner_after_incomplete_teardown. Add an
RAII guard after acquiring the runtime-owner lock that restores the owner to
Idle on Drop, including assertion failures, and remove the manual reset
statements at the test ends.
🪄 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: c7bbf793-7860-4a03-93a5-444feae4b5b2
📒 Files selected for processing (18)
crates/cli/src/server.rscrates/cli/tests/coverage/server_tests.rscrates/core/Cargo.tomlcrates/core/src/observability/plugin_component.rscrates/core/src/plugin.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugins/model_pricing.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/tests/unit/plugin_tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
- GitHub Check: Check / Run
🧰 Additional context used
📓 Path-based instructions (21)
**/*.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/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/cli/tests/coverage/server_tests.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.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/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/core/Cargo.tomlcrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.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/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.rs
**/*.{rs,py}
📄 CodeRabbit inference engine (AGENTS.md)
Follow binding naming conventions in Rust and Python: use
snake_case.
Files:
crates/core/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/cli/tests/coverage/server_tests.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.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/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/cli/tests/coverage/server_tests.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.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/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/cli/tests/coverage/server_tests.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.rs
**/*.{rs,go,js,ts}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding
//comment form.
Files:
crates/core/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/cli/tests/coverage/server_tests.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.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/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/cli/tests/coverage/server_tests.rscrates/core/Cargo.tomlcrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.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/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/core/Cargo.tomlcrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.rs
**/*.{rs,py,go,js,ts}
📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)
If a language surface changed, always run that language's test target even when Rust core did not change.
Files:
crates/core/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/cli/tests/coverage/server_tests.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.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:
- Scope stacks decide where work belongs and which scope-local behavior is visible.
- Middleware registries decide what guardrails and intercepts run around managed calls.
- Plugins install reusable runtime behavior from configuration.
- Events record runtime behavior in ATOF form.
- Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.
Repository Structure
The repository layout separates the Rust runtime, language bindings,
documentation, 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 patternsPrerequisites
Insta...
Files:
crates/core/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/cli/tests/coverage/server_tests.rscrates/core/Cargo.tomlcrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.rs
crates/{core,adaptive}/**/*.rs
⚙️ CodeRabbit configuration file
crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.
Files:
crates/core/tests/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/core/src/observability/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_tests.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.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/integration/plugin_host_builtin_ownership_tests.rscrates/core/tests/unit/dynamic_worker_tests.rscrates/cli/tests/coverage/server_tests.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/integration/worker_plugin_tests.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/tests/unit/plugin_tests.rscrates/core/tests/integration/native_plugin_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/plugin_component.rscrates/core/src/plugins/model_pricing.rscrates/core/src/plugins/nemo_guardrails/component.rscrates/core/tests/fixtures/native_plugin/src/lib.rscrates/core/tests/fixtures/worker_plugin/src/main.rscrates/core/src/plugin/dynamic.rscrates/cli/src/server.rscrates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/src/plugin/dynamic/worker.rscrates/core/src/plugin.rs
**/Cargo.toml
📄 CodeRabbit inference engine (.agents/skills/prepare-code-freeze/SKILL.md)
Confirm or infer the target release version from
upstream/main:Cargo.toml. Derive the release branch asrelease/<major>.<minor>.Keep Rust package names and workspace metadata in
Cargo.tomlinternally consistent across the project.
Files:
crates/core/Cargo.toml
**/*.toml
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Add the SPDX license header to all TOML files using the
#comment form.
Files:
crates/core/Cargo.toml
{crates/core/src/plugin/dynamic/**,crates/plugin/**,crates/worker/**,crates/worker-proto/**,crates/types/**,python/plugin/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}
📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)
Keep the stable boundary explicit: native plugins cross a C ABI, and worker plugins cross
grpc-v1.
Files:
crates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/src/plugin/dynamic/worker.rs
{crates/core/src/plugin/dynamic/**/*.rs,examples/rust-native-plugin/**/*.rs}
📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)
Do not pass Rust runtime types, trait objects, futures, or allocator-owned strings across the native dynamic-library boundary.
Files:
crates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/src/plugin/dynamic/worker.rs
{crates/core/src/plugin/dynamic/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}
📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)
Native and worker plugins are trusted extensions; document that native plugins are in-process and unsandboxed, and worker plugins provide process isolation but not a security sandbox.
Files:
crates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/src/plugin/dynamic/worker.rs
{crates/core/src/plugin/dynamic/**/*.rs,crates/plugin/**/*.rs,crates/worker/**/*.rs,crates/worker-proto/**/*.rs,python/plugin/**/*.py}
📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)
Manifest validation must cover kind, compatibility, load contract, integrity, capability mismatch, and disabled-plugin behavior.
Files:
crates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/src/plugin/dynamic/worker.rs
crates/core/src/plugin/dynamic/**/*.rs
📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)
The native loader must keep libraries alive until registered callbacks are cleared and must deregister plugin kinds before unload.
Files:
crates/core/src/plugin/dynamic/host.rscrates/core/src/plugin/dynamic/native.rscrates/core/src/plugin/dynamic/worker.rs
🔇 Additional comments (27)
crates/core/tests/unit/dynamic_worker_tests.rs (1)
1481-1481: LGTM!crates/core/tests/unit/plugin_tests.rs (5)
12-12: LGTM!Also applies to: 30-40
304-413: LGTM!Also applies to: 433-436
950-978: LGTM!
1027-1066: LGTM!Also applies to: 1096-1184
1186-1235: LGTM!crates/core/src/plugin.rs (4)
863-896: LGTM!
926-993: LGTM!
1186-1389: LGTM!
1536-1712: LGTM!Also applies to: 1738-1885
crates/core/src/plugin/dynamic.rs (1)
21-58: LGTM!crates/core/src/plugin/dynamic/native.rs (1)
118-133: LGTM!Also applies to: 145-158
crates/core/src/plugin/dynamic/worker.rs (1)
164-179: LGTM!Also applies to: 191-204, 265-424, 603-603
crates/core/src/observability/plugin_component.rs (1)
58-58: LGTM!Also applies to: 547-549
crates/core/src/plugins/model_pricing.rs (1)
17-25: LGTM!crates/core/src/plugins/nemo_guardrails/component.rs (1)
18-18: LGTM!Also applies to: 400-402
crates/core/tests/integration/native_plugin_tests.rs (2)
8-144: LGTM!
1110-1614: LGTM!Also applies to: 1649-1657, 1776-1789
crates/core/src/plugin/dynamic/host.rs (2)
33-63: LGTM!Also applies to: 88-166, 210-425
167-196: 🩺 Stability & AvailabilityNo action needed The panic path still drops
PendingPluginRegistrations/PendingPluginRegistrationContext, so rollback failures are recorded during unwinding before this branch runs.failures.is_empty()here means there was nothing left to retain, so unloading the runtimes is safe.> Likely an incorrect or invalid review comment.crates/cli/src/server.rs (1)
149-203: LGTM!Also applies to: 317-379
crates/cli/tests/coverage/server_tests.rs (1)
1705-1734: LGTM!crates/core/Cargo.toml (1)
124-127: LGTM!crates/core/tests/fixtures/native_plugin/src/lib.rs (1)
283-298: LGTM!crates/core/tests/fixtures/worker_plugin/src/main.rs (1)
87-87: LGTM!Also applies to: 133-133, 156-159
crates/core/tests/integration/plugin_host_builtin_ownership_tests.rs (1)
19-80: LGTM!crates/core/tests/integration/worker_plugin_tests.rs (1)
26-31: LGTM!Also applies to: 48-128
mnajafian-nv
left a comment
There was a problem hiding this comment.
Thanks, this is moving in a good direction. I left two focused inline comments on validation/activation alignment and the retained-owner activity contract.
| @@ -908,10 +985,11 @@ pub fn list_plugin_kinds() -> Vec<String> { | |||
| /// The returned plugin is shared by [`Arc`], so callers receive a cheap clone. | |||
| pub fn lookup_plugin(plugin_kind: &str) -> Option<Arc<dyn Plugin>> { | |||
| let _ = ensure_builtin_plugins_registered(); | |||
There was a problem hiding this comment.
One thing I’d tighten before merge: this drops builtin-registration failure on the read path, while activation now treats the same condition as a hard error. With a preclaimed builtin kind, that can let validation or lookup-based checks proceed and only fail later during activation. I’d rather keep those paths aligned by surfacing the failure here, or add a regression that makes the intended mismatch explicit if that behavior is deliberate.
| if !self.active { | ||
| return Ok(()); | ||
| } | ||
| self.active = false; |
There was a problem hiding this comment.
One contract detail seems worth making explicit before merge: self.active flips to false before teardown is proven safe, but later paths intentionally retain the runtimes and host claim when callbacks cannot be cleared or unload is unsafe. In that state a binding can report inactive even though a new activation would still conflict on retained ownership. I’d either keep active true until the claim is actually released, or document and test that is_active() means 'this handle is no longer reusable' rather than 'the process is ready for another activation.'
|
CLI lifecycle selection: flowchart TD
A["Relay CLI startup"] --> B{"Any plugin configuration"}
B -->|"No"| C["Run without plugin activation"]
B -->|"Yes"| D{"Dynamic plugin specs present"}
D -->|"No"| E["Use existing static initialization"]
D -->|"Yes"| F["Create PluginHostActivation"]
E --> G["Run gateway"]
F --> G
G --> H["Close sessions and flush subscribers"]
H --> I{"Activation type"}
I -->|"Static"| J["Clear static configuration"]
I -->|"Dynamic"| K["Clear dynamic host"]
|
|
Dynamic activation: sequenceDiagram
participant Caller
participant Executor as Lifecycle executor
participant Owner as Process owner
participant Loader
participant Registry
participant Config as Configuration runtime
Caller->>Executor: Activate base config and specs
Executor->>Executor: Validate nonempty and unique IDs
Executor->>Owner: Acquire exclusive host lease
Owner-->>Executor: Return owner ID
Executor->>Registry: Ensure built-in plugin kinds
Executor->>Loader: Load native libraries
Executor->>Loader: Start worker processes
Loader->>Registry: Register adapters with registration IDs
Executor->>Config: Append dynamic components after static components
Executor->>Config: Validate and initialize components
alt Initialization succeeds
Config-->>Caller: Activation guard and report
else Initialization fails cleanly
Config->>Registry: Roll back component registrations
Loader->>Registry: Deregister adapters
Loader->>Loader: Unload libraries and workers
Owner->>Owner: Release lease
Config-->>Caller: Activation error
else Rollback cannot prove safety
Loader->>Loader: Retain loaded runtimes
Owner->>Owner: Retain lease
Config-->>Caller: Incomplete rollback error
end
|
|
Safe teardown: flowchart TD
A["Clear activation"] --> B["Flush subscribers and remove component callbacks"]
B --> C{"Callbacks proven removed"}
C -->|"No"| D["Retain runtimes and owner for process lifetime"]
D --> E["Return teardown error"]
C -->|"Yes"| F["Deregister plugin adapters by registration ID"]
F --> G{"Adapters safe to unload"}
G -->|"No"| D
G -->|"Yes"| H["Shut down worker processes"]
H --> I{"Worker shutdown safe"}
I -->|"No"| D
I -->|"Yes"| J["Unload libraries and runtimes"]
J --> K["Release process owner"]
K --> L["Return success or recoverable teardown error"]
|
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Overview
This promotes dynamic-plugin activation from CLI-private orchestration into a core-owned lifecycle for harness-native embedding hosts. It deliberately does not replace the existing static plugin lifecycle: static-only callers continue to use normal plugin initialization, while
PluginHostActivationrequires at least one explicit dynamic plugin specification.Lifecycle contract
initialize_plugins*/clear_plugin_configurationremain the static-only API.PluginHostActivation::activate(base_config, dynamic_specs)is the owned dynamic API and rejects an emptydynamic_specslist with an actionable error.Details
PluginHostActivationandDynamicPluginActivationSpecAPIs for native and worker plugins.clear()plus best-effortDropcleanup that logs failures.Where should the reviewer start?
Start with
crates/core/src/plugin/dynamic/host.rsfor the public lifecycle and executor contract, thencrates/core/src/plugin.rsfor ownership and transactional rollback.crates/cli/src/server.rsshows the static/dynamic dispatch. Native and worker integration tests cover mixed static/dynamic activation, conflicts, rollback, in-flight callbacks during teardown, cancellation, explicit clear, and drop cleanup.Validation
cargo fmt --all -- --checkcargo clippy -p nemo-relay --features worker-grpc --all-targets -- -D warningscargo clippy -p nemo-relay-cli --all-targets -- -D warningsRelated Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
None.
Summary by CodeRabbit