Repository navigation
fix(security): render guardrails stream chunks as text instead of reprs - #2308
Merged
rapids-bot[bot] merged 1 commit intoOct 15, 2026
Merged
Conversation
The guardrails middleware buffered the wrapped function's stream and joined str(chunk) for every chunk, so callers received Python reprs of typed chunks (for example ChatResponseChunk) instead of the reply text. The token-by-token output rails path fed the same reprs to LLMRails.stream_async(). Both paths now use the shared stream_chunk_to_text() renderer that the other defense middlewares already use, so typed chunks are converted through the global type converter to their text representation. Signed-off-by: CodeAlex52 <59381946+CodeAlex52@users.noreply.github.com>
This was referenced Oct 11, 2026
Member
|
/ok to test 5217277 |
rapids-bot Bot
pushed a commit
that referenced
this pull request
Oct 15, 2026
…ddleware tests (#2340) Closes #2316. The content-guard, output-verifier and PII defense middleware tests each built the same hand-written stream (`ChatResponseChunk` "Hello " then "world"). This moves it into one `structured_chunk_stream` fixture in a new `packages/nvidia_nat_security/tests/middleware/conftest.py` and points the three tests at it. Tests only, no behavior change. Scope note: #2234 and #2308 are not merged yet, so their guardrails copies are not on `develop`. The fixture lives at the `middleware/` level so those tests can adopt it once they land. ## Testing ``` python -m pytest packages/nvidia_nat_security/tests/middleware/defense -q 138 passed ``` Same 138 pass on unmodified `develop`. yapf + ruff (pinned pre-commit versions) clean. ## By Submitting this PR I confirm: - I am familiar with the Contributing Guidelines. - All commits are signed-off (DCO). ## Summary by CodeRabbit * **Tests** * Standardized streamed-response test scenarios across security middleware checks with consistent multi-part text. * Preserved checks that streamed text is analyzed correctly and response chunks are delivered as expected. * **No user-facing changes** * This update changes test coverage only. Authors: - Colin McDonough (https://github.com/ColinM-sys) Approvers: - Will Killian (https://github.com/willkill07) URL: #2340
willkill07
approved these changes
Oct 15, 2026
Member
|
/merge |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2230
Problem
With a
guardrailsmiddleware attached to a streaming workflow, the caller receives Pythonrepr()dumps of the stream chunks instead of the reply text.GuardrailsMiddleware.function_middleware_streambuffered the wrapped function's output andreconstructed it with:
NAT's own agents stream typed chunks (
ChatResponseChunk), sostr(chunk)producesChatResponseChunk(...)reprs. The token-by-token path has the same problem: itsupstream()generator feeds
str(chunk)intoLLMRails.stream_async(), so the output rails evaluate reprsrather than reply text.
Fix
Both paths now render each chunk with
stream_chunk_to_text(), the shared helper the otherdefense middlewares (
defense_middleware_pii,..._content_guard,..._output_verifier) alreadyuse. It routes non-
strvalues through the global type converter —ChatResponseChunkisregistered there in
nat/data_models/api_server.pyaschoices[0].delta.content— and falls backto
str()for values without a text representation, so arbitrary stream items keep working.Tests
Two cases in
packages/nvidia_nat_security/tests/middleware/guardrails/test_nemo_guardrails_middleware.py:test_buffered_stream_renders_typed_chunks_as_text— default (buffered) path: twoChatResponseChunks in,["Hello world"]out. Fails before the fix with the repr form.test_stream_output_rails_evaluate_text_not_chunk_reprs—stream_output_rails=True: assertsthe pieces handed to the generator consumed by
LLMRails.stream_async()are the reply text.Fails before the fix.
Validation
pytest packages/nvidia_nat_security/tests/middleware→ 196 passed (the guardrails middlewarefile alone: 37 passed, +2 over the previous 35).
yapf --style ./pyproject.tomlandruff check(v0.15.0, the versions pinned in.pre-commit-config.yaml) are clean on both files.tests/eval/**could not run in this environment (nvidia_nat_evalis not installed in mycheckout), and I did not run the full-repo suite.
Summary by CodeRabbit