Skip to content

fix(streaming): handle curly quotes and flush buffered replies - #2517

Draft
Pouyanpi wants to merge 1 commit into
NVIDIA-NeMo:developfrom
Pouyanpi:pouyanpi/curly-quotes-minimal
Draft

Pouyanpi wants to merge 1 commit into
NVIDIA-NeMo:developfrom
Pouyanpi:pouyanpi/curly-quotes-minimal

Conversation

@Pouyanpi

@Pouyanpi Pouyanpi commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Deferred to a future release under NGR-1070; resolves NGR-1047 as part of the later reasoning-model migration. PR #2550 supplies the independent v0.25.0 fix for NGR-1045. Retained as a draft, with #2516 stacked on this branch.

Description

Handle matching curly quote delimiters in generated bot messages, strip closing delimiters before trailing whitespace, and flush buffered replies at end-of-stream. Preserve straight-quoted multiline replies containing inner curly quotes.

Smaller replacement for #2515; no quote-depth tracking or new dialogue-boundary inference. Arbitrary nested curly quotations and extra-dialogue recovery remain outside scope.

Related Issue(s)

Resolves NGR-1047. Supersedes #2515.

Verification

366 related tests passed, including streaming, generation, and IORails coverage. Pre-commit passed. Full suite and live-provider tests not run.

AI Assistance

Codex assisted with implementation, tests, and review.

Checklist

  • Regression tests added; validation recorded.
  • Conventional commit title and DCO sign-off.
  • No documentation or generated changelog changes.
  • Maintainer review (draft).

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of straight and curly quotes in streamed and multiline responses, preserving quoted message content and correctly recognizing message boundaries.
    • Updated streaming behavior so buffered text and end-of-stream signals are delivered in the correct order.
  • Tests
    • Added coverage for quote handling, multiline responses, and streaming across different generation modes.

@github-actions github-actions Bot added status: needs triage New issues that have not yet been reviewed or categorized. size: M labels Oct 28, 2026
@Pouyanpi Pouyanpi self-assigned this Oct 28, 2026
@Pouyanpi Pouyanpi removed the status: needs triage New issues that have not yet been reviewed or categorized. label Nov 3, 2026
@Pouyanpi
Pouyanpi marked this pull request as ready for review November 3, 2026 10:04
@Pouyanpi Pouyanpi added the status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile). label Nov 3, 2026
@coderabbitai

coderabbitai Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Walkthrough

Streaming and response parsing now support straight and curly quote delimiters. Buffered streaming preserves end-of-stream signals, and generation appends the handoff marker after parsing.

Changes: Quote-aware response streaming

Layer / File(s) Summary
Alternative patterns and buffered stream handling
nemoguardrails/streaming.py, nemoguardrails/actions/llm/generation.py, tests/test_streaming_handler.py
StreamingHandler accepts alternative prefixes and suffixes. It handles buffered end-of-stream markers after buffered text, and tests cover quote patterns, chunking, and buffering.
Quote-aware parsing and generation
nemoguardrails/llm/completion_parsing.py, nemoguardrails/actions/llm/generation.py, tests/llm/test_completion_parsing.py, tests/test_generation_equivalence.py, tests/test_llmrails_singlecall.py
Parsing recognizes straight and curly quote delimiters. Generation configures both quote forms and appends the handoff marker after output parsing. Tests cover multiline responses and single-call and regular generation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Possibly related PRs

Suggested reviewers: christinaexyou, tgasser-nv

Merge Risk: Minimal · up to f5b9d

No identified issue remains that would block merging after normal checks.

Pre-merge checks: Passed 5 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 40.91% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 5 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the two main changes in the PR: curly quote delimiter handling and flushing of buffered replies at end-of-stream. The raw summary confirms both areas changed in generation.py, completi…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Test Results For Major Changes Passed The PR introduces a substantial streaming and quote-parsing change, and the description documents testing information: 366 related tests passed and pre-commit passed. Numeric, convergence, and perform…
Full details: Title check

Explanation

The title names the two main changes in the PR: curly quote delimiter handling and flushing of buffered replies at end-of-stream. The raw summary confirms both areas changed in generation.py, completion_parsing.py, and streaming.py, with matching tests. The title follows the conventional commit style and is specific enough for a teammate to scan history and understand the primary change.

Full details: Test Results For Major Changes

Explanation

The PR introduces a substantial streaming and quote-parsing change, and the description documents testing information: 366 related tests passed and pre-commit passed. Numeric, convergence, and performance impact conditions do not apply.

Full details: Docstring Coverage

Explanation

Docstring coverage is 40.91% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 22 functions across 7 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 731f6d0 and f5b9d62.

Files selected for processing: 7
  • nemoguardrails/actions/llm/generation.py
  • nemoguardrails/llm/completion_parsing.py
  • nemoguardrails/streaming.py
  • tests/llm/test_completion_parsing.py
  • tests/test_generation_equivalence.py
  • tests/test_llmrails_singlecall.py
  • tests/test_streaming_handler.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

@Pouyanpi

Pouyanpi commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator Author

@greptile review this PR

@greptile-apps

greptile-apps Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with the previously reported low-impact quote-loss issue still outstanding.

Findings

  1. P2 Final inner quote disappears ▶
Fix with agent prompt
### Issue 1
nemoguardrails/streaming.py:420-424
Streaming removes a final curly quote even when the reply opened with a straight quote. If the provider ends with `  "She said “hello”` before closing the outer quote, `_strip_current_suffix` removes the inner `”`. Non-streaming `strip_quotes` preserves it, so callers receive different text.

Choose the closing quote from the matched opening quote rather than stripping either alternative.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR adds curly-quote support and fixes how buffered replies reach streaming callers.

  • Multiline replies stop at their matching outer quote.
  • Streaming replies accept either quote style and flush before they end.
  • Single-call streaming delivers buffered replies in order.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Model chunks] --> B[Buffer]
  B --> C[Read two intent lines]
  C --> D[Parse intents]
  D --> E[Attach stream marker]
  E --> F[Bot message phase]
  F --> G[Release buffered reply]
  G --> H[Forward saved end signal]
Loading

Reviews (2) · Last reviewed commit: "fix(streaming): support curly quote deli..." · Reviewed by Greptile

Comment on lines +420 to +424
suffixes = (self.suffix,) if isinstance(self.suffix, str) else self.suffix
for candidate in (self.current_chunk, self.current_chunk.rstrip()):
for suffix in suffixes:
if suffix and candidate.endswith(suffix):
self.current_chunk = candidate[: -len(suffix)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Final inner quote disappears

Streaming removes a final curly quote even when the reply opened with a straight quote. If the provider ends with "She said “hello” before closing the outer quote, _strip_current_suffix removes the inner ”. Non-streaming strip_quotes preserves it, so callers receive different text.

Choose the closing quote from the matched opening quote rather than stripping either alternative.

Prompt To Fix With AI
This is a comment left during a code review.
Path: nemoguardrails/streaming.py
Line: 420-424

Comment:
**Final inner quote disappears**

Streaming removes a final curly quote even when the reply opened with a straight quote. If the provider ends with `  "She said “hello”` before closing the outer quote, `_strip_current_suffix` removes the inner `”`. Non-streaming `strip_quotes` preserves it, so callers receive different text.

Choose the closing quote from the matched opening quote rather than stripping either alternative.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

…lies

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: M status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant