Skip to content

fix(providers): normalize tool schemas for JSON Schema 2020-12 compliance (Bedrock/LiteLLM)#879

Open
umi008 wants to merge 5 commits into
Zoo-Code-Org:mainfrom
umi008:fix/869-maxitems-normalization
Open

fix(providers): normalize tool schemas for JSON Schema 2020-12 compliance (Bedrock/LiteLLM)#879
umi008 wants to merge 5 commits into
Zoo-Code-Org:mainfrom
umi008:fix/869-maxitems-normalization

Conversation

@umi008

@umi008 umi008 commented Jul 11, 2026

Copy link
Copy Markdown

Related GitHub Issue

Closes: #869

Description

Tool requests through the OpenAI Compatible provider path (used by 15+ providers including LiteLLM, OpenRouter, DeepSeek, xAI, etc.) were sending raw tool schemas without JSON Schema 2020-12 normalization. When the backend is AWS Bedrock (directly or through a LiteLLM proxy), it rejects schemas containing keywords like maxItems on array properties because Bedrock enforces strict 2020-12 compliance.

normalizeToolSchema() was already applied in the direct Bedrock and VSCode LM provider paths, but BaseProvider.convertToolSchemaForOpenAI() — the shared method used by all OpenAI-compatible providers — was not calling it.

Fix: convertToolSchemaForOpenAI() now calls normalizeToolSchema() first to convert deprecated type arrays to anyOf, strip unsupported format values, and enforce additionalProperties: false, before applying OpenAI-specific strict mode transforms (all-required, null removal). The two passes compose safely: normalization handles 2020-12 compliance, and the existing OpenAI pass adds the strict-mode constraints on top.

Test Procedure

  1. Configure an OpenAI Compatible provider pointing at a LiteLLM proxy backed by AWS Bedrock (or any backend enforcing strict JSON Schema 2020-12)
  2. Send a tool-enabled request (Code/Architect/Orchestrator mode)
  3. Verify the request succeeds — no 400 error about unsupported keywords like maxItems

Manual verification of schema transformation:

// Before: type arrays and maxItems pass through unchecked
// After: normalizeToolSchema() converts type arrays → anyOf and strips unsupported keywords

Pre-Submission Checklist

Screenshots / Videos

N/A — backend schema normalization, no UI changes.

Documentation Updates

  • No documentation updates are required.

Additional Notes

This fix benefits all providers that inherit from BaseProvider and use convertToolsForOpenAI() — OpenRouter, DeepSeek, xAI, Groq, Fireworks, Together, SambaNova, Baseten, Friendli, and others. The normalization is idempotent for well-formed schemas, so the risk of regression is minimal.

Summary by CodeRabbit

  • Bug Fixes
    • Improved OpenAI tool-schema conversion with an initial JSON Schema normalization step for more consistent output.
    • Enhanced robustness by safely handling non-object and unexpected schema shapes.
    • Improved schema compatibility by refining array handling (e.g., removing unsupported item properties) and correctly representing nullable types.
  • Tests
    • Expanded coverage for OpenAI/Bedrock JSON-Schema compatibility and additional edge cases in tool-schema conversion.

…nAI Compatible path

Call normalizeToolSchema() before applying OpenAI-specific strict mode
transforms in convertToolSchemaForOpenAI(). This ensures tool schemas are
2020-12 compliant (type arrays -> anyOf, unsupported formats stripped)
before backends like Bedrock validate them through OpenAI-compatible
proxies like LiteLLM.

Previously, only the direct provider paths (bedrock, vscode-lm) applied
this normalization. The 15+ providers that inherit from BaseProvider and
use convertToolsForOpenAI() were sending raw schemas that Bedrock rejects
with 'maxItems is not supported' errors.

Fixes Zoo-Code-Org#869
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

BaseProvider now normalizes tool schemas for JSON Schema 2020-12 compliance before applying OpenAI strict-mode transformations, with guards for invalid inputs and normalized results.

Changes

OpenAI tool schema normalization

Layer / File(s) Summary
Normalize schemas before OpenAI conversion
src/utils/json-schema.ts, src/api/providers/base-provider.ts
Tool-schema normalization removes unsupported array constraints, extracts string constraints, and runs before OpenAI conversion. The conversion uses the normalized object schema with early guards.
Validate normalized tool schemas
src/api/providers/__tests__/base-provider.spec.ts
Tests cover removal of maxItems, nullable anyOf conversion, and non-object schemas without additionalProperties.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • #308 — Both changes address JSON Schema normalization for provider-compatible tool schemas.

Suggested reviewers: hannesrudolph

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main fix: normalizing OpenAI-compatible tool schemas for JSON Schema 2020-12 and Bedrock.
Description check ✅ Passed The PR description covers the issue link, motivation, implementation, testing, checklist, and ancillary sections; only Get in Touch is omitted.
Linked Issues check ✅ Passed The code and tests address #869 by normalizing shared OpenAI-compatible tool schemas before Bedrock-sensitive strict-mode transforms.
Out of Scope Changes check ✅ Passed The changes are focused on schema normalization and related tests, with no clear unrelated edits.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/api/providers/base-provider.ts`:
- Around line 70-84: Update the schema handling around normalizeToolSchema so
object-like normalized schemas with properties and additionalProperties: false
are processed even when type is absent, rather than returning early; use an
object-shape check that also covers flattened compositions and nullable object
conversions. In the same logic, remove the redundant Array.isArray(prop.type)
branch and rely on normalizeToolSchema’s recursive type-array normalization
before applying strict required-field and nested-property processing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8f75b99a-dfae-41c8-8a20-c5926d1ab896

📥 Commits

Reviewing files that changed from the base of the PR and between 116b70e and 351d15e.

📒 Files selected for processing (1)
  • src/api/providers/base-provider.ts

Comment thread src/api/providers/base-provider.ts Outdated
… call

The spread of normalied (Record<string, unknown>) caused TS7053
because the inferred type was {} and string indexing was not allowed.
Added explicit Record<string, any> type annotation to fix compilation.
@umi008
umi008 force-pushed the fix/869-maxitems-normalization branch from 1a45e3a to feb79dd Compare July 11, 2026 03:10
…ation

Two test updates after adding normalizeToolSchema() to the schema pipeline:

1. Nullable types test: Now asserts anyOf conversion instead of null
   stripping. normalizeToolSchema() converts type arrays to anyOf for
   JSON Schema 2020-12 compliance, which is correct behavior.

2. Non-object schemas test: Verifies type preservation through
   normalization. additionalProperties is NOT added to non-object
   schemas (strings, numbers, etc.) because the JSON Schema spec says
   it's only valid on object types.
@umi008
umi008 force-pushed the fix/869-maxitems-normalization branch from 9c7204b to 826eb83 Compare July 11, 2026 03:19
@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/api/providers/base-provider.ts 71.42% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Jul 11, 2026
@taltas

taltas commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

@roomote Do a review please

@roomote

roomote Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

I saw the mention, but I could not start work on this PR with the current Roomote GitHub setup.

@edelauna edelauna left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution, had some comments on the implementation and how it related to your inspiration for opening the PR.

Comment thread src/api/providers/base-provider.ts
Comment thread src/api/providers/base-provider.ts Outdated
Comment thread src/api/providers/__tests__/base-provider.spec.ts
Comment thread src/api/providers/__tests__/base-provider.spec.ts Outdated
Comment thread src/api/providers/__tests__/base-provider.spec.ts Outdated
…bjects, improve tests

- Remove minItems/maxItems from ARRAY_SPECIFIC_PROPERTIES so they
  dont get forwarded to array-branch schemas (Bedrock rejects them)
- Also strip minLength, maxLength, pattern from normalize output
- Fix nullable object handling: check for properties field as well
  as type=object so nullable schemas (converted to anyOf) still get
  strict-mode processing (required/boolean/null removal)
- Add test for maxItems stripping on array schemas
- Add assertion that stale type array field is removed after normalization
- Fix non-object schema test to use type: ["string"] (actually changed
  by normalization) and assert anyOf output

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/api/providers/__tests__/base-provider.spec.ts (1)

175-181: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Drop the redundant nullable-type branch normalizeToolSchema() already normalizes nested schemas recursively, so convertToolSchemaForOpenAI() doesn’t need the Array.isArray(prop.type) && prop.type.includes("null") path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/__tests__/base-provider.spec.ts` around lines 175 - 181,
Remove the redundant nullable-type handling branch that checks
Array.isArray(prop.type) and prop.type.includes("null") from
convertToolSchemaForOpenAI(). Rely on normalizeToolSchema() to recursively
convert nullable nested schemas, while preserving the existing normalization and
OpenAI schema conversion behavior for other property types.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/api/providers/__tests__/base-provider.spec.ts`:
- Around line 175-181: Remove the redundant nullable-type handling branch that
checks Array.isArray(prop.type) and prop.type.includes("null") from
convertToolSchemaForOpenAI(). Rely on normalizeToolSchema() to recursively
convert nullable nested schemas, while preserving the existing normalization and
OpenAI schema conversion behavior for other property types.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8f88462f-0d9a-4a62-98b1-35ab2cf903bb

📥 Commits

Reviewing files that changed from the base of the PR and between feb79dd and 8289a9a.

📒 Files selected for processing (3)
  • src/api/providers/__tests__/base-provider.spec.ts
  • src/api/providers/base-provider.ts
  • src/utils/json-schema.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/api/providers/base-provider.ts

@github-actions github-actions Bot added awaiting-author PR is waiting for the author to address requested changes and removed awaiting-review PR changes are ready and waiting for maintainer re-review labels Jul 17, 2026
@github-actions github-actions Bot added awaiting-author PR is waiting for the author to address requested changes and removed awaiting-author PR is waiting for the author to address requested changes labels Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-author PR is waiting for the author to address requested changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] OpenAI Compatible provider (Bedrock backend) rejects maxItems in tool schema — normalizeToolSchema() fix may not cover this code path

3 participants