Skip to content

fix(opencode): fold schema items into gemini union branches - #43495

Open
Role1776 wants to merge 1 commit into
anomalyco:devfrom
Role1776:fix/gemini-tool-schema-anyof-items
Open

fix(opencode): fold schema items into gemini union branches#43495
Role1776 wants to merge 1 commit into
anomalyco:devfrom
Role1776:fix/gemini-tool-schema-anyof-items

Conversation

@Role1776

Copy link
Copy Markdown

Issue for this PR

Closes #43494

Type of change

  • Bug fix

What does this PR do?

When a tool input is declared as a nullable array (type: ["null", "array"]), @ai-sdk/google's convertJSONSchemaToOpenAPISchema emits anyOf: [{ type: "array" }] but leaves a dangling sibling items at the parent level. Gemini rejects the resulting function declaration, so any tool with a nullable-array input fails against @ai-sdk/google / @ai-sdk/google-vertex.

This PR adds a foldArrayItems helper in packages/opencode/src/session/llm/request.ts that recursively folds the sibling items into the array-typed branches of any union (anyOf/oneOf/allOf) — and rewrites the parent type: ["null", "array"] form into an anyOf of an array branch carrying items plus a null branch. It mutates the plain schema in place so the surrounding jsonSchema() wrapper (whose jsonSchema getter returns this same reference) keeps working, and only runs for models whose npm is @ai-sdk/google or @ai-sdk/google-vertex.

How did you verify your code works?

Verified locally that a tool whose input schema is type: ["null", "array"] now produces a Gemini function declaration with items nested inside the array branch, and that the schema error no longer occurs for Google/Vertex models.

Screenshots / recordings

N/A (not a UI change)

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference; please use your judgment.

The workaround direction is right — fixing the schema before @ai-sdk/google's OpenAPI conversion instead of patching generated output — and correctly scoped to google/google-vertex only. Some concerns:

  • packages/opencode/src/session/llm/request.ts:184-193 — foldArrayItems mutates the tool's inputSchema object in place, and tool objects are typically defined once at module scope. Why it matters: the first request through a Google model permanently rewrites the shared schema for every later request, including other providers — they'll now see the anyOf form instead of their preferred type:["null","array"] shape. It happens to be idempotent on re-entry, but cross-provider contamination by hidden global mutation is fragile. Suggestion: structuredClone the plain schema per request and return a shallow-copied tool ({ ...tool, inputSchema: clone }) from the map.

  • packages/opencode/src/session/llm/request.ts (foldArrayItems, type-array branch) — A multi-type union like type: ["string","array"] collapses to anyOf:[{type:"array",items}] (plus null branch), silently discarding the "string" alternative. Rare in practice, but zod-style pipelines can emit it, and the tool then rejects valid string inputs at the model/API boundary. Suggestion: carry non-array, non-null members over as extra branches.

  • packages/opencode/src/session/llm/request.ts (combiner tail) — When a combiner exists but no branch is array-typed, the final delete schema.items silently drops the constraint, weakening validation with no signal. Suggestion: keep the items (or log) when nothing matched, rather than quiet data loss.

  • packages/opencode/src/session/llm/request.ts:185-187 — The extraction heuristic isRecord(schema.jsonSchema) ? schema.jsonSchema : schema will happily treat a raw Zod instance (or any object lacking those keys) as a plain JSON schema and traverse/mutate its internals. Suggestion: verify the candidate actually looks like JSON Schema (has one of type/properties/items/$ref/anyOf…) before folding.

  • Testing — This recursive transformer has exactly the kind of edge cases that regress silently (nested nullable arrays, anyOf branches that already carry items, double-folding idempotency, multi-type unions) and the PR adds no tests. Suggestion: add unit tests covering at least the nullable-array fix case plus idempotency.

  • Nit: folding items into every branch of an allOf changes intersection semantics; consider restricting the combiner fix to anyOf/oneOf.

No blocking issues found.

@Role1776
Role1776 force-pushed the fix/gemini-tool-schema-anyof-items branch from cd39190 to f6a0c55 Compare August 22, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gemini tool schema fails when a tool input is a nullable array

2 participants