janiussyafiq commented on issue #13353:
URL: https://github.com/apache/apisix/issues/13353#issuecomment-4435427497

   Closing this as not-planned — the problem framing was incorrect, and what's 
left after the correction isn't strong enough to justify the feature.
   
   ### Correction: there is no "last-flagger-wins"
   
   I claimed that each `lua_body_filter` plugin reads original content from 
`ctx.var.llm_response_text`, makes its own decision, rewrites the body if it 
flags, and the client sees whichever plugin's deny shape ran last. That's not 
what the dispatcher actually does.
   
   `apisix/plugin.lua:1488-1512` exits the entire response-filter phase as soon 
as any plugin's `lua_body_filter` returns a non-nil `code` — including 
`(ngx.OK, body)`, because `0` is truthy in Lua:
   
   ```lua
   local code, new_body = phase_func(conf, api_ctx, headers, body)
   if code then
       if code ~= ngx_ok then
           ngx.status = code
       end
       ngx_print(new_body)
       ngx_exit(ngx_ok)    -- terminates the request; subsequent plugins never 
run
   end
   ```
   
   So the actual semantics are **first-flagger-wins**: the highest-priority 
plugin runs first, and if it flags, the dispatcher exits — every lower-priority 
plugin is skipped, never calls its vendor, and never gets a chance to "win." I 
verified this empirically by drafting the stacked-moderation test I had 
originally planned (ai-aliyun-content-moderation at 1029 + ai-lakera-guard at 
1028) and observing that only the aliyun mock logged a scan request when both 
would have flagged; the lakera mock was never hit.
   
   ### What's left of the original motivation
   
   After removing the bogus "last-flagger-wins" framing, the only real residual 
problem is:
   
   > Additive vendor cost on the **clean path** when an operator stacks two 
moderation plugins.
   
   That's true, but it's a weak motivation for plugin-internal coordination 
machinery:
   
   - Operators who stack two moderation plugins implicitly opted in to paying 
both vendors on clean requests.
   - If cost matters, the natural answer is to not stack — pick one primary, or 
order by cost ascending and accept the additive cost as the price of 
defense-in-depth.
   - On the flagged path (which is typically the more interesting cost concern) 
the dispatcher already short-circuits — additional vendors don't get called.
   
   A `coordinate_with_siblings` flag plus shared `ai_moderation_decided` ctx 
vars introduces real complexity (per-plugin opt-in, ordering rules, what 
happens if two flag, how to expose disagreement, etc.) for a fairly narrow 
optimization. Not worth carrying.
   
   ### If anyone hits this in the future
   
   The current first-flagger-wins behavior is sensible and well-defined. If you 
actually need both vendors' verdicts visible (observability, A/B comparison, 
etc.), a much lighter approach is per-plugin `action: alert` modes that 
log/observe but never inject a deny — no dispatcher coordination needed. Both 
`ai-lakera-guard` and `ai-aliyun-content-moderation` already support this 
pattern.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to