Skip to content

Commit e66a458

Browse files
rinfxCH3CHO
authored andcommitted
AI observability upgrade (alibaba#1587)
Co-authored-by: Kent Dong <[email protected]>
1 parent 997729f commit e66a458

File tree

11 files changed

+195
-237
lines changed

11 files changed

+195
-237
lines changed

plugins/wasm-go/extensions/ai-cache/core.go

+3
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ func processCacheHit(key string, response string, stream bool, ctx wrapper.HttpC
7474

7575
ctx.SetContext(CACHE_KEY_CONTEXT_KEY, nil)
7676

77+
ctx.SetUserAttribute("cache_status", "hit")
78+
ctx.WriteUserAttributeToLogWithKey(wrapper.AILogKey)
79+
7780
if stream {
7881
proxywasm.SendHttpResponseWithDetail(200, "ai-cache.hit", [][2]string{{"content-type", "text/event-stream; charset=utf-8"}}, []byte(fmt.Sprintf(c.StreamResponseTemplate, escapedResponse)), -1)
7982
} else {

plugins/wasm-go/extensions/ai-cache/go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ replace github.com/alibaba/higress/plugins/wasm-go => ../..
88

99
require (
1010
github.com/alibaba/higress/plugins/wasm-go v1.4.2
11-
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
11+
github.com/google/uuid v1.6.0
12+
github.com/higress-group/proxy-wasm-go-sdk v1.0.0
1213
github.com/tidwall/gjson v1.17.3
1314
github.com/tidwall/resp v0.1.1
1415
// github.com/weaviate/weaviate-go-client/v4 v4.15.1
1516
)
1617

1718
require (
18-
github.com/google/uuid v1.6.0 // indirect
1919
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 // indirect
2020
github.com/magefile/mage v1.14.0 // indirect
2121
github.com/stretchr/testify v1.9.0 // indirect

plugins/wasm-go/extensions/ai-cache/go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
33
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
44
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
55
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
6-
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f h1:ZIiIBRvIw62gA5MJhuwp1+2wWbqL9IGElQ499rUsYYg=
7-
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
6+
github.com/higress-group/proxy-wasm-go-sdk v1.0.0 h1:BZRNf4R7jr9hwRivg/E29nkVaKEak5MWjBDhWjuHijU=
7+
github.com/higress-group/proxy-wasm-go-sdk v1.0.0/go.mod h1:iiSyFbo+rAtbtGt/bsefv8GU57h9CCLYGJA74/tF5/0=
88
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
99
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
1010
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

plugins/wasm-go/extensions/ai-cache/main.go

+6
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,15 @@ func onHttpRequestBody(ctx wrapper.HttpContext, c config.PluginConfig, body []by
128128
func onHttpResponseHeaders(ctx wrapper.HttpContext, c config.PluginConfig, log wrapper.Log) types.Action {
129129
skipCache := ctx.GetContext(SKIP_CACHE_HEADER)
130130
if skipCache != nil {
131+
ctx.SetUserAttribute("cache_status", "skip")
132+
ctx.WriteUserAttributeToLogWithKey(wrapper.AILogKey)
131133
ctx.DontReadResponseBody()
132134
return types.ActionContinue
133135
}
136+
if ctx.GetContext(CACHE_KEY_CONTEXT_KEY) != nil {
137+
ctx.SetUserAttribute("cache_status", "miss")
138+
ctx.WriteUserAttributeToLogWithKey(wrapper.AILogKey)
139+
}
134140
contentType, _ := proxywasm.GetHttpResponseHeader("content-type")
135141
if strings.Contains(contentType, "text/event-stream") {
136142
ctx.SetContext(STREAM_CONTEXT_KEY, struct{}{})

plugins/wasm-go/extensions/ai-security-guard/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ description: 阿里云内容安全检测
3131
| `denyMessage` | string | optional | openai格式的流式/非流式响应 | 指定内容非法时的响应内容 |
3232
| `protocol` | string | optional | openai | 协议格式,非openai协议填`original` |
3333
| `riskLevelBar` | string | optional | high | 拦截风险等级,取值为 max, high, medium, low |
34+
| `timeout` | int | optional | 2000 | 调用内容安全服务时的超时时间 |
3435

3536
补充说明一下 `denyMessage`,对非法请求的处理逻辑为:
3637
- 如果配置了 `denyMessage`,返回内容为 `denyMessage` 配置内容,格式为openai格式的流式/非流式响应

plugins/wasm-go/extensions/ai-security-guard/main.go

+24-17
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const (
5353
DefaultStreamingResponseJsonPath = "choices.0.delta.content"
5454
DefaultDenyCode = 200
5555
DefaultDenyMessage = "很抱歉,我无法回答您的问题"
56+
DefaultTimeout = 2000
5657

5758
AliyunUserAgent = "CIPFrom/AIGateway"
5859
LengthLimit = 1800
@@ -100,6 +101,7 @@ type AISecurityConfig struct {
100101
denyMessage string
101102
protocolOriginal bool
102103
riskLevelBar string
104+
timeout uint32
103105
metrics map[string]proxywasm.MetricCounter
104106
}
105107

@@ -225,6 +227,11 @@ func parseConfig(json gjson.Result, config *AISecurityConfig, log wrapper.Log) e
225227
} else {
226228
config.riskLevelBar = HighRisk
227229
}
230+
if obj := json.Get("timeout"); obj.Exists() {
231+
config.timeout = uint32(obj.Int())
232+
} else {
233+
config.timeout = DefaultTimeout
234+
}
228235
config.client = wrapper.NewClusterClient(wrapper.FQDNCluster{
229236
FQDN: serviceName,
230237
Port: servicePort,
@@ -253,6 +260,7 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, config AISecurityConfig, log
253260

254261
func onHttpRequestBody(ctx wrapper.HttpContext, config AISecurityConfig, body []byte, log wrapper.Log) types.Action {
255262
log.Debugf("checking request body...")
263+
startTime := time.Now().UnixMilli()
256264
content := gjson.GetBytes(body, config.requestContentJsonPath).String()
257265
model := gjson.GetBytes(body, "model").String()
258266
ctx.SetContext("requestModel", model)
@@ -279,6 +287,10 @@ func onHttpRequestBody(ctx wrapper.HttpContext, config AISecurityConfig, body []
279287
}
280288
if riskLevelToInt(response.Data.RiskLevel) < riskLevelToInt(config.riskLevelBar) {
281289
if contentIndex >= len(content) {
290+
endTime := time.Now().UnixMilli()
291+
ctx.SetUserAttribute("safecheck_request_rt", endTime-startTime)
292+
ctx.SetUserAttribute("safecheck_status", "request pass")
293+
ctx.WriteUserAttributeToLogWithKey(wrapper.AILogKey)
282294
proxywasm.ResumeHttpRequest()
283295
} else {
284296
singleCall()
@@ -305,7 +317,9 @@ func onHttpRequestBody(ctx wrapper.HttpContext, config AISecurityConfig, body []
305317
}
306318
ctx.DontReadResponseBody()
307319
config.incrementCounter("ai_sec_request_deny", 1)
308-
ctx.SetUserAttribute("safecheck_status", "request deny")
320+
endTime := time.Now().UnixMilli()
321+
ctx.SetUserAttribute("safecheck_request_rt", endTime-startTime)
322+
ctx.SetUserAttribute("safecheck_status", "reqeust deny")
309323
if response.Data.Advice != nil {
310324
ctx.SetUserAttribute("safecheck_riskLabel", response.Data.Result[0].Label)
311325
ctx.SetUserAttribute("safecheck_riskWords", response.Data.Result[0].RiskWords)
@@ -345,7 +359,7 @@ func onHttpRequestBody(ctx wrapper.HttpContext, config AISecurityConfig, body []
345359
reqParams.Add(k, v)
346360
}
347361
reqParams.Add("Signature", signature)
348-
err := config.client.Post(fmt.Sprintf("/?%s", reqParams.Encode()), [][2]string{{"User-Agent", AliyunUserAgent}}, nil, callback)
362+
err := config.client.Post(fmt.Sprintf("/?%s", reqParams.Encode()), [][2]string{{"User-Agent", AliyunUserAgent}}, nil, callback, config.timeout)
349363
if err != nil {
350364
log.Errorf("failed call the safe check service: %v", err)
351365
proxywasm.ResumeHttpRequest()
@@ -364,20 +378,6 @@ func convertHeaders(hs [][2]string) map[string][]string {
364378
return ret
365379
}
366380

367-
// headers: map[string][]string -> [][2]string
368-
func reconvertHeaders(hs map[string][]string) [][2]string {
369-
var ret [][2]string
370-
for k, vs := range hs {
371-
for _, v := range vs {
372-
ret = append(ret, [2]string{k, v})
373-
}
374-
}
375-
sort.SliceStable(ret, func(i, j int) bool {
376-
return ret[i][0] < ret[j][0]
377-
})
378-
return ret
379-
}
380-
381381
func onHttpResponseHeaders(ctx wrapper.HttpContext, config AISecurityConfig, log wrapper.Log) types.Action {
382382
if !config.checkResponse {
383383
log.Debugf("response checking is disabled")
@@ -401,6 +401,7 @@ func onHttpResponseHeaders(ctx wrapper.HttpContext, config AISecurityConfig, log
401401

402402
func onHttpResponseBody(ctx wrapper.HttpContext, config AISecurityConfig, body []byte, log wrapper.Log) types.Action {
403403
log.Debugf("checking response body...")
404+
startTime := time.Now().UnixMilli()
404405
hdsMap := ctx.GetContext("headers").(map[string][]string)
405406
isStreamingResponse := strings.Contains(strings.Join(hdsMap["content-type"], ";"), "event-stream")
406407
model := ctx.GetStringContext("requestModel", "unknown")
@@ -433,6 +434,10 @@ func onHttpResponseBody(ctx wrapper.HttpContext, config AISecurityConfig, body [
433434
}
434435
if riskLevelToInt(response.Data.RiskLevel) < riskLevelToInt(config.riskLevelBar) {
435436
if contentIndex >= len(content) {
437+
endTime := time.Now().UnixMilli()
438+
ctx.SetUserAttribute("safecheck_response_rt", endTime-startTime)
439+
ctx.SetUserAttribute("safecheck_status", "response pass")
440+
ctx.WriteUserAttributeToLogWithKey(wrapper.AILogKey)
436441
proxywasm.ResumeHttpResponse()
437442
} else {
438443
singleCall()
@@ -458,6 +463,8 @@ func onHttpResponseBody(ctx wrapper.HttpContext, config AISecurityConfig, body [
458463
proxywasm.SendHttpResponse(uint32(config.denyCode), [][2]string{{"content-type", "application/json"}}, jsonData, -1)
459464
}
460465
config.incrementCounter("ai_sec_response_deny", 1)
466+
endTime := time.Now().UnixMilli()
467+
ctx.SetUserAttribute("safecheck_response_rt", endTime-startTime)
461468
ctx.SetUserAttribute("safecheck_status", "response deny")
462469
if response.Data.Advice != nil {
463470
ctx.SetUserAttribute("safecheck_riskLabel", response.Data.Result[0].Label)
@@ -498,7 +505,7 @@ func onHttpResponseBody(ctx wrapper.HttpContext, config AISecurityConfig, body [
498505
reqParams.Add(k, v)
499506
}
500507
reqParams.Add("Signature", signature)
501-
err := config.client.Post(fmt.Sprintf("/?%s", reqParams.Encode()), [][2]string{{"User-Agent", AliyunUserAgent}}, nil, callback)
508+
err := config.client.Post(fmt.Sprintf("/?%s", reqParams.Encode()), [][2]string{{"User-Agent", AliyunUserAgent}}, nil, callback, config.timeout)
502509
if err != nil {
503510
log.Errorf("failed call the safe check service: %v", err)
504511
proxywasm.ResumeHttpResponse()

plugins/wasm-go/extensions/ai-statistics/go.sum

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
33
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
44
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
55
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
6-
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f h1:ZIiIBRvIw62gA5MJhuwp1+2wWbqL9IGElQ499rUsYYg=
7-
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
6+
github.com/higress-group/proxy-wasm-go-sdk v1.0.0 h1:BZRNf4R7jr9hwRivg/E29nkVaKEak5MWjBDhWjuHijU=
87
github.com/higress-group/proxy-wasm-go-sdk v1.0.0/go.mod h1:iiSyFbo+rAtbtGt/bsefv8GU57h9CCLYGJA74/tF5/0=
98
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
109
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
1110
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1211
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
13-
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
14-
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
12+
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
1513
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
1614
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
1715
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=

0 commit comments

Comments
 (0)