Files
ai-gateway-go/internal/contentpolicy/output.go
T
superidou c22669c31d 0.11.0: 旗舰版功能补齐(License/登录记录/会话管理/角色管理/门户定时任务/模型配额/输出脱敏/供应链扫描/记忆管理/AI助手/真实概览)
- 新增迁移 000031-000034(登录日志/角色/模型配额/记忆)
- 新增包: license/memory/modelquota/assistant,扫描引擎
- 全部功能后端+前端+端到端验证通过(25 包单测)
2026-08-13 11:37:18 +08:00

85 lines
2.1 KiB
Go

package contentpolicy
import (
"bytes"
"encoding/json"
"strings"
)
// OutputRedact 对一条模型响应 JSON(完整响应或 SSE data 载荷)应用全部
// redact 策略的规则:提取 choices[].message.content / choices[].delta.content
// 文本并替换敏感信息。与输入侧不同,输出 JSON 是给客户端消费的展示数据,
// 重编码可接受。返回替换后的字节与是否发生替换。
func (e *Engine) OutputRedact(payload []byte) ([]byte, bool) {
if e == nil || len(payload) == 0 {
return payload, false
}
rules := e.outputRules()
if len(rules) == 0 {
return payload, false
}
var document any
decoder := json.NewDecoder(bytes.NewReader(payload))
decoder.UseNumber()
if decoder.Decode(&document) != nil {
return payload, false
}
changed := false
redactTree(&document, &changed, rules)
if !changed {
return payload, false
}
encoded, err := json.Marshal(document)
if err != nil {
return payload, false
}
return encoded, true
}
// outputRules 返回全部启用策略的 redact 规则(输出侧不区分端点)。
func (e *Engine) outputRules() []compiledRule {
current := e.current.Load()
if current == nil {
return nil
}
var rules []compiledRule
for _, policy := range current.policies {
if policy.Action == "redact" {
rules = append(rules, policy.rules...)
}
}
return rules
}
// redactTree 递归遍历 JSON,仅对 content 文本应用规则。
func redactTree(value *any, changed *bool, rules []compiledRule) {
switch current := (*value).(type) {
case map[string]any:
for key, child := range current {
local := child
if strings.ToLower(key) == "content" {
if text, ok := local.(string); ok {
next := text
for _, rule := range rules {
if rule.expression.MatchString(next) {
next = rule.expression.ReplaceAllString(next, rule.replacement)
}
}
if next != text {
*changed = true
local = next
}
}
}
redactTree(&local, changed, rules)
current[key] = local
}
case []any:
for index, child := range current {
local := child
redactTree(&local, changed, rules)
current[index] = local
}
}
}