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:
@@ -129,6 +129,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/Subagents: restore announce-chain delivery to agent injection, defer nested announce output until descendant follow-up content is ready, and prevent descendant deferrals from consuming announce retry budget so deep chains do not drop final completions. (#22223) Thanks @tyler6204.
|
||||
- Agents/System Prompt: label allowlisted senders as authorized senders to avoid implying ownership. Thanks @thewilloftheshadow.
|
||||
- Agents/Tool display: fix exec cwd suffix inference so `pushd ... && popd ... && <command>` does not keep stale `(in <dir>)` context in summaries. (#21925) Thanks @Lukavyi.
|
||||
- Agents/Google: flatten residual nested `anyOf`/`oneOf` unions in Gemini tool-schema cleanup so Cloud Code Assist no longer rejects unsupported union keywords that survive earlier simplification. (#22825) Thanks @Oceanswave.
|
||||
- Tools/web_search: handle xAI Responses API payloads that emit top-level `output_text` blocks (without a `message` wrapper) so Grok web_search no longer returns `No response` for those results. (#20508) Thanks @echoVic.
|
||||
- Agents/Failover: treat non-default override runs as direct fallback-to-configured-primary (skip configured fallback chain), normalize default-model detection for provider casing/whitespace, and add regression coverage for override/auth error paths. (#18820) Thanks @Glucksberg.
|
||||
- Docker/Build: include `ownerDisplay` in `CommandsSchema` object-level defaults so Docker `pnpm build` no longer fails with `TS2769` during plugin SDK d.ts generation. (#22558) Thanks @obviyus.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user