feat(library): namespace custom task prompts in self-check#2175
Conversation
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Characterize current-invocation precedence so a bare rail cannot inherit a custom task from earlier event history. Refs: #1874 (comment) Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Record provider-backed self-check execution through generate_async and stream_async for sequential, parallel, normal streaming, and chunk-level output-rail streaming paths. Keep main generation deterministic with a fake model while all rail decisions replay from real provider cassettes. Refs: #1874 Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Greptile SummaryThis PR namespaces custom self-check prompt task identifiers under their parent default task (e.g.
|
| Filename | Overview |
|---|---|
| nemoguardrails/library/self_check/utils.py | Core change: renames $task to $variant param, adds get_self_check_prompt_task for namespaced prompts with a deprecation fallback for bare task keys. |
| nemoguardrails/rails/llm/config.py | Refactors _validate_self_check_prompts into _validate_self_check_rail_prompts using get_self_check_prompt_task for namespacing, but hardcodes variant string instead of using SELF_CHECK_VARIANT_PARAM. |
| tests/test_multiple_self_check_rails.py | Updated all tests to use $variant= and namespaced prompt keys; adds new tests for bare-task deprecation and get_self_check_prompt_task namespacing. |
| tests/v2_x/test_multiple_self_check_rails.py | New Colang 2 coverage for multiple custom input/output self-check tasks using the $variant= syntax. |
| tests/recorded/rails/library/test_self_check.py | Adds recorded public-API tests covering multi-task sequential, parallel, streaming, and error propagation scenarios. |
| tests/integrations/langchain/test_streaming.py | Updates existing streaming test to use $variant= and adds new tests for multiple custom tasks in streaming input/output rails. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Rail config YAML] --> B[_validate_self_check_rail_prompts]
B --> C[get_self_check_prompt_task]
C -->|default task| D[Return default task]
C -->|custom, new-style| E[Return namespaced key]
C -->|custom, bare key only| F[DeprecationWarning + return bare key]
D --> G[validate prompt exists]
E --> G
F --> G
subgraph Runtime
H[self_check_input/output action] --> I[resolve_self_check_task]
I --> J[run_self_check_task]
J --> K[get_self_check_llm by bare name]
J --> L[get_self_check_prompt_task namespaced key]
L --> M[render_task_prompt]
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Rail config YAML] --> B[_validate_self_check_rail_prompts]
B --> C[get_self_check_prompt_task]
C -->|default task| D[Return default task]
C -->|custom, new-style| E[Return namespaced key]
C -->|custom, bare key only| F[DeprecationWarning + return bare key]
D --> G[validate prompt exists]
E --> G
F --> G
subgraph Runtime
H[self_check_input/output action] --> I[resolve_self_check_task]
I --> J[run_self_check_task]
J --> K[get_self_check_llm by bare name]
J --> L[get_self_check_prompt_task namespaced key]
L --> M[render_task_prompt]
end
Reviews (3): Last reviewed commit: "refactor(self-check): align internal par..." | Re-trigger Greptile
📝 WalkthroughWalkthroughSelf-check task handling now derives namespaced prompt identifiers for custom tasks. Rails configuration validation checks those identifiers, while unit, integration, recorded, streaming, and v2 tests cover multi-task execution, routing, blocking, parallelism, and provider errors. ChangesSelf-check task namespacing
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant RailsConfig
participant SelfCheckRuntime
participant TaskSpecificLLM
participant RailsStream
RailsConfig->>RailsConfig: validate self_check_* $task prompt
RailsStream->>SelfCheckRuntime: run input/output self-check
SelfCheckRuntime->>TaskSpecificLLM: submit namespaced prompt
TaskSpecificLLM-->>SelfCheckRuntime: return safe or unsafe
SelfCheckRuntime-->>RailsStream: return parsed self-check result
RailsStream-->>RailsStream: emit normal or guardrail-violation chunks
🚥 Pre-merge checks | ✅ 4 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 `@tests/integrations/langchain/test_streaming.py`:
- Around line 574-579: Strengthen the streaming assertion around
chat.app.stream_async by verifying that no collected chunk contains the
blocked_data_leakage marker before asserting the final error parameter. Keep the
existing error parsing and final self-check assertion intact while explicitly
enforcing that blocked content is never emitted.
🪄 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: CHILL
Plan: Enterprise
Run ID: 19001ae9-2b4e-40cf-9ab8-d40c28e18706
📒 Files selected for processing (20)
nemoguardrails/library/self_check/utils.pynemoguardrails/rails/llm/config.pytests/integrations/langchain/test_streaming.pytests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_generate_async_parallel_runs_all_tasks.yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_generate_async_sequential_blocks_output.yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_input_provider_error_raises.yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_input_second_task_blocks.yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_input_tasks_allow.yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_output_rails_streaming[parallel-allows].yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_output_rails_streaming[sequential-blocks].yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_output_second_task_blocks.yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_output_second_task_blocks_fake_main_stream.yamltests/recorded/rails/library/cassettes/test_self_check/test_multiple_self_check_stream_async_runs_input_tasks.yamltests/recorded/rails/library/configs.pytests/recorded/rails/library/configs/openai_multi_self_check/config.ymltests/recorded/rails/library/configs/openai_multi_self_check_invalid_model/config.ymltests/recorded/rails/library/test_self_check.pytests/test_multiple_self_check_rails.pytests/test_rails_config.pytests/v2_x/test_multiple_self_check_rails.py
The self-check rail flow parameter and the synthesized prompt-key suffix used $task=, which collides with NeMo naming every prompt-key id a "task": the prompt line read `task: self_check_output $task=...`, saying "task" twice for two different meanings. Rename the parameter to $variant=, which names what the value is, a subordinate variant of the base check selecting an alternate prompt and model. The parameter is unreleased, so no backward-compatibility path is required. Covers Colang 1 and 2 flows, config validation, and recorded, streaming, and unit test coverage.
Follow-up to the $task= -> $variant= rename: rename the internal constants and function arguments that name the config param (SELF_CHECK_*_TASK_PARAM -> SELF_CHECK_*_VARIANT_PARAM, task_param -> variant_param) so param-level names match the wire name. Names that refer to the resolved task value (default_task, the action task= kwarg, get_self_check_prompt_task) stay task, since the value is still used as a task for prompt lookup and model routing.
The self-check flow passed its $variant value into the action as task=, so the action signature read self_check_input(task=...) while the config-facing selector is $variant=. Rename the carrier to variant across the flow-to-action boundary: both flows.co calls (variant=$variant), both action signatures (task -> variant), and resolve_self_check_task input parameter. The resolved value stays task from resolve_self_check_task return onward, since it keys the prompt template and model routing. The action parameter is unreleased (added with the multiple-self-check feature), so no compatibility path is needed. follow-up on #2175
Description
Follow-up to #1874. Namespace custom self-check prompt task identifiers under
self_check_inputandself_check_outputwhile preserving task-specific modelrouting.
Adds Colang 1 and 2 coverage plus recorded public-API coverage for sequential,
parallel, and streaming execution. Review requested from @RobGeada.
AI Assistance
Checklist
Summary by CodeRabbit
New Features
Bug Fixes
Tests