From f0bd14b64f540ef16553dcb6b0a6252d3efdb7b6 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Fri, 6 Feb 2026 00:19:56 +0800 Subject: [PATCH] refactor(util): optimize JSON schema processing and keyword removal logic - Consolidated path-finding logic into a new `findPathsByFields` helper function. - Refactored repetitive loop structures to improve readability and performance. - Added depth-based sorting for deletion paths to ensure proper removal order. --- internal/util/gemini_schema.go | 60 +++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/internal/util/gemini_schema.go b/internal/util/gemini_schema.go index b6b128d4..e74d1271 100644 --- a/internal/util/gemini_schema.go +++ b/internal/util/gemini_schema.go @@ -61,14 +61,20 @@ func cleanJSONSchema(jsonStr string, addPlaceholder bool) string { // removeKeywords removes all occurrences of specified keywords from the JSON schema. func removeKeywords(jsonStr string, keywords []string) string { + deletePaths := make([]string, 0) + pathsByField := findPathsByFields(jsonStr, keywords) for _, key := range keywords { - for _, p := range findPaths(jsonStr, key) { + for _, p := range pathsByField[key] { if isPropertyDefinition(trimSuffix(p, "."+key)) { continue } - jsonStr, _ = sjson.Delete(jsonStr, p) + deletePaths = append(deletePaths, p) } } + sortByDepth(deletePaths) + for _, p := range deletePaths { + jsonStr, _ = sjson.Delete(jsonStr, p) + } return jsonStr } @@ -235,8 +241,9 @@ var unsupportedConstraints = []string{ } func moveConstraintsToDescription(jsonStr string) string { + pathsByField := findPathsByFields(jsonStr, unsupportedConstraints) for _, key := range unsupportedConstraints { - for _, p := range findPaths(jsonStr, key) { + for _, p := range pathsByField[key] { val := gjson.Get(jsonStr, p) if !val.Exists() || val.IsObject() || val.IsArray() { continue @@ -424,14 +431,21 @@ func removeUnsupportedKeywords(jsonStr string) string { "$schema", "$defs", "definitions", "const", "$ref", "additionalProperties", "propertyNames", // Gemini doesn't support property name validation ) + + deletePaths := make([]string, 0) + pathsByField := findPathsByFields(jsonStr, keywords) for _, key := range keywords { - for _, p := range findPaths(jsonStr, key) { + for _, p := range pathsByField[key] { if isPropertyDefinition(trimSuffix(p, "."+key)) { continue } - jsonStr, _ = sjson.Delete(jsonStr, p) + deletePaths = append(deletePaths, p) } } + sortByDepth(deletePaths) + for _, p := range deletePaths { + jsonStr, _ = sjson.Delete(jsonStr, p) + } // Remove x-* extension fields (e.g., x-google-enum-descriptions) that are not supported by Gemini API jsonStr = removeExtensionFields(jsonStr) return jsonStr @@ -581,6 +595,42 @@ func findPaths(jsonStr, field string) []string { return paths } +func findPathsByFields(jsonStr string, fields []string) map[string][]string { + set := make(map[string]struct{}, len(fields)) + for _, field := range fields { + set[field] = struct{}{} + } + paths := make(map[string][]string, len(set)) + walkForFields(gjson.Parse(jsonStr), "", set, paths) + return paths +} + +func walkForFields(value gjson.Result, path string, fields map[string]struct{}, paths map[string][]string) { + switch value.Type { + case gjson.JSON: + value.ForEach(func(key, val gjson.Result) bool { + keyStr := key.String() + safeKey := escapeGJSONPathKey(keyStr) + + var childPath string + if path == "" { + childPath = safeKey + } else { + childPath = path + "." + safeKey + } + + if _, ok := fields[keyStr]; ok { + paths[keyStr] = append(paths[keyStr], childPath) + } + + walkForFields(val, childPath, fields, paths) + return true + }) + case gjson.String, gjson.Number, gjson.True, gjson.False, gjson.Null: + // Terminal types - no further traversal needed + } +} + func sortByDepth(paths []string) { sort.Slice(paths, func(i, j int) bool { return len(paths[i]) > len(paths[j]) }) }