fix(gemini): strip unsupported dependent schema keywords

Closes: #6539
This commit is contained in:
Luis Pater
2026-10-12 17:48:42 +08:00
parent 7a687f424c
commit 9126772dfd
2 changed files with 97 additions and 0 deletions

View File

@@ -1322,6 +1322,7 @@ func removeUnsupportedKeywords(jsonStr string, options jsonSchemaCleanOptions) s
"$schema", "$defs", "definitions", "const", "$ref", "$id", "id", "additionalProperties",
"$anchor", "$vocabulary", "$dynamicRef", "$dynamicAnchor",
"propertyNames", "patternProperties", // Gemini doesn't support these schema keywords
"dependentRequired", "dependentSchemas", "dependencies",
"if", "then", "else",
"$comment", "enumDescriptions", "enumTitles", "prefill", "deprecated", "encrypted", // Schema metadata fields unsupported by Gemini
"additionalItems", "unevaluatedProperties", "unevaluatedItems", "contentSchema",

View File

@@ -3039,3 +3039,99 @@ func TestCleanJSONSchema_EnforcesObjectTypeForProperties_Issue6394(t *testing.T)
}
})
}
// TestCleanJSONSchema_RemovesDependentKeywords_Issue6539 covers Issue #6539:
// JSON Schema keywords dependentRequired, dependentSchemas, and dependencies are rejected by
// the Gemini/Antigravity backend with 400 INVALID_ARGUMENT ("Unknown name \"dependentRequired\" ... Cannot find field")
// and must be stripped while preserving legitimate tool properties with those names.
func TestCleanJSONSchema_RemovesDependentKeywords_Issue6539(t *testing.T) {
input := `{
"type": "object",
"properties": {
"baseline_url": {"type": "string"},
"candidate_url": {"type": "string"},
"options": {
"type": "object",
"properties": {
"threshold": {"type": "number"},
"diff_color": {"type": "string"}
},
"dependentRequired": {
"threshold": ["diff_color"]
},
"dependentSchemas": {
"threshold": {
"properties": {
"diff_color": {"type": "string"}
}
}
},
"dependencies": {
"threshold": ["diff_color"]
}
},
"dependentRequired": {
"type": "string",
"description": "Legitimate property named dependentRequired"
},
"dependentSchemas": {
"type": "object",
"properties": {
"nested": {"type": "string"}
},
"dependentRequired": {
"nested": ["other"]
}
},
"dependencies": {
"type": "string",
"description": "Legitimate property named dependencies"
}
},
"dependentRequired": {
"baseline_url": ["candidate_url"]
},
"dependentSchemas": {
"baseline_url": {
"required": ["candidate_url"]
}
},
"dependencies": {
"baseline_url": ["candidate_url"]
}
}`
cleaners := map[string]func(string) string{
"Antigravity": CleanJSONSchemaForAntigravity,
"AntigravityTool": func(s string) string { return CleanJSONSchemaForAntigravityTool(s, false) },
"AntigravityResponse": CleanJSONSchemaForAntigravityResponse,
"Gemini": CleanJSONSchemaForGemini,
"GeminiJSONSchema": CleanJSONSchemaForGeminiJSONSchema,
}
for name, clean := range cleaners {
t.Run(name, func(t *testing.T) {
got := clean(input)
parsed := gjson.Parse(got)
for _, kw := range []string{"dependentRequired", "dependentSchemas", "dependencies"} {
if parsed.Get(kw).Exists() {
t.Errorf("[%s] root %q was not removed: %s", name, kw, got)
}
if parsed.Get("properties.options." + kw).Exists() {
t.Errorf("[%s] nested properties.options.%s was not removed: %s", name, kw, got)
}
}
if parsed.Get("properties.dependentSchemas.dependentRequired").Exists() {
t.Errorf("[%s] properties.dependentSchemas.dependentRequired was not removed: %s", name, got)
}
for _, prop := range []string{"dependentRequired", "dependentSchemas", "dependencies"} {
if !parsed.Get("properties." + prop).Exists() {
t.Errorf("[%s] legitimate property %q was removed: %s", name, prop, got)
}
}
})
}
}