Skip to content

fix(translator): prevent schema property name collision in Gemini sanitizer (#13057, #13477) - #13059

Closed
zcrew0x wants to merge 1 commit into
cheaper-inference:release/v3.8.51from
zcrew0x:fix/gemini-schema-properties-collision
Closed

zcrew0x wants to merge 1 commit into
cheaper-inference:release/v3.8.51from
zcrew0x:fix/gemini-schema-properties-collision

Conversation

@zcrew0x

@zcrew0x zcrew0x commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #13057
Closes #13477

Problem

When a tool parameter schema contains a property named "properties", "required", etc. (e.g. in Roblox Studio, Blender, JSON Patch, or delivery.pin):

{
  "type": "object",
  "properties": {
    "action": { "type": "string" },
    "properties": { "type": "array", "items": { "type": "string" } }
  },
  "required": ["action"]
}

Schema visitor functions in open-sse/translator/helpers/geminiHelper.ts (such as injectObjectType, addPlaceholders, cleanupRequired, etc.) traversed schemas by doing a blind for (const value of Object.values(record)). When visiting schema.properties, because record.type was undefined and record.properties (or record.required) existed, injectObjectType injected schema.properties.type = "object" directly into the property dictionary.

This corrupted the schema sent to Google CloudCode/Gemini:

{
  "type": "object",
  "properties": {
    "action": { "type": "string" },
    "properties": { "type": "array", "items": { "type": "string" } },
    "type": "object"  <-- Invalid schema property
  }
}

Google rejected the request with HTTP 400:

"Invalid value at 'request.tools[0].function_declarations[1].parameters.properties[2].value' (type.googleapis.com/google.cloud.aiplatform.master.Schema), 'object'"

Solution

  1. Introduce forEachSubschema in geminiHelper.ts to traverse subschemas according to JSON Schema structure (descending into values of properties, patternProperties, $defs, definitions rather than treating the container maps as schema nodes).
  2. Use forEachSubschema across all schema visitors in geminiHelper.ts (injectObjectType, addPlaceholders, cleanupRequired, normalizeAdditionalProperties, convertConstToEnum, convertEnumValuesToStrings, mergeAllOf, flattenAnyOfOneOf, and flattenTypeArrays).
  3. Add regression unit test suite in tests/unit/gemini-schema-properties-name-collision-13057.test.ts covering both property named "properties" (fix(providers): Gemini schema sanitizer corrupts tools containing property named 'properties' (HTTP 400) & keepalive chunk desync in AI-SDK #13057) and property named "required" ([BUG] Antigravity (AGY) Provider Returns 400 Bad Request - Schema Incompatibility with Google Gemini Tools #13477).

@zcrew0x
zcrew0x force-pushed the fix/gemini-schema-properties-collision branch from 3ea5fe1 to a596e55 Compare September 9, 2026 05:24
fenix007 pushed a commit to fenix007/OmniRoute that referenced this pull request Sep 9, 2026
Adapt upstream PR cheaper-inference#13059 to the existing v3.8.48 sanitizer phases. Preserve tool arguments and response-schema fields while retaining actual constraint cleanup. Add regression coverage for OpenAI and Anthropic request translation and record upstream maintenance decisions.

Co-Authored-By: zcrew0x <zcrew0x@users.noreply.github.com>

Co-Authored-By: GPT-6 <noreply@openai.com>
@ekinnee

ekinnee commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@zcrew0x target release/v3.8.51 please

@zcrew0x
zcrew0x changed the base branch from main to release/v3.8.51 September 11, 2026 06:33
@zcrew0x
zcrew0x force-pushed the fix/gemini-schema-properties-collision branch from a596e55 to 8e98197 Compare September 11, 2026 06:46
@zcrew0x

zcrew0x commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

@ekinnee Rebased onto release/v3.8.51 and changed target base branch to release/v3.8.51 as requested. All unit tests passing.

@sprintberlin

Copy link
Copy Markdown
Contributor

Verified against the exact #13477 Stage payload

This PR also fixes the concrete Antigravity/Gemini 400 reported in #13477.

I replayed the exact captured Stage request shape locally through buildGeminiTools:

  • target: antigravity/gemini-3.8-flash-tiered
  • result on Stage: HTTP 400 before inference
  • catalog: 62 tools
  • culprit isolated by tool/property bisection: message (index 31), specifically delivery.pin
  • delivery.pin is anyOf: [boolean, object]; the object branch has properties named enabled, notify, and required

On current release/v3.8.51, the sanitizer produces this invalid shape:

{
  "type": "object",
  "properties": {
    "enabled": { "type": "boolean" },
    "notify": { "type": "boolean" },
    "required": { "type": "boolean" },
    "type": "object"
  },
  "required": ["enabled"]
}

The last entry is the bug: injectObjectType treats the properties map itself as a schema node because that map contains a user property named required, then injects a phantom type: "object" inside the property map. Google's Schema proto rejects the string where it expects a property schema object.

At this PR's head (8e981974), the same captured tool becomes:

{
  "type": "object",
  "properties": {
    "enabled": { "type": "boolean" },
    "notify": { "type": "boolean" },
    "required": { "type": "boolean" }
  },
  "required": ["enabled"]
}

No phantom properties.type, and anyOf/oneOf are absent. I also ran the complete captured 62-tool catalog through this PR: all 62 declarations survive, no union keyword survives the Gemini cleaner, and no scalar type entry is injected into a properties map. Focused tests at this head are green: gemini-schema-properties-name-collision-13057, gemini-malformed-required-and-bare-map-12269, and gemini-helper — 38/38.

So #13477 is not a separate converter problem in this reproduced case; it is another real-world manifestation of #13057's unsafe schema-map traversal. Please consider adding Closes #13477 (or at least Related to #13477) and pinning the captured delivery.pin shape as an additional regression case. I will not open a competing PR for the same root cause.

@zcrew0x zcrew0x closed this Sep 13, 2026
@zcrew0x
zcrew0x force-pushed the fix/gemini-schema-properties-collision branch from 8e98197 to 4694ef6 Compare September 13, 2026 16:13
@zcrew0x zcrew0x changed the title fix(translator): prevent schema property name collision in Gemini sanitizer (#13057) fix(translator): prevent schema property name collision in Gemini sanitizer (#13057, #13477) Sep 13, 2026
@zcrew0x

zcrew0x commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

@sprintberlin Thank you for reproducing and validating against the #13477 stage payload! I have added the delivery.pin regression test case covering the required property name collision and updated the PR description to close #13477.

@zcrew0x

zcrew0x commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #13690 with clean history on release/v3.8.51 and the added regression test for #13477 (delivery.pin.required).

diegosouzapw added a commit that referenced this pull request Sep 17, 2026
…-09-15) (#13731)

Second `npm run release:reconcile` pass on `release/v3.8.50..release/v3.8.51`
(0915890..c0f92ec, 916 non-merge commits, 877 merged PRs):

- fold the 173 changelog.d fragments accumulated since #12971 under
  `## [3.8.51]` and delete them
- generate bullets for the 58 cycle commits that had no fragment
  (4 features / 42 fixes / 12 maintenance), each with the merged PR link and
  `— thanks @author`
- link 137 fragment bullets to the PR of the commit that added them and
  credit the author; two prefix/origin mismatches reviewed (#12945→#13392,
  #13001→#13379, both maintainer rebaselines of other people's PRs)
- refresh "Release by the numbers" + Top-25 and regenerate the
  `### 🙌 Contributors` hall (112 external contributors + maintainer; every
  non-bot author of the 877 merged PRs present)
- closed-PR credit audit for the window: nothing to add (#13215→#13361 and
  #13059→#13690 are still open, #12998 was independently fixed earlier by
  #12853); no human co-author trailers, no commits without a PR
- resync the 58 i18n CHANGELOG mirrors

Gates: check:changelog-integrity OK, check:docs-sync PASS.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…-09-15) (cheaper-inference#13731)

Second `npm run release:reconcile` pass on `release/v3.8.50..release/v3.8.51`
(104d4f8..d61b804, 916 non-merge commits, 877 merged PRs):

- fold the 173 changelog.d fragments accumulated since cheaper-inference#12971 under
  `## [3.8.51]` and delete them
- generate bullets for the 58 cycle commits that had no fragment
  (4 features / 42 fixes / 12 maintenance), each with the merged PR link and
  `— thanks @author`
- link 137 fragment bullets to the PR of the commit that added them and
  credit the author; two prefix/origin mismatches reviewed (cheaper-inference#12945→cheaper-inference#13392,
  cheaper-inference#13001→cheaper-inference#13379, both maintainer rebaselines of other people's PRs)
- refresh "Release by the numbers" + Top-25 and regenerate the
  `### 🙌 Contributors` hall (112 external contributors + maintainer; every
  non-bot author of the 877 merged PRs present)
- closed-PR credit audit for the window: nothing to add (cheaper-inference#13215→cheaper-inference#13361 and
  cheaper-inference#13059→cheaper-inference#13690 are still open, cheaper-inference#12998 was independently fixed earlier by
  cheaper-inference#12853); no human co-author trailers, no commits without a PR
- resync the 58 i18n CHANGELOG mirrors

Gates: check:changelog-integrity OK, check:docs-sync PASS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants