This is an automated email from the ASF dual-hosted git repository.

shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 11c2d580b feat(ai-aws-content-moderation): add request_check_roles and 
request_check_mode (#13773)
11c2d580b is described below

commit 11c2d580b9933350efa54a379e24cb93504c2d1a
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Thu Aug 6 12:55:52 2026 +0800

    feat(ai-aws-content-moderation): add request_check_roles and 
request_check_mode (#13773)
---
 apisix/plugins/ai-aws-content-moderation.lua       |  75 ++++-
 .../en/latest/plugins/ai-aws-content-moderation.md |   4 +-
 .../zh/latest/plugins/ai-aws-content-moderation.md |   4 +-
 t/plugin/ai-aws-content-moderation.t               | 358 +++++++++++++++++++++
 4 files changed, 438 insertions(+), 3 deletions(-)

diff --git a/apisix/plugins/ai-aws-content-moderation.lua 
b/apisix/plugins/ai-aws-content-moderation.lua
index b8d3fc4e9..cc23e627a 100644
--- a/apisix/plugins/ai-aws-content-moderation.lua
+++ b/apisix/plugins/ai-aws-content-moderation.lua
@@ -31,6 +31,7 @@ local pairs    = pairs
 local unpack   = unpack
 local type     = type
 local ipairs   = ipairs
+local next     = next
 local table    = table
 local str_byte = string.byte
 local str_sub  = string.sub
@@ -91,6 +92,31 @@ local schema = {
         },
         check_request = { type = "boolean", default = true },
         check_response = { type = "boolean", default = false },
+        request_check_roles = {
+            type = "array",
+            items = { type = "string", enum = { "user", "tool", "system", 
"assistant" } },
+            minItems = 1,
+            uniqueItems = true,
+            default = { "user", "tool", "system", "assistant" },
+            description = "which message roles to moderate on the request 
side. user, tool " ..
+                          "and assistant follow request_check_mode; system is 
checked on " ..
+                          "every request because it can be poisoned by 
malicious ToolCall " ..
+                          "arguments. assistant messages are client-supplied 
history, so " ..
+                          "they are moderated by default too. Note: 
tool-result moderation " ..
+                          "applies to OpenAI-compatible formats where the tool 
output is a " ..
+                          "distinct tool role/item; for Anthropic/Bedrock 
(tool results are " ..
+                          "nested blocks inside user messages) tool content is 
not extracted.",
+        },
+        request_check_mode = {
+            type = "string",
+            enum = { "last", "all" },
+            default = "all",
+            description = "which user/tool/assistant messages to moderate: 
last (only the " ..
+                          "latest consecutive block of selected-role messages) 
| all (every " ..
+                          "selected-role message). Does not apply to the 
system role, which " ..
+                          "is always checked. Note that selecting assistant 
together with " ..
+                          "last widens the block, since assistant turns no 
longer end it.",
+        },
         request_check_length_limit = {
             type = "integer",
             minimum = MIN_SEGMENT_BYTES,
@@ -479,7 +505,54 @@ function _M.access(conf, ctx)
         return
     end
 
-    local contents = proto.extract_request_content(request_tab)
+    local roles = {}
+    for _, role in ipairs(conf.request_check_roles) do
+        roles[role] = true
+    end
+    -- every role but system is a turn role, extracted through 
extract_turn_content
+    local turn_roles = {}
+    for role in pairs(roles) do
+        if role ~= "system" then
+            turn_roles[role] = true
+        end
+    end
+
+    -- A configured role whose extractor this protocol doesn't implement would
+    -- otherwise pass unmoderated. Route that through fail_mode instead of
+    -- silently skipping the configured moderation.
+    if (roles.system and not proto.extract_system_content)
+            or (next(turn_roles) and not proto.extract_turn_content) then
+        local handled, code, body = binding.on_unsupported(
+            conf.fail_mode, _M.name, ctx,
+            "protocol cannot extract configured request_check_roles",
+            HTTP_INTERNAL_SERVER_ERROR,
+            "protocol " .. (ctx.ai_client_protocol or "unknown")
+                .. " cannot moderate the configured request_check_roles")
+        if handled then
+            return code, body
+        end
+        return
+    end
+
+    -- Collect the text of every configured role and score it in one pass:
+    -- Comprehend takes a flat list of text segments with no role field, so
+    -- per-role calls would only cost extra requests. system is always included
+    -- (not subject to request_check_mode, it can be poisoned by malicious
+    -- ToolCall arguments); the turn roles follow request_check_mode.
+    local contents = {}
+    if roles.system then
+        local system_texts = proto.extract_system_content(request_tab)
+        for i = 1, #system_texts do
+            contents[#contents + 1] = system_texts[i]
+        end
+    end
+    if next(turn_roles) then
+        local turn_texts = proto.extract_turn_content(request_tab,
+                                                      conf.request_check_mode, 
turn_roles)
+        for i = 1, #turn_texts do
+            contents[#contents + 1] = turn_texts[i]
+        end
+    end
     local content = table.concat(contents, " ")
     if content == "" then
         return
diff --git a/docs/en/latest/plugins/ai-aws-content-moderation.md 
b/docs/en/latest/plugins/ai-aws-content-moderation.md
index fe43939b1..8d830548b 100644
--- a/docs/en/latest/plugins/ai-aws-content-moderation.md
+++ b/docs/en/latest/plugins/ai-aws-content-moderation.md
@@ -38,7 +38,7 @@ import TabItem from '@theme/TabItem';
 
 The `ai-aws-content-moderation` Plugin integrates with [AWS 
Comprehend](https://aws.amazon.com/comprehend/) to check content for toxicity 
when proxying to LLMs, such as profanity, hate speech, insult, harassment, 
violence, and more, rejecting requests if the evaluated outcome exceeds the 
configured threshold.
 
-The Plugin is protocol-aware: it extracts the prompt content from the LLM 
request (for example `messages[].content`) and moderates only that decoded 
text, rather than the raw request body.
+The Plugin is protocol-aware: it extracts the prompt content from the LLM 
request (for example `messages[].content`) and moderates only that decoded 
text, rather than the raw request body. `request_check_roles` selects which 
message roles are moderated, and `request_check_mode` can narrow `user`/`tool` 
moderation to the newest turn so conversation history is not re-scored on every 
request.
 
 Both directions can be moderated. Set `check_response` to moderate the LLM 
response as well. For streaming responses, `stream_check_mode` selects between 
`realtime`, which moderates batches as they arrive and replaces the remainder 
of the stream once a batch is flagged, and `final_packet`, which moderates the 
assembled response and annotates the last chunk with `risk_level`. The verdict 
is also exposed on the request context as `$llm_content_risk_level` (`high` or 
`none`) for logging.
 
@@ -62,6 +62,8 @@ The `ai-aws-content-moderation` Plugin should be used with 
either [`ai-proxy`](.
 | `moderation_threshold` | number | False | 0.5 | 0 - 1 | Overall toxicity 
threshold. A higher value means more toxic content allowed. This option differs 
from the individual category thresholds in `moderation_categories`. For 
example, if `moderation_categories` is set with a `PROFANITY` threshold of 
`0.5`, and a request has a `PROFANITY` score of `0.1`, the request will not 
exceed the category threshold. However, if the request has other categories 
like `SEXUAL` or `VIOLENCE_OR_THREAT`  [...]
 | `check_request` | boolean | False | `true` | | If `true`, moderate the 
request content. |
 | `check_response` | boolean | False | `false` | | If `true`, moderate the LLM 
response content. |
+| `request_check_roles` | array[string] | False | 
`["user","tool","system","assistant"]` | items are `user`, `tool`, `system`, 
`assistant` | Which message roles to moderate on the request side. `user`, 
`tool` and `assistant` follow `request_check_mode`; `system` is checked on 
every request (it can be poisoned by malicious ToolCall arguments overwriting 
the system prompt). `assistant` messages in a request are client-supplied 
history rather than the model's own output, so they are moderat [...]
+| `request_check_mode` | string | False | `all` | `last`, `all` | Which 
user/tool/assistant messages to moderate. `last`: only the latest consecutive 
block of selected-role messages (the newest turn). `all`: every selected-role 
message. Does not apply to `system`, which is always moderated when enabled via 
`request_check_roles`. Note that `last` combined with `assistant` widens the 
block rather than narrowing it, because assistant turns no longer end it — drop 
`assistant` from `request_c [...]
 | `request_check_length_limit` | integer | False | `1000` | [4, 1024] | 
Maximum bytes of request content per Comprehend text segment. Longer content is 
split on character boundaries into several segments, which are then batched 
into as few Comprehend calls as possible. The upper bound is AWS Comprehend's 1 
KB per-segment limit. |
 | `response_check_length_limit` | integer | False | `1000` | [4, 1024] | 
Maximum bytes of response content per Comprehend text segment. Longer content 
is split on character boundaries into several segments, which are then batched 
into as few Comprehend calls as possible. The upper bound is AWS Comprehend's 1 
KB per-segment limit. |
 | `stream_check_mode` | string | False | `final_packet` | `realtime`, 
`final_packet` | Streaming moderation mode, used when `check_response` is 
`true`. `realtime`: moderate batches while the response streams, replacing the 
rest of the stream once a batch is flagged. `final_packet`: moderate the 
assembled response and annotate the last chunk with `risk_level`. |
diff --git a/docs/zh/latest/plugins/ai-aws-content-moderation.md 
b/docs/zh/latest/plugins/ai-aws-content-moderation.md
index d98259984..4c90914ee 100644
--- a/docs/zh/latest/plugins/ai-aws-content-moderation.md
+++ b/docs/zh/latest/plugins/ai-aws-content-moderation.md
@@ -40,7 +40,7 @@ import TabItem from '@theme/TabItem';
 
 `ai-aws-content-moderation` 插件集成了 [AWS 
Comprehend](https://aws.amazon.com/comprehend/),用于在代理请求到 LLM 
时检查请求内容中的有害内容,例如亵渎、仇恨言论、侮辱、骚扰、暴力等,如果评估结果超过配置的阈值则拒绝请求。
 
-该插件是协议感知的:它会从 LLM 请求中提取提示内容(例如 `messages[].content`),仅审核解码后的文本,而不是原始请求体。
+该插件是协议感知的:它会从 LLM 请求中提取提示内容(例如 
`messages[].content`),仅审核解码后的文本,而不是原始请求体。`request_check_roles` 
用于选择审核哪些消息角色,`request_check_mode` 可将 `user`/`tool` 的审核范围收窄到最新一轮,避免每次请求都重复审核历史对话。
 
 `ai-aws-content-moderation` 插件应与 [`ai-proxy`](./ai-proxy.md) 或 
[`ai-proxy-multi`](./ai-proxy-multi.md) 插件一起使用,以代理 LLM 请求。
 
@@ -57,6 +57,8 @@ import TabItem from '@theme/TabItem';
 | `moderation_categories` | object | 否 | | | 审核类别及其对应阈值的键值对。在每个键值对中,键应为 
`PROFANITY`、`HATE_SPEECH`、`INSULT`、`HARASSMENT_OR_ABUSE`、`SEXUAL` 或 
`VIOLENCE_OR_THREAT` 之一;阈值应在 0 到 1 之间(包含)。 |
 | `moderation_threshold` | number | 否 | 0.5 | 0 - 1 | 
整体毒性阈值。值越高,允许的有害内容越多。此选项与 `moderation_categories` 中的单独类别阈值不同。例如,如果 
`moderation_categories` 中设置了 `PROFANITY` 阈值为 `0.5`,而请求的 `PROFANITY` 分数为 
`0.1`,则请求不会超过类别阈值。但如果请求的其他类别(如 `SEXUAL` 或 `VIOLENCE_OR_THREAT`)超过了 
`moderation_threshold`,则请求将被拒绝。 |
 | `check_request` | boolean | 否 | `true` | | 如果为 `true`,则审核请求内容。 |
+| `request_check_roles` | array[string] | 否 | 
`["user","tool","system","assistant"]` | 取值为 `user`、`tool`、`system`、`assistant` 
| 请求侧审核哪些消息角色。`user`、`tool` 与 `assistant` 遵循 `request_check_mode`;`system` 
每次请求都审核(其可能被恶意 ToolCall 参数覆盖篡改)。请求中的 `assistant` 
消息由客户端提供,而非模型自身的输出,因此默认也会被审核。注意:tool 结果审核适用于 OpenAI 兼容格式(tool 输出为独立的 `tool` 
角色/项);Anthropic、Bedrock 的 tool 结果以嵌套 block 形式存在于 user 消息中,其内容不会被抽取。 |
+| `request_check_mode` | string | 否 | `all` | `last`、`all` | 审核哪些 
user/tool/assistant 消息。`last`:仅审核最后一段连续的所选角色消息(最新一轮);`all`:审核所有所选角色消息。不作用于 
`system`——只要通过 `request_check_roles` 启用,`system` 每次都审核。注意:`last` 与 `assistant` 
同时使用会扩大而非缩小审核范围,因为 assistant 消息不再中断该连续块;若只想审核最新一轮,请从 `request_check_roles` 中移除 
`assistant`。 |
 | `deny_code` | integer | 否 | `200` | [200, 599] | 请求被拒绝时返回的 HTTP 状态码。默认为 
`200`,使兼容 provider 的拒绝响应在客户端 SDK 中被解析为正常补全;设置为 4xx 可将拒绝暴露为 HTTP 错误。 |
 | `deny_message` | string | 否 | | | 请求被拒绝时返回的消息。未设置时,返回审核原因(例如 `request body 
exceeds toxicity threshold`)。 |
 | `fail_mode` | string | 否 | `skip` | `skip`、`warn`、`error` | 当请求未经过 
`ai-proxy`/`ai-proxy-multi`,因而无法作为 AI 
请求进行审核时的处理行为。`skip`:放行请求且不做检查;`warn`:放行并记录 warning 日志;`error`:拒绝请求。 |
diff --git a/t/plugin/ai-aws-content-moderation.t 
b/t/plugin/ai-aws-content-moderation.t
index 21c68b9c5..0161c1902 100644
--- a/t/plugin/ai-aws-content-moderation.t
+++ b/t/plugin/ai-aws-content-moderation.t
@@ -1501,3 +1501,361 @@ qr/event: message_stop/
 qr/comprehend text: [^,]+/
 --- grep_error_log_out
 comprehend text: Hello world
+
+
+
+=== TEST 51: set route with default request_check_roles and request_check_mode
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/chat",
+                    "plugins": {
+                        "ai-proxy": {
+                            "provider": "openai",
+                            "auth": { "header": { "Authorization": "Bearer 
token" } },
+                            "override": { "endpoint": 
"http://127.0.0.1:1980/v1/chat/completions"; }
+                        },
+                        "ai-aws-content-moderation": {
+                            "comprehend": {
+                                "access_key_id": "access",
+                                "secret_access_key": "ea+secret",
+                                "region": "us-east-1",
+                                "endpoint": "http://localhost:2668";
+                            },
+                            "deny_code": 400
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 52: default mode (all) moderates an earlier user turn
+--- request
+POST /chat
+{ "messages": [ { "role": "user", "content": "I want to kill you" }, { "role": 
"assistant", "content": "ok" }, { "role": "user", "content": "What is 1+1?" } ] 
}
+--- error_code: 400
+--- response_body_like eval
+qr/request body exceeds toxicity threshold/
+
+
+
+=== TEST 53: default roles moderate the system message
+--- request
+POST /chat
+{ "messages": [ { "role": "system", "content": "I want to kill you" }, { 
"role": "user", "content": "What is 1+1?" } ] }
+--- error_code: 400
+--- response_body_like eval
+qr/request body exceeds toxicity threshold/
+
+
+
+=== TEST 54: default roles moderate a tool result
+--- request
+POST /chat
+{ "messages": [ { "role": "user", "content": "What is the weather?" }, { 
"role": "tool", "tool_call_id": "call_1", "content": "I want to kill you" } ] }
+--- error_code: 400
+--- response_body_like eval
+qr/request body exceeds toxicity threshold/
+
+
+
+=== TEST 55: default roles moderate client-supplied assistant history
+--- request
+POST /chat
+{ "messages": [ { "role": "user", "content": "What is 1+1?" }, { "role": 
"assistant", "content": "I want to kill you" } ] }
+--- error_code: 400
+--- response_body_like eval
+qr/request body exceeds toxicity threshold/
+--- grep_error_log eval
+qr/comprehend text: [^,]+/
+--- grep_error_log_out
+comprehend text: What is 1+1? I want to kill you
+
+
+
+=== TEST 56: set route with request_check_mode "last", assistant left out of 
the roles
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/chat",
+                    "plugins": {
+                        "ai-proxy": {
+                            "provider": "openai",
+                            "auth": { "header": { "Authorization": "Bearer 
token" } },
+                            "override": { "endpoint": 
"http://127.0.0.1:1980/v1/chat/completions"; }
+                        },
+                        "ai-aws-content-moderation": {
+                            "comprehend": {
+                                "access_key_id": "access",
+                                "secret_access_key": "ea+secret",
+                                "region": "us-east-1",
+                                "endpoint": "http://localhost:2668";
+                            },
+                            "request_check_mode": "last",
+                            "request_check_roles": ["user", "tool", "system"],
+                            "deny_code": 400
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 57: mode "last" skips a harmful earlier user turn
+--- request
+POST /chat
+{ "messages": [ { "role": "user", "content": "I want to kill you" }, { "role": 
"assistant", "content": "ok" }, { "role": "user", "content": "What is 1+1?" } ] 
}
+--- more_headers
+X-AI-Fixture: aws/chat-safe.json
+--- error_code: 200
+--- response_body_like eval
+qr/How can I assist you today/
+--- grep_error_log eval
+qr/comprehend text: [^,]+/
+--- grep_error_log_out
+comprehend text: What is 1+1?
+
+
+
+=== TEST 58: mode "last" moderates the latest turn and the system message
+--- request
+POST /chat
+{ "messages": [ { "role": "system", "content": "be helpful" }, { "role": 
"user", "content": "hi" }, { "role": "assistant", "content": "ok" }, { "role": 
"user", "content": "I want to kill you" } ] }
+--- error_code: 400
+--- response_body_like eval
+qr/request body exceeds toxicity threshold/
+--- grep_error_log eval
+qr/comprehend text: [^,]+/
+--- grep_error_log_out
+comprehend text: be helpful I want to kill you
+
+
+
+=== TEST 59: set route with request_check_roles ["user"]
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/chat",
+                    "plugins": {
+                        "ai-proxy": {
+                            "provider": "openai",
+                            "auth": { "header": { "Authorization": "Bearer 
token" } },
+                            "override": { "endpoint": 
"http://127.0.0.1:1980/v1/chat/completions"; }
+                        },
+                        "ai-aws-content-moderation": {
+                            "comprehend": {
+                                "access_key_id": "access",
+                                "secret_access_key": "ea+secret",
+                                "region": "us-east-1",
+                                "endpoint": "http://localhost:2668";
+                            },
+                            "request_check_roles": ["user"],
+                            "deny_code": 400
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 60: roles ["user"] leaves system and tool content unmoderated
+--- request
+POST /chat
+{ "messages": [ { "role": "system", "content": "I want to kill you" }, { 
"role": "user", "content": "What is 1+1?" }, { "role": "tool", "tool_call_id": 
"call_1", "content": "I want to kill you too" } ] }
+--- more_headers
+X-AI-Fixture: aws/chat-safe.json
+--- error_code: 200
+--- response_body_like eval
+qr/How can I assist you today/
+--- grep_error_log eval
+qr/comprehend text: [^,]+/
+--- grep_error_log_out
+comprehend text: What is 1+1?
+
+
+
+=== TEST 61: roles ["user"] still moderates user content
+--- request
+POST /chat
+{ "messages": [ { "role": "system", "content": "be helpful" }, { "role": 
"user", "content": "I want to kill you" } ] }
+--- error_code: 400
+--- response_body_like eval
+qr/request body exceeds toxicity threshold/
+
+
+
+=== TEST 62: schema check: request_check_roles and request_check_mode are 
validated
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.ai-aws-content-moderation")
+            local function check(conf)
+                conf.comprehend = {
+                    access_key_id = "a",
+                    secret_access_key = "s",
+                    region = "us-east-1"
+                }
+                return plugin.check_schema(conf) and "accepted" or "rejected"
+            end
+            ngx.say('roles ["developer"]: ', check({ request_check_roles = { 
"developer" } }))
+            ngx.say("roles []: ", check({ request_check_roles = {} }))
+            ngx.say('roles ["user", "user"]: ',
+                    check({ request_check_roles = { "user", "user" } }))
+            ngx.say('mode "latest": ', check({ request_check_mode = "latest" 
}))
+            ngx.say('roles ["assistant"]: ', check({ request_check_roles = { 
"assistant" } }))
+            ngx.say('roles ["tool", "system"] + mode "all": ',
+                    check({ request_check_roles = { "tool", "system" },
+                            request_check_mode = "all" }))
+        }
+    }
+--- response_body
+roles ["developer"]: rejected
+roles []: rejected
+roles ["user", "user"]: rejected
+mode "latest": rejected
+roles ["assistant"]: accepted
+roles ["tool", "system"] + mode "all": accepted
+
+
+
+=== TEST 63: set route with fail_mode error on a protocol without role 
extractors
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/chat",
+                    "plugins": {
+                        "ai-proxy": {
+                            "provider": "openai",
+                            "auth": { "header": { "Authorization": "Bearer 
token" } },
+                            "override": { "endpoint": 
"http://127.0.0.1:1980/v1/chat/completions"; }
+                        },
+                        "ai-aws-content-moderation": {
+                            "comprehend": {
+                                "access_key_id": "access",
+                                "secret_access_key": "ea+secret",
+                                "region": "us-east-1",
+                                "endpoint": "http://localhost:2668";
+                            },
+                            "fail_mode": "error"
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 64: passthrough protocol cannot extract roles, fail_mode decides
+--- request
+POST /chat
+{ "prompt": "I want to kill you" }
+--- error_code: 500
+--- response_body_chomp
+protocol passthrough cannot moderate the configured request_check_roles
+
+
+
+=== TEST 65: set route with request_check_mode "last" and assistant among the 
roles
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/chat",
+                    "plugins": {
+                        "ai-proxy": {
+                            "provider": "openai",
+                            "auth": { "header": { "Authorization": "Bearer 
token" } },
+                            "override": { "endpoint": 
"http://127.0.0.1:1980/v1/chat/completions"; }
+                        },
+                        "ai-aws-content-moderation": {
+                            "comprehend": {
+                                "access_key_id": "access",
+                                "secret_access_key": "ea+secret",
+                                "region": "us-east-1",
+                                "endpoint": "http://localhost:2668";
+                            },
+                            "request_check_mode": "last",
+                            "request_check_roles": ["user", "assistant"],
+                            "deny_code": 400
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 66: assistant turns no longer end the block, so "last" reaches back 
into history
+--- request
+POST /chat
+{ "messages": [ { "role": "user", "content": "I want to kill you" }, { "role": 
"assistant", "content": "ok" }, { "role": "user", "content": "What is 1+1?" } ] 
}
+--- error_code: 400
+--- response_body_like eval
+qr/request body exceeds toxicity threshold/
+--- grep_error_log eval
+qr/comprehend text: [^,]+/
+--- grep_error_log_out
+comprehend text: I want to kill you ok What is 1+1?

Reply via email to