Skip to content

fix(openai-compatible): make strict mode opt-in for structured outputs - #446

Merged
cpsievert merged 3 commits into
mainfrom
fix/openai-compatible-strict-opt-in
Oct 12, 2026
Merged

cpsievert merged 3 commits into
mainfrom
fix/openai-compatible-strict-opt-in

Conversation

@cpsievert

Copy link
Copy Markdown
Collaborator

Ports tidyverse/ellmer#1135.

Problem

OpenAICompletionsProvider — the base for every OpenAI-compatible provider — unconditionally sent strict: true in structured-output response_format bodies. Strict mode (constrained decoding) is an OpenAI implementation detail, not part of JSON Schema, and it's only safe when the backend actually enforces it. Backends that do prompt-based structured output read the schema the standard way, and some reject unknown flags outright.

Note chatlas never had the other two leaks ellmer's PR fixed: tool definitions here already omit strict unless the user opts in per-tool (Tool(strict=True)), and schemas are standard pydantic JSON Schema (no all-required-plus-nullable convention). So this port is scoped to the response_format flag.

Changes

OpenAICompletionsProvider gains a strict flag (default False) that gates strict: true in the response_format body:

  • Opt in (enforce strict mode): ChatGroq(), ChatAzureOpenAICompletions(), and ChatPosit() models whose id starts with openai/ (kept in sync across set_model()).
  • Auto-detect: ChatOpenAICompletions() — chatlas's dual-duty entry point for both OpenAI-proper and third-party backends — enables strict mode when base_url points at api.openai.com and disables it otherwise. No behavior change for existing OpenAI users; third-party backends (vLLM, Ollama, LiteLLM, ...) get standard JSON Schema by default. A new strict argument overrides the heuristic.
  • New strict argument (default False): ChatOpenRouter(), ChatPortkey(), ChatHuggingFace(), for endpoints where the right choice can't be determined automatically.
  • Inherit the new default: ChatDatabricks(), ChatMistral(), ChatDeepSeek(), ChatPerplexity(), ChatCloudflare(), ChatOllama(), ChatLMStudio(), ChatGithub() — these no longer send strict: true.

ChatGithub() is the one judgment call: ellmer's PR doesn't mention chat_github(), so it stays opted out here.

Cassettes

Recorded request bodies in the five affected cassettes (openrouter, cloudflare, databricks, mistral, huggingface data-extraction tests) were hand-updated to drop the flag; recorded responses are untouched.

Tests

New credential-free tests cover: the response_format gating on both branches, ChatOpenAICompletions() base-url auto-detection and explicit override, per-provider defaults (groq/openrouter/portkey/huggingface/azure), and the ChatPosit() openai/ heuristic including set_model() switches.

OpenAICompletionsProvider unconditionally sent strict: true in
structured-output response_format bodies. Strict mode (constrained
decoding) is an OpenAI implementation detail, not part of JSON Schema,
and is only safe when the backend actually enforces it -- several
OpenAI-compatible backends reject the flag or mishandle the schema.

Strict mode is now gated on a provider flag (default off):

- ChatGroq() and ChatAzureOpenAICompletions() opt in (both enforce it).
- ChatPosit() opts in for models whose id starts with 'openai/'.
- ChatOpenAICompletions() auto-detects: strict when base_url points at
  api.openai.com, standard JSON Schema otherwise; its new strict
  argument overrides the heuristic.
- ChatOpenRouter(), ChatPortkey(), and ChatHuggingFace() gain a strict
  argument (default False) for endpoints where the right choice can't
  be determined automatically.

Recorded request bodies in the five affected provider cassettes were
hand-updated to drop the flag; recorded responses are untouched.

Ports tidyverse/ellmer#1135.
@cpsievert
cpsievert merged commit 03f22ae into main Oct 12, 2026
8 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

Development

Successfully merging this pull request may close these issues.

1 participant