Skip to content

Fix Gemini schema conversion dropping anyOf and nullability#57160

Merged
rtfeldman merged 2 commits into
mainfrom
fix/gemini-tool-schema-nullability
May 19, 2026
Merged

Fix Gemini schema conversion dropping anyOf and nullability#57160
rtfeldman merged 2 commits into
mainfrom
fix/gemini-tool-schema-nullability

Conversation

@rtfeldman

@rtfeldman rtfeldman commented May 19, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #49292:

  • Bug fix in push_any_of_constraint: when both an anyOf and a non-empty allOf were already present at the same level, the existing anyOf was silently dropped. Now it's always preserved.
  • Canonical OpenAPI nullability: collapse {nullable: true}-only entries out of anyOf onto the parent so anyOf: [{type: "string"}, {type: "null"}] becomes {type: "string", nullable: true} (the form Gemini actually expects) instead of anyOf: [{type: "string"}, {nullable: true}].
  • Compile-time path consistency: route ToJsonSchemaSubsetTransform through the same convert_null_in_types_to_nullable helper so Rust-defined tools using Option<T> also keep nullability on Gemini, instead of silently truncating to the non-null type.
  • Drop an unnecessary clone in push_any_of_constraint and simplify convert_types_to_any_of_defs.
  • Add a small obj() test helper and a regression test for the anyOf + allOf + multi-type case.

Release Notes:

  • Fixed Option<T> tool parameters being sent to Gemini without their nullability, and fixed tool schemas with anyOf + allOf losing constraints during the OpenAPI 3.0 conversion

…type schema

Adapts MCP and Rust-defined tool schemas to OpenAPI 3.0's nullability
convention so Gemini accepts them:

- Convert type: [T, "null"] (and type: "null") into nullable: true
  alongside the remaining type.
- Convert multi-type arrays like type: ["string", "number"] into
  anyOf, combining with any pre-existing anyOf/allOf/oneOf via allOf
  without dropping constraints.
- Collapse {nullable: true}-only entries out of anyOf onto the parent
  so Option<T>-style schemas land as {type: T, nullable: true}.
- Apply the same nullable conversion in the compile-time
  ToJsonSchemaSubsetTransform so Rust-defined tools also keep
  nullability on Gemini.

Co-authored-by: Daniel Strobusch <1438302+dastrobu@users.noreply.github.com>
@rtfeldman rtfeldman self-assigned this May 19, 2026
@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label May 19, 2026
@zed-community-bot zed-community-bot Bot added the staff Pull requests authored by a current member of Zed staff label May 19, 2026
…a-nullability

# Conflicts:
#	crates/language_model_core/src/tool_schema.rs
@rtfeldman rtfeldman changed the title language_model_core: Fix Gemini tool schema nullability language_model_core: Preserve nullability and existing anyOf in Gemini tool schemas May 19, 2026
@rtfeldman rtfeldman changed the title language_model_core: Preserve nullability and existing anyOf in Gemini tool schemas Fix Gemini schema conversion dropping anyOf and nullability May 19, 2026
@rtfeldman rtfeldman marked this pull request as ready for review May 19, 2026 18:32
@rtfeldman rtfeldman added this pull request to the merge queue May 19, 2026
Merged via the queue into main with commit 8dff89c May 19, 2026
36 checks passed
@rtfeldman rtfeldman deleted the fix/gemini-tool-schema-nullability branch May 19, 2026 19:11
TomPlanche pushed a commit to TomPlanche/zed that referenced this pull request May 20, 2026
…stries#57160)

Follow-up to zed-industries#49292:

- **Bug fix in `push_any_of_constraint`**: when both an `anyOf` and a
non-empty `allOf` were already present at the same level, the existing
`anyOf` was silently dropped. Now it's always preserved.
- **Canonical OpenAPI nullability**: collapse `{nullable: true}`-only
entries out of `anyOf` onto the parent so `anyOf: [{type: "string"},
{type: "null"}]` becomes `{type: "string", nullable: true}` (the form
Gemini actually expects) instead of `anyOf: [{type: "string"},
{nullable: true}]`.
- **Compile-time path consistency**: route `ToJsonSchemaSubsetTransform`
through the same `convert_null_in_types_to_nullable` helper so
Rust-defined tools using `Option<T>` also keep nullability on Gemini,
instead of silently truncating to the non-null type.
- Drop an unnecessary clone in `push_any_of_constraint` and simplify
`convert_types_to_any_of_defs`.
- Add a small `obj()` test helper and a regression test for the `anyOf +
allOf + multi-type` case.

Release Notes:

- Fixed `Option<T>` tool parameters being sent to Gemini without their
nullability, and fixed tool schemas with `anyOf` + `allOf` losing
constraints during the OpenAPI 3.0 conversion

Co-authored-by: Daniel Strobusch <1438302+dastrobu@users.noreply.github.com>
TomPlanche pushed a commit to TomPlanche/zed that referenced this pull request Jun 2, 2026
…stries#57160)

Follow-up to zed-industries#49292:

- **Bug fix in `push_any_of_constraint`**: when both an `anyOf` and a
non-empty `allOf` were already present at the same level, the existing
`anyOf` was silently dropped. Now it's always preserved.
- **Canonical OpenAPI nullability**: collapse `{nullable: true}`-only
entries out of `anyOf` onto the parent so `anyOf: [{type: "string"},
{type: "null"}]` becomes `{type: "string", nullable: true}` (the form
Gemini actually expects) instead of `anyOf: [{type: "string"},
{nullable: true}]`.
- **Compile-time path consistency**: route `ToJsonSchemaSubsetTransform`
through the same `convert_null_in_types_to_nullable` helper so
Rust-defined tools using `Option<T>` also keep nullability on Gemini,
instead of silently truncating to the non-null type.
- Drop an unnecessary clone in `push_any_of_constraint` and simplify
`convert_types_to_any_of_defs`.
- Add a small `obj()` test helper and a regression test for the `anyOf +
allOf + multi-type` case.

Release Notes:

- Fixed `Option<T>` tool parameters being sent to Gemini without their
nullability, and fixed tool schemas with `anyOf` + `allOf` losing
constraints during the OpenAPI 3.0 conversion

Co-authored-by: Daniel Strobusch <1438302+dastrobu@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement staff Pull requests authored by a current member of Zed staff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants