fix(gemini): convert prefixItems and ensure array items in schema sanitizer
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,8 @@ export const UNSUPPORTED_SCHEMA_CONSTRAINTS = [
|
|||||||
"uniqueItems", "contains",
|
"uniqueItems", "contains",
|
||||||
// 2020-12 keywords with no Gemini equivalent
|
// 2020-12 keywords with no Gemini equivalent
|
||||||
"unevaluatedProperties", "unevaluatedItems", "contentSchema",
|
"unevaluatedProperties", "unevaluatedItems", "contentSchema",
|
||||||
|
// Tuple-array keywords; converted to items first, leftovers stripped
|
||||||
|
"prefixItems", "additionalItems",
|
||||||
// Claude rejects these in VALIDATED mode
|
// Claude rejects these in VALIDATED mode
|
||||||
"default", "examples",
|
"default", "examples",
|
||||||
// JSON Schema meta keywords
|
// JSON Schema meta keywords
|
||||||
@@ -308,6 +310,37 @@ function ensureObjectType(obj) {
|
|||||||
for (const v of Object.values(obj)) if (v && typeof v === "object") ensureObjectType(v);
|
for (const v of Object.values(obj)) if (v && typeof v === "object") ensureObjectType(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert prefixItems (tuple validation) to items — Gemini cannot express tuples,
|
||||||
|
// and a type:"array" schema without items is rejected with "missing field"
|
||||||
|
function convertPrefixItems(obj) {
|
||||||
|
if (!obj || typeof obj !== "object") return;
|
||||||
|
|
||||||
|
if (Array.isArray(obj.prefixItems) && obj.prefixItems.length > 0) {
|
||||||
|
const variants = obj.prefixItems.filter(s => s && s.type !== "null");
|
||||||
|
if (!obj.items && variants.length === 1) {
|
||||||
|
obj.items = variants[0];
|
||||||
|
} else if (!obj.items && variants.length > 1) {
|
||||||
|
obj.items = { anyOf: variants };
|
||||||
|
}
|
||||||
|
delete obj.prefixItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const value of Object.values(obj)) {
|
||||||
|
if (value && typeof value === "object") {
|
||||||
|
convertPrefixItems(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gemini requires items on every type:"array" schema — fill a permissive placeholder
|
||||||
|
function ensureArrayItems(obj) {
|
||||||
|
if (!obj || typeof obj !== "object") return;
|
||||||
|
if (obj.type === "array" && !obj.items) {
|
||||||
|
obj.items = { type: "string" };
|
||||||
|
}
|
||||||
|
for (const v of Object.values(obj)) if (v && typeof v === "object") ensureArrayItems(v);
|
||||||
|
}
|
||||||
|
|
||||||
// Clean JSON Schema for Antigravity API compatibility - removes unsupported keywords recursively
|
// Clean JSON Schema for Antigravity API compatibility - removes unsupported keywords recursively
|
||||||
export function cleanJSONSchemaForAntigravity(schema) {
|
export function cleanJSONSchemaForAntigravity(schema) {
|
||||||
if (!schema || typeof schema !== "object") return schema;
|
if (!schema || typeof schema !== "object") return schema;
|
||||||
@@ -321,11 +354,13 @@ export function cleanJSONSchemaForAntigravity(schema) {
|
|||||||
|
|
||||||
// Phase 2: Flatten complex structures
|
// Phase 2: Flatten complex structures
|
||||||
mergeAllOf(cleaned);
|
mergeAllOf(cleaned);
|
||||||
|
convertPrefixItems(cleaned);
|
||||||
flattenAnyOfOneOf(cleaned);
|
flattenAnyOfOneOf(cleaned);
|
||||||
flattenTypeArrays(cleaned);
|
flattenTypeArrays(cleaned);
|
||||||
|
|
||||||
// Phase 2.5: Infer missing type=object when properties exist (Gemini requirement)
|
// Phase 2.5: Infer missing type=object when properties exist (Gemini requirement)
|
||||||
ensureObjectType(cleaned);
|
ensureObjectType(cleaned);
|
||||||
|
ensureArrayItems(cleaned);
|
||||||
|
|
||||||
// Phase 3: Remove all unsupported keywords at ALL levels (including inside arrays)
|
// Phase 3: Remove all unsupported keywords at ALL levels (including inside arrays)
|
||||||
removeUnsupportedKeywords(cleaned, UNSUPPORTED_SCHEMA_CONSTRAINTS);
|
removeUnsupportedKeywords(cleaned, UNSUPPORTED_SCHEMA_CONSTRAINTS);
|
||||||
|
|||||||
Reference in New Issue
Block a user