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]