Commit 2243ec3
authored
feat: implement nemo_guardrails remote backend (#144)
#### Overview
Adds the first real built-in `nemo_guardrails` remote backend for NeMo Relay core.
This PR moves beyond the contract-only surface and implements the first shippable remote slice:
- built-in plugin auto-registration in core
- remote backend activation for `mode = remote`
- non-streaming and streaming LLM execution through the Guardrails server
- managed `tool_output` execution checks through the same remote backend
- request-default pass-through for remote Guardrails request semantics
- explicit validation that stock remote mode does not currently support managed `tool_input`
- focused runtime validation and coverage for transport, malformed responses, tool rewrites, and error handling
- [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
- auto-register the built-in `nemo_guardrails` plugin through `ensure_builtin_plugins_registered()`
- add a dedicated default core feature `guardrails-remote` for the remote backend dependency path
- implement the remote Guardrails runtime across:
- `crates/core/src/plugins/nemo_guardrails/component.rs`
- `crates/core/src/plugins/nemo_guardrails/remote.rs`
- support the native remote server contract for `codec = "openai_chat"`
- support both non-streaming and streaming LLM execution
- support remote request defaults pass-through:
- `context`
- `thread_id`
- `state`
- `rails`
- `llm_params`
- `llm_output`
- `output_vars`
- `log`
- support managed `tool_output` execution checks via the remote backend
- reject managed `tool_input` in stock remote mode with a focused validation error instead of silently leaving it non-enforcing
- emit coarse backend-level `nemo_guardrails.remote.*` marks for both LLM and managed tool remote checks
- precompute stable remote Guardrails payloads during runtime initialization instead of rebuilding them per request
- split remote runtime coverage into `component_tests.rs` and `remote_tests.rs` to keep contract/config tests separate from transport/runtime behavior
- add focused unit coverage for:
- built-in registration
- remote config validation
- transport and malformed-response lanes
- non-streaming and streaming remote execution
- managed `tool_output` blocking and rewrite behavior
- tool-only config isolation from LLM execution
- context/state/thread propagation on managed tool checks
Rail and observability boundary in this PR:
- Native NeMo Relay-managed surfaces:
- `input`
- `output`
- `tool_output`
- These are real runtime interception surfaces in NeMo Relay. The built-in plugin installs managed LLM and tool execution behavior around them, and the remote backend can block or rewrite those executions directly.
- Managed `tool_input` remains a known NeMo Relay surface, but stock remote mode now rejects it explicitly because the Guardrails remote contract does not currently activate pre-execution tool-call rails from externally submitted chat-completions history.
- Remote request-time rail pass-through:
- `request_defaults.rails.input`
- `request_defaults.rails.output`
- `request_defaults.rails.retrieval`
- `request_defaults.rails.dialog`
- `request_defaults.rails.tool_input`
- `request_defaults.rails.tool_output`
- These are forwarded to the Guardrails server as remote request semantics. In this PR, `retrieval` and `dialog` are supported this way only: NeMo Relay does not currently expose separate managed retrieval or dialog execution surfaces to intercept locally.
- The remote backend emits coarse backend-level marks:
- `nemo_guardrails.remote.start`
- `nemo_guardrails.remote.end`
- `nemo_guardrails.remote.error`
- These marks cover managed LLM remote execution and managed `tool_output` remote checks. They provide backend-level visibility into remote Guardrails activity without introducing separate NeMo Relay-native retrieval or dialog scopes in this slice.
Intentional PR2 boundary:
- native remote support is `openai_chat` only
- local backend is still not implemented
- stock remote mode supports managed `tool_output` but not managed `tool_input`
- this stays core-only; binding surface enablement is deferred
Validation:
- `cargo fmt --all`
- `cargo test -p nemo-relay nemo_guardrails --lib --tests`
- `cargo clippy --workspace --all-targets -- -D warnings`
- `uv run pre-commit run --files crates/core/src/plugins/nemo_guardrails/component.rs crates/core/tests/unit/plugins/nemo_guardrails/component_tests.rs crates/core/tests/unit/plugins/nemo_guardrails/remote_tests.rs`
Targeted live validation covered:
- allowed and blocked non-streaming LLM requests
- streaming requests
- trusted HTTPS transport
- managed `tool_output` blocked path
- direct stock Guardrails repro confirming externally supplied pre-exec tool-call history does not activate remote managed `tool_input`
Notes:
- `uv run pre-commit run --all-files` still reports an unrelated repo-environment failure in `ty` for unresolved `deepagents` integration imports
- unrelated `package-lock.json` churn was restored and is not part of this PR
#### Where should the reviewer start?
Start in `crates/core/src/plugins/nemo_guardrails/component.rs`, then `crates/core/src/plugins/nemo_guardrails/remote.rs`.
The most important design decision in this PR is that the built-in plugin now implements a real remote execution backend while keeping the remote contract honest:
- native remote support is OpenAI chat-completions shaped
- LLM and managed `tool_output` execution are supported through the core runtime
- stock remote managed `tool_input` is explicitly rejected instead of being left as a silent best-effort path
- broader non-chat codec parity and the local Python runtime remain out of scope for this slice
#### Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)
- Relates to #NMF-131
- Relates to #NMF-148
Authors:
- https://github.com/afourniernv
Approvers:
- Will Killian (https://github.com/willkill07)
URL: #1441 parent fb75181 commit 2243ec3
7 files changed
Lines changed: 3014 additions & 35 deletions
File tree
- crates/core
- src
- plugins/nemo_guardrails
- tests/unit/plugins/nemo_guardrails
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
19 | 23 | | |
20 | 24 | | |
21 | 25 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
762 | 762 | | |
763 | 763 | | |
764 | 764 | | |
765 | | - | |
766 | | - | |
767 | | - | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
768 | 770 | | |
769 | 771 | | |
770 | 772 | | |
| |||
Lines changed: 134 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
20 | 26 | | |
21 | 27 | | |
22 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
23 | 39 | | |
24 | 40 | | |
25 | 41 | | |
| |||
182 | 198 | | |
183 | 199 | | |
184 | 200 | | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
185 | 207 | | |
186 | 208 | | |
187 | 209 | | |
| |||
307 | 329 | | |
308 | 330 | | |
309 | 331 | | |
| 332 | + | |
| 333 | + | |
310 | 334 | | |
311 | 335 | | |
312 | 336 | | |
| |||
349 | 373 | | |
350 | 374 | | |
351 | 375 | | |
352 | | - | |
353 | | - | |
| 376 | + | |
| 377 | + | |
354 | 378 | | |
355 | | - | |
356 | | - | |
357 | | - | |
358 | | - | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
359 | 383 | | |
360 | 384 | | |
361 | 385 | | |
| |||
419 | 443 | | |
420 | 444 | | |
421 | 445 | | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
422 | 461 | | |
423 | 462 | | |
424 | 463 | | |
| |||
497 | 536 | | |
498 | 537 | | |
499 | 538 | | |
| 539 | + | |
| 540 | + | |
500 | 541 | | |
501 | 542 | | |
502 | 543 | | |
| |||
526 | 567 | | |
527 | 568 | | |
528 | 569 | | |
| 570 | + | |
529 | 571 | | |
530 | 572 | | |
531 | 573 | | |
| |||
869 | 911 | | |
870 | 912 | | |
871 | 913 | | |
| 914 | + | |
| 915 | + | |
| 916 | + | |
| 917 | + | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
872 | 951 | | |
873 | 952 | | |
874 | 953 | | |
| |||
885 | 964 | | |
886 | 965 | | |
887 | 966 | | |
| 967 | + | |
| 968 | + | |
| 969 | + | |
| 970 | + | |
| 971 | + | |
| 972 | + | |
| 973 | + | |
| 974 | + | |
| 975 | + | |
| 976 | + | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
| 981 | + | |
| 982 | + | |
| 983 | + | |
| 984 | + | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
888 | 1015 | | |
889 | 1016 | | |
890 | 1017 | | |
| |||
1138 | 1265 | | |
1139 | 1266 | | |
1140 | 1267 | | |
1141 | | - | |
| 1268 | + | |
1142 | 1269 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
| 14 | + | |
0 commit comments