Skip to content

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

Open
umi008 wants to merge 3 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 3 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 compatibility when converting tool schemas for OpenAI.
    • Added safer handling for invalid or unexpected schema formats.
    • Normalizes incoming JSON schemas before applying OpenAI strict-mode transformations to improve consistency and reliability.

…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

Warning

Review limit reached

@umi008, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 6 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a3f17fb1-deb8-470a-88d0-d01e0851d08b

📥 Commits

Reviewing files that changed from the base of the PR and between feb79dd and 826eb83.

📒 Files selected for processing (1)
  • src/api/providers/__tests__/base-provider.spec.ts
📝 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/api/providers/base-provider.ts
convertToolSchemaForOpenAI validates the input, calls normalizeToolSchema, validates the normalized object schema, and builds the conversion result from the normalized copy. Documentation and imports reflect the new normalization step.

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

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The change matches issue #869 by normalizing shared OpenAI-compatible tool schemas before Bedrock-strict transforms.
Out of Scope Changes check ✅ Passed The diff stays focused on the schema-normalization fix in BaseProvider and shows no unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes the main change: normalizing tool schemas for OpenAI-compatible providers.
Description check ✅ Passed The description follows the template well, includes the linked issue, implementation summary, testing steps, and checklist items.
✨ 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

✅ All modified and coverable lines are covered by tests.

📢 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-review PR changes are ready and waiting for maintainer re-review

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

1 participant