fix(streaming): don't reuse stale across output-rail chunks (#1935) - #1943
Conversation
…nks (NVIDIA-NeMo#1935) Signed-off-by: Aleksandr Popov <alexander230r@gmail.com>
Greptile SummaryThis PR fixes a stale-parameter bug in streaming output rails where
|
| Filename | Overview |
|---|---|
| nemoguardrails/rails/llm/utils.py | Defensive copy added at the return site of get_action_details_from_flow_id to isolate the shared flow-config action_params from callers that mutate the returned dict. |
| nemoguardrails/rails/llm/llmrails.py | _prepare_params now creates a local resolved_params copy before performing $bot_message/$user_message substitution, eliminating in-place mutation of the shared action_params reference across streamed chunks. |
| tests/test_streaming_output_rails.py | Two new async tests exercise the fixed substitution for $bot_message and $user_message across multiple streamed chunks; coverage is appropriate for the bug being fixed. |
Sequence Diagram
sequenceDiagram
participant ChunkLoop as "chunk loop"
participant GetDetails as "get_action_details_from_flow_id"
participant FlowConfig as "flow config (shared)"
participant PrepareParams as "_prepare_params"
participant Action as "output rail action"
Note over FlowConfig: "action_params = {text: $bot_message}"
ChunkLoop->>GetDetails: "get_action_details(flow_id) — chunk N"
GetDetails->>FlowConfig: "read element[action_params]"
GetDetails-->>ChunkLoop: "dict(action_params) — fresh copy"
ChunkLoop->>PrepareParams: "_prepare_params(action_params=copy)"
PrepareParams->>PrepareParams: "resolved_params = dict(action_params)"
PrepareParams->>PrepareParams: "resolved_params[text] = bot_chunk_N"
PrepareParams-->>ChunkLoop: "params with resolved text"
ChunkLoop->>Action: "execute(params)"
Action-->>ChunkLoop: "result"
Note over FlowConfig: "action_params still {text: $bot_message}"
ChunkLoop->>GetDetails: "get_action_details(flow_id) — chunk N+1"
GetDetails->>FlowConfig: "read element[action_params] (unchanged)"
GetDetails-->>ChunkLoop: "dict(action_params) — fresh copy again"
Reviews (2): Last reviewed commit: "fix: test coverage for changes in PR" | Re-trigger Greptile
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR fixes a bug where streaming output rails were reusing stale parameter substitutions across chunks. The fix copies action parameters before resolving placeholders, ensuring each chunk's ChangesPrevent stale parameter substitution in streaming output rails
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related issues
🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Signed-off-by: Aleksandr Popov <alexander230r@gmail.com>
|
Heads-up for @Alexander230 and @Pouyanpi — the lone Python 3.10 CI failure on this PR isn't from the streaming fix. The failing test is Source link — the test compares A Independent of all that: nice fix on the streaming bug, especially the test in |
…erlap check Closes NVIDIA-NeMo#1953. `test_parallel_rails_success` previously asserted that `input_rails_duration < 1.5s` and `output_rails_duration < 1.5s`. The threshold was chosen to sit between the parallel-execution lower bound (~1.0s, two `asyncio.sleep(1.0)` in flight) and the serial-execution upper bound (~2.0s). On a slow / loaded CI runner, event-loop scheduling overhead alone can push the parallel duration above 1.5s, producing a false-positive failure even though parallelism worked correctly. Reproduced on Ubuntu / Python 3.10 in NVIDIA-NeMo#1943's matrix. Replace the wall-clock threshold with a structural overlap check: two rails are running in parallel iff the second rail starts before the first finishes. `ActivatedRail` already exposes `started_at` / `finished_at` (see `nemoguardrails/rails/llm/options.py`), so the data is there. The new check is applied to both the input-rails and output-rails buckets. The previous index-based name assertions are preserved unchanged. Signed-off-by: ppcvote <risky9763@gmail.com>
Pouyanpi
left a comment
There was a problem hiding this comment.
Thank you @Alexander230 for fixing this bug 🚀
…eMo#1935) (NVIDIA-NeMo#1943) Signed-off-by: Aleksandr Popov <alexander230r@gmail.com>
Fix for #1935. Prevents erratic in-place mutations for
$bot_message/$user_message.