diff --git a/src/agents/schema/clean-for-gemini.ts b/src/agents/schema/clean-for-gemini.ts index d87bcdcbbc8..e18d2e8c18d 100644 --- a/src/agents/schema/clean-for-gemini.ts +++ b/src/agents/schema/clean-for-gemini.ts @@ -29,6 +29,16 @@ export const GEMINI_UNSUPPORTED_SCHEMA_KEYWORDS = new Set([ "maxProperties", ]); +const SCHEMA_META_KEYS = ["description", "title", "default"] as const; + +function copySchemaMeta(from: Record, to: Record): void { + for (const key of SCHEMA_META_KEYS) { + if (key in from && from[key] !== undefined) { + to[key] = from[key]; + } + } +} + // Check if an anyOf/oneOf array contains only literal values that can be flattened. // TypeBox Type.Literal generates { const: "value", type: "string" }. // Some schemas may use { enum: ["value"], type: "string" }. @@ -164,6 +174,39 @@ function tryResolveLocalRef(ref: string, defs: SchemaDefs | undefined): unknown return defs.get(name); } +function simplifyUnionVariants(params: { obj: Record; variants: unknown[] }): { + variants: unknown[]; + simplified?: unknown; +} { + const { obj, variants } = params; + + const { variants: nonNullVariants, stripped } = stripNullVariants(variants); + + const flattened = tryFlattenLiteralAnyOf(nonNullVariants); + if (flattened) { + const result: Record = { + type: flattened.type, + enum: flattened.enum, + }; + copySchemaMeta(obj, result); + return { variants: nonNullVariants, simplified: result }; + } + + if (stripped && nonNullVariants.length === 1) { + const lone = nonNullVariants[0]; + if (lone && typeof lone === "object" && !Array.isArray(lone)) { + const result: Record = { + ...(lone as Record), + }; + copySchemaMeta(obj, result); + return { variants: nonNullVariants, simplified: result }; + } + return { variants: nonNullVariants, simplified: lone }; + } + + return { variants: stripped ? nonNullVariants : variants }; +} + function cleanSchemaForGeminiWithDefs( schema: unknown, defs: SchemaDefs | undefined, @@ -198,20 +241,12 @@ function cleanSchemaForGeminiWithDefs( const result: Record = { ...(cleaned as Record), }; - for (const key of ["description", "title", "default"]) { - if (key in obj && obj[key] !== undefined) { - result[key] = obj[key]; - } - } + copySchemaMeta(obj, result); return result; } const result: Record = {}; - for (const key of ["description", "title", "default"]) { - if (key in obj && obj[key] !== undefined) { - result[key] = obj[key]; - } - } + copySchemaMeta(obj, result); return result; } @@ -229,74 +264,18 @@ function cleanSchemaForGeminiWithDefs( : undefined; if (hasAnyOf) { - const { variants: nonNullVariants, stripped } = stripNullVariants(cleanedAnyOf ?? []); - if (stripped) { - cleanedAnyOf = nonNullVariants; - } - - const flattened = tryFlattenLiteralAnyOf(nonNullVariants); - if (flattened) { - const result: Record = { - type: flattened.type, - enum: flattened.enum, - }; - for (const key of ["description", "title", "default"]) { - if (key in obj && obj[key] !== undefined) { - result[key] = obj[key]; - } - } - return result; - } - if (stripped && nonNullVariants.length === 1) { - const lone = nonNullVariants[0]; - if (lone && typeof lone === "object" && !Array.isArray(lone)) { - const result: Record = { - ...(lone as Record), - }; - for (const key of ["description", "title", "default"]) { - if (key in obj && obj[key] !== undefined) { - result[key] = obj[key]; - } - } - return result; - } - return lone; + const simplified = simplifyUnionVariants({ obj, variants: cleanedAnyOf ?? [] }); + cleanedAnyOf = simplified.variants; + if ("simplified" in simplified) { + return simplified.simplified; } } if (hasOneOf) { - const { variants: nonNullVariants, stripped } = stripNullVariants(cleanedOneOf ?? []); - if (stripped) { - cleanedOneOf = nonNullVariants; - } - - const flattened = tryFlattenLiteralAnyOf(nonNullVariants); - if (flattened) { - const result: Record = { - type: flattened.type, - enum: flattened.enum, - }; - for (const key of ["description", "title", "default"]) { - if (key in obj && obj[key] !== undefined) { - result[key] = obj[key]; - } - } - return result; - } - if (stripped && nonNullVariants.length === 1) { - const lone = nonNullVariants[0]; - if (lone && typeof lone === "object" && !Array.isArray(lone)) { - const result: Record = { - ...(lone as Record), - }; - for (const key of ["description", "title", "default"]) { - if (key in obj && obj[key] !== undefined) { - result[key] = obj[key]; - } - } - return result; - } - return lone; + const simplified = simplifyUnionVariants({ obj, variants: cleanedOneOf ?? [] }); + cleanedOneOf = simplified.variants; + if ("simplified" in simplified) { + return simplified.simplified; } }