Skip to content

OpenAI Compatible: setting to disable strict tool schemas (strict: true rejected by strict-unaware proxies) #1827

Closed

Description

@easonLiangWorldedtech

Problem

Using the OpenAI Compatible provider (custom base URL) against an OpenAI-compatible proxy that does not support strict structured tool output fails every request with:

400 strict=true requires generated function arguments to satisfy the declared JSON Schema, which <proxy> cannot guarantee

The proxy rejects requests containing strict: true on tools because its backend cannot guarantee schema-conforming generated arguments. There is currently no client-side workaround.

Root cause

OpenAiHandler sends every request with tools: this.convertToolsForOpenAI(metadata?.tools) (src/api/providers/openai.ts, lines 173/241/377/412). BaseProvider.convertToolsForOpenAI (src/api/providers/base-provider.ts) hard-codes:

  • strict: true for all non-MCP tools
  • schema normalization: every property forced into required, additionalProperties: false

The codebase already sends strict: false elsewhere - MCP tools (base-provider.ts) and the NanoGPT provider (src/api/providers/nanogpt.ts, lines 138-140) - so this is a hard-coded per-provider choice, not a protocol requirement.

Proposal

Add a provider setting openAiStrictToolSchemas (boolean, default true = current behavior), scoped to the OpenAI Compatible provider:

  • false: tools are sent with strict: false and the declared schemas are preserved as-is (no all-required / additionalProperties coercion) - same shape as the NanoGPT path.
  • default true: no behavior change for OpenAI and strict-capable gateways.

The declared tool schemas are already strict-mode compatible (e.g. read_file declares required: ["path"] and additionalProperties: false), so skipping normalization does not loosen real required constraints - optional parameters simply stay optional.

Implementation plan (per the AGENTS.md persisted-setting checklist)

  • packages/types/src/provider-settings/openai.ts: openAiStrictToolSchemas: z.boolean().optional() + shared default constant (true)
  • webview-ui/src/components/settings/providers/OpenAICompatible.tsx: checkbox following the openAiR1FormatEnabled wiring + i18n label
  • src/api/providers/openai.ts: read this.options.openAiStrictToolSchemas ?? true, thread the flag through convertToolsForOpenAI, skip the schema conversion when off
  • Tests: provider-level (strict on/off schema shape, MCP tools always strict: false), webview save round trip (true/false/unset)
  • Out of scope (for now): openai-native / openai-codex / xai inline strict: !isMcp maps (first-party endpoints)

Repro

  1. Configure the provider "OpenAI Compatible" with a proxy that rejects strict: true (observed with NInfer serving qwen3.8-27b)
  2. Start any task - every createMessage request 400s before the first token

Activity

  1. airouter-dev commented on Sep 27, 2026

    @airouter-dev

    The important distinction here is between the OpenAI wire format and a gateway's structured-tool capability. I would make this a per-connection or per-model capability, rather than a global provider switch.

    A focused implementation/test contract could be:

    • Keep the current strict normalization as the default for OpenAI and strict-capable connections.
    • For an OpenAI-compatible connection with openAiStrictToolSchemas=false, preserve the declared required and additionalProperties fields and send strict: false (or omit the field if that is the gateway's accepted form).
    • Decide this from explicit connection/model configuration, not from the provider name. Two endpoints using the same OpenAI-compatible protocol can have different tool-schema behavior.
    • Cover both a root URL and a URL already ending in /v1; normalize once so the request path cannot become /v1/v1/chat/completions.
    • Add fixtures for strict success, a 400 caused by strict-schema rejection, optional-property preservation, MCP tools remaining non-strict, and a normal non-tool request. Redact keys and request content from diagnostics.

    For an optional external check of the ordinary OpenAI-compatible Chat Completions path, I operate AI Router with the API root https://api.ai-router.dev/v1. This should be treated only as an independent transport/control endpoint; the strict-tool behavior must be tested against the specific account-visible model and must not be assumed from the endpoint name.

    Disclosure: I represent AI Router. I am not requesting a provider-specific branch or default, and I have not verified that AI Router supports this issue's strict: true behavior.

  2. easonLiangWorldedtech commented on Sep 27, 2026

    @easonLiangWorldedtech
    ContributorAuthor

    Thanks for the detailed review — most of the contract matches what the fix for this issue implements:

    • Per-connection scope: openAiStrictToolSchemas lives on the OpenAI Compatible connection's own settings (the same configuration that holds the base URL and API key). It is an explicit per-connection value, never inferred from the provider name or the endpoint, and it does not affect any other provider (openai-native, openai-codex, xai, ...). Two endpoints speaking the same OpenAI-compatible protocol can behave differently by configuring the flag independently per connection.
    • Behavior when disabled: with openAiStrictToolSchemas: false, each tool is sent with strict: false and the declared JSON schema passes through untouched — declared required and additionalProperties preserved, no normalization applied. This mirrors the existing precedent for MCP tools. An explicit strict: false is sent rather than omitting the field, since the Chat Completions API accepts both and the explicit form is easier to reason about.
    • Base URL / /v1: the handler passes the configured base URL straight to the OpenAI SDK, which owns request-path joining; the strict flag is orthogonal to the request path, so no /v1/v1/chat/completions doubling is introduced. Base-URL handling already has existing coverage (e.g. the Azure OpenAI base-URL normalization tests).
    • Test contract: the implementation adds fixtures for the strict default (normalized schema on the wire), strict disabled with the declared schema preserved (including optional properties), MCP tools remaining non-strict, a gateway 400 caused by strict-schema rejection propagating with its status preserved, and a normal non-tool request that carries no tools. Error diagnostics are unchanged by this work, so nothing new leaks keys or request content.

    Disclosure noted — thank you for flagging it. No AI-Router-specific branch, default, or endpoint handling goes into this change. https://api.ai-router.dev/v1 can be used to manually verify the ordinary Chat Completions transport path; since strict-tool behavior is account/model-visible, any such check is transport verification only and is not assumed from the endpoint name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions