Skip to content

fix(translator): sanitize tool schemas on the responses to chat path - #18806

Merged
diegosouzapw merged 4 commits into
cheaper-inference:release/v3.8.53from
gaurav0107:fix/18679-responses-chat-tool-schema-sanitize
Nov 4, 2026
Merged

diegosouzapw merged 4 commits into
cheaper-inference:release/v3.8.53from
gaurav0107:fix/18679-responses-chat-tool-schema-sanitize

Conversation

@gaurav0107

@gaurav0107 gaurav0107 commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • openaiResponsesToOpenAIRequest forwarded client tool schemas to the Chat Completions upstream unchanged. Harnesses that deep-truncate their schemas (Codex Desktop) send array keywords replaced by placeholder strings, e.g. "enum": "[MaxDepth]". Ollama Cloud validates tool schemas with Go structs and rejects the request before inference: cannot unmarshal string into Go struct field ToolProperty.enum of type []interface {}.
  • A post-pass now runs every emitted function.parameters through the existing stripInvalidSchemaConstructs sanitizer, which the Claude path already uses (as suggested in the issue and confirmed by the maintainer). One pass covers plain function tools, flattened namespace (MCP) sub-tools, tool_search, and tools that arrive already in Chat shape.
  • Valid schemas are unchanged. The schemaCoercion.ts doc comment, which said OpenAI targets tolerate these constructs, now notes the strict-validator case.

Related Issues

Target Branch

  • Base is the active release/v3.8.x branch (release/v3.8.53), not main
  • New feature? Not applicable: this is a bug fix

Validation

  • Change type: routing / translator
  • Focused tests: the new test file plus every test file that calls openaiResponsesToOpenAIRequest, and the schema-coercion suites
  • eslint + prettier --check on the changed files, and npm run check:open-sse-typecheck
  • Reconciled with the current release/v3.8.53 tip
  • Production-code change includes a new automated test

Without the fix, the new test fails on the three invalid-schema cases:

✖ #18679 Responses -> Chat drops a string-valued enum from a function tool schema
✖ #18679 Responses -> Chat sanitizes namespace (MCP) sub-tool schemas too
✖ #18679 Responses -> Chat coerces an index-keyed enum object to an array
✔ #18679 Responses -> Chat leaves a valid tool schema unchanged
ℹ tests 4
ℹ pass 1
ℹ fail 3

With the fix:

$ node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit tests/unit/responses-to-chat-tool-schema-sanitize-18679.test.ts
ℹ tests 4
ℹ pass 4
ℹ fail 0

# 56 files: every test that imports openaiResponsesToOpenAIRequest, plus the
# openai-responses / responses-to-chat / schema-coercion suites
ℹ tests 523
ℹ pass 523
ℹ fail 0

$ npm run check:open-sse-typecheck
openSseTypecheckErrors=0

$ npm run check:file-size && npm run check:complexity && npm run check:test-discovery
OK / OK / OK

Tests Added Or Updated

  • Changelog fragment: changelog.d/fixes/18806-responses-chat-tool-schema-sanitize.md.

  • tests/unit/responses-to-chat-tool-schema-sanitize-18679.test.ts (new) asserts on the translated Chat body:

    • a function tool with "enum": "[MaxDepth]" nested under anyOf[0].properties (the shape from the report) has no string-valued enum left, and its valid sibling enum is kept;
    • the same schema on a namespace (MCP) sub-tool is sanitized the same way;
    • an index-keyed enum object ({"0":"a","1":"b"}) becomes ["a","b"];
    • a valid schema with additionalProperties: false is deep-equal to the input.

Coverage Notes

  • The open-sse/translator/request/openai-responses.ts change (the new sanitizeChatToolParameters helper and its call site) is covered by the new test file and by the existing translator suites listed above. schemaCoercion.ts changes only a comment.

Reviewer Notes

  • stripInvalidSchemaConstructs also turns numeric-string constraints into numbers (minimum: "5" to 5) and replaces placeholder strings in schema positions with {}. These Chat targets now get the same normalization the Claude path already applies.
  • Tools without function.parameters (for example web_search passed through as-is) are returned by reference, unchanged.

The Responses -> Chat Completions translator forwarded client tool
schemas unchanged. Harnesses that deep-truncate their schemas (Codex
Desktop) send array keywords replaced by placeholder strings such as
"enum": "[MaxDepth]". Strict OpenAI-compatible validators reject these
before inference; Ollama Cloud fails with "cannot unmarshal string into
Go struct field ToolProperty.enum".

Run every emitted function.parameters through the existing
stripInvalidSchemaConstructs sanitizer, which the Claude path already
uses. The post-pass covers plain function tools, flattened namespace
(MCP) sub-tools, tool_search and pre-converted Chat tools. Valid schemas
are unchanged.

Closes cheaper-inference#18679
@gaurav0107
gaurav0107 marked this pull request as ready for review November 3, 2026 00:32
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@diegosouzapw
diegosouzapw merged commit 4715073 into cheaper-inference:release/v3.8.53 Nov 4, 2026
3 of 37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants