Skip to content

Commit f5f6305

Browse files
committed
Merge remote-tracking branch 'github/master' into dev-apisix
2 parents 0a71071 + 00f18aa commit f5f6305

24 files changed

+4066
-34
lines changed

apisix/balancer.lua

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ local set_more_tries = balancer.set_more_tries
2727
local get_last_failure = balancer.get_last_failure
2828
local set_timeouts = balancer.set_timeouts
2929
local ngx_now = ngx.now
30-
local str_byte = string.byte
3130

3231
local module_name = "balancer"
3332
local pickers = {}
@@ -195,12 +194,6 @@ end
195194
local function pick_server(route, ctx)
196195
local up_conf = ctx.upstream_conf
197196

198-
for _, node in ipairs(up_conf.nodes) do
199-
if core.utils.parse_ipv6(node.host) and str_byte(node.host, 1) ~= str_byte("[") then
200-
node.host = '[' .. node.host .. ']'
201-
end
202-
end
203-
204197
local nodes_count = #up_conf.nodes
205198
if nodes_count == 1 then
206199
local node = up_conf.nodes[1]

apisix/upstream.lua

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ local tostring = tostring
2525
local ipairs = ipairs
2626
local pairs = pairs
2727
local pcall = pcall
28+
local str_byte = string.byte
2829
local ngx_var = ngx.var
2930
local is_http = ngx.config.subsystem == "http"
3031
local upstreams
@@ -325,10 +326,16 @@ function _M.upstreams()
325326
end
326327

327328

328-
function _M.check_schema(conf)
329+
local function check_schema(conf)
330+
for _, node in ipairs(conf.nodes or {}) do
331+
if core.utils.parse_ipv6(node.host) and str_byte(node.host, 1) ~= str_byte("[") then
332+
return false, "IPv6 address must be enclosed with '[' and ']'"
333+
end
334+
end
329335
return core.schema.check(core.schema.upstream, conf)
330336
end
331337

338+
_M.check_schema = check_schema
332339

333340
local function get_chash_key_schema(hash_on)
334341
if not hash_on then
@@ -357,7 +364,7 @@ end
357364

358365
local function check_upstream_conf(in_dp, conf)
359366
if not in_dp then
360-
local ok, err = core.schema.check(core.schema.upstream, conf)
367+
local ok, err = check_schema(conf)
361368
if not ok then
362369
return false, "invalid configuration: " .. err
363370
end
@@ -396,6 +403,12 @@ local function check_upstream_conf(in_dp, conf)
396403
.. "wrong ssl type"
397404
end
398405
end
406+
else
407+
for i, node in ipairs(conf.nodes or {}) do
408+
if core.utils.parse_ipv6(node.host) and str_byte(node.host, 1) ~= str_byte("[") then
409+
conf.nodes[i].host = "[" .. node.host .. "]"
410+
end
411+
end
399412
end
400413

401414
if is_http then

apisix/utils/batch-processor.lua

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,14 @@ batch_processor.schema = schema
5151
local function schedule_func_exec(self, delay, batch)
5252
local hdl, err = timer_at(delay, execute_func, self, batch)
5353
if not hdl then
54-
core.log.error("failed to create process timer: ", err)
55-
return
54+
if err == "process exiting" then
55+
-- it is allowed to create zero-delay timers even when
56+
-- the Nginx worker process starts shutting down
57+
timer_at(0, execute_func, self)
58+
else
59+
core.log.error("failed to create process timer: ", err)
60+
return
61+
end
5662
end
5763
end
5864

@@ -78,10 +84,6 @@ end
7884

7985

8086
function execute_func(premature, self, batch)
81-
if premature then
82-
return
83-
end
84-
8587
-- In case of "err" and a valid "first_fail" batch processor considers, all first_fail-1
8688
-- entries have been successfully consumed and hence reschedule the job for entries with
8789
-- index first_fail to #entries based on the current retry policy.
@@ -116,10 +118,6 @@ end
116118

117119

118120
local function flush_buffer(premature, self)
119-
if premature then
120-
return
121-
end
122-
123121
if now() - self.last_entry_t >= self.inactive_timeout or
124122
now() - self.first_entry_t >= self.buffer_duration
125123
then
@@ -140,8 +138,12 @@ end
140138
function create_buffer_timer(self)
141139
local hdl, err = timer_at(self.inactive_timeout, flush_buffer, self)
142140
if not hdl then
143-
core.log.error("failed to create buffer timer: ", err)
144-
return
141+
if err == "process exiting" then
142+
timer_at(0, flush_buffer, self)
143+
else
144+
core.log.error("failed to create buffer timer: ", err)
145+
return
146+
end
145147
end
146148
self.is_timer_running = true
147149
end

docs/en/latest/plugins/ai-aliyun-content-moderation.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
2-
title: ai-aws-content-moderation
2+
title: ai-aliyun-content-moderation
33
keywords:
44
- Apache APISIX
55
- API Gateway
66
- Plugin
77
- ai-aliyun-content-moderation
8-
description: This document contains information about the Apache APISIX ai-aws-content-moderation Plugin.
8+
description: This document contains information about the Apache APISIX ai-aliyun-content-moderation Plugin.
99
---
1010

1111
<!--
@@ -29,7 +29,7 @@ description: This document contains information about the Apache APISIX ai-aws-c
2929

3030
## Description
3131

32-
The ai-aliyun-content-moderation plugin integrates with Aliyun's content moderation service to check both request and response content for inappropriate material when working with LLMs. It supports both real-time streaming checks and final packet moderation.
32+
The `ai-aliyun-content-moderation` plugin integrates with Aliyun's content moderation service to check both request and response content for inappropriate material when working with LLMs. It supports both real-time streaming checks and final packet moderation.
3333

3434
This plugin must be used in routes that utilize the ai-proxy or ai-proxy-multi plugins.
3535

docs/en/latest/plugins/ai-proxy-multi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ description: The ai-proxy-multi Plugin extends the capabilities of ai-proxy with
3535

3636
## Description
3737

38-
The `ai-proxy-multi` Plugin simplifies access to LLM and embedding models by transforming Plugin configurations into the designated request format for OpenAI, DeepSeek, Azure, AIMLAPI, and other OpenAI-compatible APIs. It extends the capabilities of [`ai-proxy-multi`](./ai-proxy.md) with load balancing, retries, fallbacks, and health checks.
38+
The `ai-proxy-multi` Plugin simplifies access to LLM and embedding models by transforming Plugin configurations into the designated request format for OpenAI, DeepSeek, Azure, AIMLAPI, and other OpenAI-compatible APIs. It extends the capabilities of [`ai-proxy`](./ai-proxy.md) with load balancing, retries, fallbacks, and health checks.
3939

4040
In addition, the Plugin also supports logging LLM request information in the access log, such as token usage, model, time to the first response, and more.
4141

docs/en/latest/plugins/inspect.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ be flushed, and it would not affect other caches to avoid slowing down other par
6868
* If the breakpointis related to local function or anonymous function,
6969
then you have to set it to `nil` (because no way to get function reference), which would flush the whole jit cache of Lua vm.
7070

71-
You attach a `filter_func` function of the breakpoint, the function takes the `info` as argument and returns
72-
true of false to determine whether the breakpoint would be removed. You could setup one-shot breakpoint
71+
You attach a `filter_func` function to the breakpoint. The function takes the `info` as an argument and returns
72+
true or false to determine whether the breakpoint would be removed. This allows you to set up a one-shot breakpoint
7373
at ease.
7474

7575
The `info` is a hash table which contains below keys:

docs/zh/latest/config.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
"tutorials/observe-your-api",
3030
"tutorials/health-check",
3131
"tutorials/client-to-apisix-mtls",
32-
"tutorials/keycloak-oidc"
32+
"tutorials/keycloak-oidc",
33+
"tutorials/manage-api-consumers",
34+
"tutorials/cache-api-responses"
3335
]
3436
},
3537
{
@@ -56,6 +58,22 @@
5658
"type": "category",
5759
"label": "插件",
5860
"items": [
61+
{
62+
"type": "category",
63+
"label": "人工智能",
64+
"items": [
65+
"plugins/ai-proxy",
66+
"plugins/ai-proxy-multi",
67+
"plugins/ai-rate-limiting",
68+
"plugins/ai-prompt-guard",
69+
"plugins/ai-aws-content-moderation",
70+
"plugins/ai-aliyun-content-moderation",
71+
"plugins/ai-prompt-decorator",
72+
"plugins/ai-prompt-template",
73+
"plugins/ai-rag",
74+
"plugins/ai-request-rewrite"
75+
]
76+
},
5977
{
6078
"type": "category",
6179
"label": "普通插件",
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
title: ai-aliyun-content-moderation
3+
keywords:
4+
- Apache APISIX
5+
- API 网关
6+
- Plugin
7+
- ai-aliyun-content-moderation
8+
description: 本文档包含有关 Apache APISIX ai-aliyun-content-moderation 插件的信息。
9+
---
10+
11+
<!--
12+
#
13+
# Licensed to the Apache Software Foundation (ASF) under one or more
14+
# contributor license agreements. See the NOTICE file distributed with
15+
# this work for additional information regarding copyright ownership.
16+
# The ASF licenses this file to You under the Apache License, Version 2.0
17+
# (the "License"); you may not use this file except in compliance with
18+
# the License. You may obtain a copy of the License at
19+
#
20+
# http://www.apache.org/licenses/LICENSE-2.0
21+
#
22+
# Unless required by applicable law or agreed to in writing, software
23+
# distributed under the License is distributed on an "AS IS" BASIS,
24+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25+
# See the License for the specific language governing permissions and
26+
# limitations under the License.
27+
#
28+
-->
29+
30+
## 描述
31+
32+
`ai-aliyun-content-moderation` 插件集成了阿里云的内容审核服务,用于在与大语言模型 (LLM) 交互时检查请求和响应内容是否包含不当材料。它支持实时流式检查和最终数据包审核两种模式。
33+
34+
此插件必须在使用 `ai-proxy``ai-proxy-multi` 插件的路由中使用。
35+
36+
## 插件属性
37+
38+
| **字段** | **必选项** | **类型** | **描述** |
39+
| ---------------------------- | ---------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
40+
| endpoint || String | 阿里云服务端点 URL |
41+
| region_id || String | 阿里云区域标识符 |
42+
| access_key_id || String | 阿里云访问密钥 ID |
43+
| access_key_secret || String | 阿里云访问密钥密码 |
44+
| check_request || Boolean | 启用请求内容审核。默认值:`true` |
45+
| check_response || Boolean | 启用响应内容审核。默认值:`false` |
46+
| stream_check_mode || String | 流式审核模式。默认值:`"final_packet"`。有效值:`["realtime", "final_packet"]` |
47+
| stream_check_cache_size || Integer | 实时模式下每次审核批次的最大字符数。默认值:`128`。必须 `>= 1` |
48+
| stream_check_interval || Number | 实时模式下批次检查之间的间隔秒数。默认值:`3`。必须 `>= 0.1` |
49+
| request_check_service || String | 用于请求审核的阿里云服务。默认值:`"llm_query_moderation"` |
50+
| request_check_length_limit || Number | 每个请求审核块的最大字符数。默认值:`2000` |
51+
| response_check_service || String | 用于响应审核的阿里云服务。默认值:`"llm_response_moderation"` |
52+
| response_check_length_limit || Number | 每个响应审核块的最大字符数。默认值:`5000` |
53+
| risk_level_bar || String | 内容拒绝的阈值。默认值:`"high"`。有效值:`["none", "low", "medium", "high", "max"]` |
54+
| deny_code || Number | 被拒绝内容的 HTTP 状态码。默认值:`200` |
55+
| deny_message || String | 被拒绝内容的自定义消息。默认值:`-` |
56+
| timeout || Integer | 请求超时时间(毫秒)。默认值:`10000`。必须 `>= 1` |
57+
| ssl_verify || Boolean | 启用 SSL 证书验证。默认值:`true` |
58+
59+
## 使用示例
60+
61+
首先初始化这些 shell 变量:
62+
63+
```shell
64+
ADMIN_API_KEY=edd1c9f034335f136f87ad84b625c8f1
65+
ALIYUN_ACCESS_KEY_ID=your-aliyun-access-key-id
66+
ALIYUN_ACCESS_KEY_SECRET=your-aliyun-access-key-secret
67+
ALIYUN_REGION=cn-hangzhou
68+
ALIYUN_ENDPOINT=https://green.cn-hangzhou.aliyuncs.com
69+
OPENAI_KEY=your-openai-api-key
70+
```
71+
72+
创建一个带有 `ai-aliyun-content-moderation``ai-proxy` 插件的路由:
73+
74+
```shell
75+
curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT \
76+
-H "X-API-KEY: ${ADMIN_API_KEY}" \
77+
-d '{
78+
"uri": "/v1/chat/completions",
79+
"plugins": {
80+
"ai-proxy": {
81+
"provider": "openai",
82+
"auth": {
83+
"header": {
84+
"Authorization": "Bearer '"$OPENAI_KEY"'"
85+
}
86+
},
87+
"override": {
88+
"endpoint": "http://localhost:6724/v1/chat/completions"
89+
}
90+
},
91+
"ai-aliyun-content-moderation": {
92+
"endpoint": "'"$ALIYUN_ENDPOINT"'",
93+
"region_id": "'"$ALIYUN_REGION"'",
94+
"access_key_id": "'"$ALIYUN_ACCESS_KEY_ID"'",
95+
"access_key_secret": "'"$ALIYUN_ACCESS_KEY_SECRET"'",
96+
"risk_level_bar": "high",
97+
"check_request": true,
98+
"check_response": true,
99+
"deny_code": 400,
100+
"deny_message": "您的请求违反了内容政策"
101+
}
102+
}
103+
}'
104+
```
105+
106+
这里使用 `ai-proxy` 插件是因为它简化了对 LLM 的访问。不过,您也可以在上游配置中配置 LLM。
107+
108+
现在发送一个请求:
109+
110+
```shell
111+
curl http://127.0.0.1:9080/v1/chat/completions -i \
112+
-H "Content-Type: application/json" \
113+
-d '{
114+
"model": "gpt-3.5-turbo",
115+
"messages": [
116+
{"role": "user", "content": "I want to kill you"}
117+
],
118+
"stream": false
119+
}'
120+
```
121+
122+
然后请求将被阻止,并返回如下错误:
123+
124+
```text
125+
HTTP/1.1 400 Bad Request
126+
Content-Type: application/json
127+
128+
{"id":"chatcmpl-123","object":"chat.completion","model":"gpt-3.5-turbo","choices":[{"index":0,"message":{"role":"assistant","content":"您的请求违反了内容政策"},"finish_reason":"stop"}],"usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0}}
129+
```

0 commit comments

Comments
 (0)