fix: flatten nested anyOf/oneOf in Gemini schema cleaning (openclaw#22825) thanks @Oceanswave

Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Oceanswave <760674+Oceanswave@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
Sean McLellan
2026-02-21 14:09:42 -05:00
committed by GitHub
parent f9108120c2
commit 00b98a368a
2 changed files with 55 additions and 0 deletions

View File

@@ -339,9 +339,63 @@ function cleanSchemaForGeminiWithDefs(
}
}
// Cloud Code Assist API rejects anyOf/oneOf in nested schemas even after
// simplifyUnionVariants runs above. Flatten remaining unions as a fallback:
// pick the common type or use the first variant's type so the tool
// declaration is accepted by Google's validation layer.
if (cleaned.anyOf && Array.isArray(cleaned.anyOf)) {
const flattened = flattenUnionFallback(cleaned, cleaned.anyOf);
if (flattened) {
return flattened;
}
}
if (cleaned.oneOf && Array.isArray(cleaned.oneOf)) {
const flattened = flattenUnionFallback(cleaned, cleaned.oneOf);
if (flattened) {
return flattened;
}
}
return cleaned;
}
/**
* Last-resort flattening for anyOf/oneOf arrays that could not be simplified
* by `simplifyUnionVariants`. Picks a representative type so the schema is
* accepted by Google's restricted JSON Schema validation.
*/
function flattenUnionFallback(
obj: Record<string, unknown>,
variants: unknown[],
): Record<string, unknown> | undefined {
const objects = variants.filter(
(v): v is Record<string, unknown> => !!v && typeof v === "object",
);
if (objects.length === 0) {
return undefined;
}
const types = new Set(objects.map((v) => v.type).filter(Boolean));
if (objects.length === 1) {
const merged: Record<string, unknown> = { ...objects[0] };
copySchemaMeta(obj, merged);
return merged;
}
if (types.size === 1) {
const merged: Record<string, unknown> = { type: Array.from(types)[0] };
copySchemaMeta(obj, merged);
return merged;
}
const first = objects[0];
if (first?.type) {
const merged: Record<string, unknown> = { type: first.type };
copySchemaMeta(obj, merged);
return merged;
}
const merged: Record<string, unknown> = {};
copySchemaMeta(obj, merged);
return merged;
}
export function cleanSchemaForGemini(schema: unknown): unknown {
if (!schema || typeof schema !== "object") {
return schema;