Uh oh!
There was an error while loading. Please reload this page.
fix(guardrails): scan tool-call args on chat.rs streaming output (#558) - #559
Conversation
chat.rs streaming buffered only delta.content for the end-of-stream check_output, so a blocked literal in a streamed tool-call's function.arguments bypassed the output guardrail — asymmetric with chat non-streaming (guardrail_output_text, #448) and /v1/messages streaming, which both scan tool calls. Accumulate tool-call name + arguments from chunk.delta.tool_calls across the stream (bounded to the hold-back cap; concatenated without a separator so a literal split across deltas reassembles) and merge them into the scanned text at both end-of-stream check sites (BufferFull/Window hold-back and EndOfStreamCheck). Under the default BufferFull policy the tool-call chunks are already held in pending, so a block holds them back (never on the wire). Test: a streamed tool_call delta with a blocked literal in arguments blocks the stream (event: error / content_filter) and the args never reach the wire. fmt + clippy clean; 403 lib tests pass. Closes#558.
Warning Review limit reached
More reviews will be available in 15 minutes and 53 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (2)
Note 🎁 Summarized by CodeRabbit FreeYour organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above. Comment |
…mode residual (#558) Audit of #559 found two issues: - HIGH-1: the fix shipped with NO test (the prior commit ran `git add chat.rs` only, so the lib.rs test was never committed — yet the PR/commit claimed one). Add streaming_output_guardrail_blocks_tool_call_arguments: a tool-call-only stream with a blocked literal in function.arguments -> event: error / content_filter, and the args never reach the wire (RED without the fix, GREEN with it). - MEDIUM-1: Window mode released a content window (and its held tool-call chunks) BEFORE the tool-call text was scanned (scanned only at end-of-stream -> too late). Fold tool_calls_buf into the per-window scan so a blocked tool-call arg is caught before its events are released. fmt + clippy clean; 403 lib tests pass.
moonming
commented
Jun 8, 2026
Audit of this PR returned DON'T-MERGE on two items, both now addressed in the latest commit:
The audit verified the default BufferFull path was already correct (real hold-back, not luck). fmt + clippy clean; 403 lib tests pass. |
Uh oh!
There was an error while loading. Please reload this page.
What
On
/v1/chat/completionsstreaming, the output guardrail buffered onlydelta.contentfor the end-of-streamcheck_output;delta.tool_callswas never accumulated. So a blocked literal in a streamed tool-call'sfunction.argumentsbypassed the output guardrail. This is asymmetric: chat non-streaming scans tool-call output (ChatResponse::guardrail_output_text, the #448 fix), and/v1/messagesstreaming scans them too — only chat streaming leaked. Surfaced by the independent pre-fix audit of the #719 follow-up batch.How
Accumulate tool-call
name+argumentsfromchunk.delta.tool_callsacross the stream (bounded toDEFAULT_STREAM_OUTPUT_BUFFER_BYTES; concatenated without a separator so a literal split across deltas reassembles), and merge them into the scanned text at both end-of-stream check sites (the hold-back BufferFull/Window path and the EndOfStreamCheck path). Under the default BufferFull policy the tool-call chunks are already held inpending(line ~2399), so a block holds them back — they never reach the wire.Test plan
A streamed
tool_calldelta carrying a blocked literal inargumentsblocks the stream (event: errorwitherror.type: content_filter) and the args never appear on the wire (held back). Mirrors the existingstreaming_output_guardrail_blocks_with_sse_error_event_and_no_donetest. fmt + clippy clean; 403aisix-proxylib tests pass.Closes#558. Refs #719, #546, #448.