feat(backend): 为 JSON Schema 清理添加警告日志
改进 cleanJSONSchema 函数: - 新增 schemaValidationKeys 映射表,标记关键验证字段 - 新增 warnSchemaKeyRemovedOnce 函数,在移除关键验证字段时输出警告(每个 key 仅警告一次) - 支持通过环境变量 SUB2API_SCHEMA_CLEAN_WARN 控制警告开关 - 默认在非 release 模式下启用警告,便于开发调试 此改进响应代码审查建议,帮助开发者识别可能影响模型输出质量的 Schema 字段移除。
This commit is contained in:
@@ -4,8 +4,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -462,7 +465,7 @@ func cleanJSONSchema(schema map[string]any) map[string]any {
|
|||||||
if schema == nil {
|
if schema == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cleaned := cleanSchemaValue(schema)
|
cleaned := cleanSchemaValue(schema, "$")
|
||||||
result, ok := cleaned.(map[string]any)
|
result, ok := cleaned.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@@ -500,6 +503,56 @@ func cleanJSONSchema(schema map[string]any) map[string]any {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var schemaValidationKeys = map[string]bool{
|
||||||
|
"minLength": true,
|
||||||
|
"maxLength": true,
|
||||||
|
"pattern": true,
|
||||||
|
"minimum": true,
|
||||||
|
"maximum": true,
|
||||||
|
"exclusiveMinimum": true,
|
||||||
|
"exclusiveMaximum": true,
|
||||||
|
"multipleOf": true,
|
||||||
|
"uniqueItems": true,
|
||||||
|
"minItems": true,
|
||||||
|
"maxItems": true,
|
||||||
|
"minProperties": true,
|
||||||
|
"maxProperties": true,
|
||||||
|
"patternProperties": true,
|
||||||
|
"propertyNames": true,
|
||||||
|
"dependencies": true,
|
||||||
|
"dependentSchemas": true,
|
||||||
|
"dependentRequired": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var warnedSchemaKeys sync.Map
|
||||||
|
|
||||||
|
func schemaCleaningWarningsEnabled() bool {
|
||||||
|
// 可通过环境变量强制开关,方便排查:SUB2API_SCHEMA_CLEAN_WARN=true/false
|
||||||
|
if v := strings.TrimSpace(os.Getenv("SUB2API_SCHEMA_CLEAN_WARN")); v != "" {
|
||||||
|
switch strings.ToLower(v) {
|
||||||
|
case "1", "true", "yes", "on":
|
||||||
|
return true
|
||||||
|
case "0", "false", "no", "off":
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 默认:非 release 模式下输出(debug/test)
|
||||||
|
return gin.Mode() != gin.ReleaseMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func warnSchemaKeyRemovedOnce(key, path string) {
|
||||||
|
if !schemaCleaningWarningsEnabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !schemaValidationKeys[key] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, loaded := warnedSchemaKeys.LoadOrStore(key, struct{}{}); loaded {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[SchemaClean] removed unsupported JSON Schema validation field key=%q path=%q", key, path)
|
||||||
|
}
|
||||||
|
|
||||||
// excludedSchemaKeys 不支持的 schema 字段
|
// excludedSchemaKeys 不支持的 schema 字段
|
||||||
// 基于 Claude API (Vertex AI) 的实际支持情况
|
// 基于 Claude API (Vertex AI) 的实际支持情况
|
||||||
// 支持: type, description, enum, properties, required, additionalProperties, items
|
// 支持: type, description, enum, properties, required, additionalProperties, items
|
||||||
@@ -562,13 +615,14 @@ var excludedSchemaKeys = map[string]bool{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// cleanSchemaValue 递归清理 schema 值
|
// cleanSchemaValue 递归清理 schema 值
|
||||||
func cleanSchemaValue(value any) any {
|
func cleanSchemaValue(value any, path string) any {
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
result := make(map[string]any)
|
result := make(map[string]any)
|
||||||
for k, val := range v {
|
for k, val := range v {
|
||||||
// 跳过不支持的字段
|
// 跳过不支持的字段
|
||||||
if excludedSchemaKeys[k] {
|
if excludedSchemaKeys[k] {
|
||||||
|
warnSchemaKeyRemovedOnce(k, path)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,15 +656,15 @@ func cleanSchemaValue(value any) any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 递归清理所有值
|
// 递归清理所有值
|
||||||
result[k] = cleanSchemaValue(val)
|
result[k] = cleanSchemaValue(val, path+"."+k)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
case []any:
|
case []any:
|
||||||
// 递归处理数组中的每个元素
|
// 递归处理数组中的每个元素
|
||||||
cleaned := make([]any, 0, len(v))
|
cleaned := make([]any, 0, len(v))
|
||||||
for _, item := range v {
|
for i, item := range v {
|
||||||
cleaned = append(cleaned, cleanSchemaValue(item))
|
cleaned = append(cleaned, cleanSchemaValue(item, fmt.Sprintf("%s[%d]", path, i)))
|
||||||
}
|
}
|
||||||
return cleaned
|
return cleaned
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user