Skip to content

fix(ai-content-moderation): stop re-counting converted chunks in realtime batches - #13765

Open
shreemaan-abhishek wants to merge 1 commit into
apache:masterfrom
shreemaan-abhishek:fix/cm-realtime-chunk-double-count
Open

fix(ai-content-moderation): stop re-counting converted chunks in realtime batches#13765
shreemaan-abhishek wants to merge 1 commit into
apache:masterfrom
shreemaan-abhishek:fix/cm-realtime-chunk-double-count

Conversation

@shreemaan-abhishek

Copy link
Copy Markdown
Contributor

Description

In stream_check_mode = realtime, ai-aws-content-moderation and
ai-aliyun-content-moderation build their moderation batch from
ctx.llm_response_contents_in_chunk, which ai-providers/base.lua refills once
per upstream chunk. When a protocol converter is active (e.g. an Anthropic
client over an OpenAI upstream), that upstream chunk is dispatched to the client
as several converted chunks and lua_body_filter runs once per converted
chunk, appending the same texts every time. Consequences:

  • the batch sent to Comprehend / aliyun held the text N times over,
  • stream_check_cache_size tripped N times sooner than configured,
  • moderation request volume scaled with the converter's fan-out, adding latency
    and third-party cost on cross-protocol routes.

Measured with the fixture used by the new test: one upstream chunk fans out into
7 Anthropic events and produced 7 identical Comprehend calls instead of 1.
Without a converter (1 upstream chunk -> 1 downstream chunk) nothing was
duplicated, which is why this went unnoticed. Moderation coverage was never
affected - the duplicated batch still contained everything.

Fix: base.lua bumps a per-upstream-chunk counter
(ctx.llm_response_chunk_seq) where it already resets
ctx.llm_response_contents_in_chunk; each plugin records the sequence it last
consumed and takes the chunk's text only on its first run. With the counter
absent (callers other than the streaming loop) behaviour is unchanged.

Accumulating the body argument instead was considered and rejected: body is
the raw SSE frame of the converted chunk, so the batch would carry the JSON
envelope rather than the response text, and stream_check_cache_size would end
up measuring envelope bytes.

Tests added:

  • t/plugin/ai-aws-content-moderation.t TEST 33 (repeated filter runs for one
    upstream chunk take its text once) and TEST 34/35 (end-to-end Anthropic
    client over OpenAI upstream: Comprehend is called exactly once; fails on
    master with 7 identical calls).
  • t/plugin/ai-aliyun-content-moderation.t TEST 74 and TEST 75/76, same shape.

Behaviour and configuration are unchanged, so no documentation update is needed.

Follow-up to #13735, raised by @AlinsRan in review
(discussion_r3672582793).

Which issue(s) this PR fixes:

No separate issue - reported in review of #13735.

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change (n/a - no user-visible behaviour or schema change)
  • I have verified that this change is backward compatible

…time batches

The realtime batch accumulator read ctx.llm_response_contents_in_chunk, which
base.lua refills once per upstream chunk. With a protocol converter active one
upstream chunk is dispatched as several converted chunks, so lua_body_filter ran
once per converted chunk and appended the same texts every time: the moderation
batch held the text N times over, stream_check_cache_size tripped N times
sooner, and request volume scaled with the converter's fan-out.

base.lua now bumps a per-upstream-chunk counter where it resets the texts, and
each plugin takes them only on its first run for that counter.
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Jul 30, 2026

@membphis membphis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

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

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants