Skip to content

fix(llm-sdk): structured output across providers + max_completion_tokens for o-series/gpt-5 (closes #93)#95

Merged
Sahil5963 merged 3 commits into
betafrom
feat/response-format-v2
May 7, 2026
Merged

fix(llm-sdk): structured output across providers + max_completion_tokens for o-series/gpt-5 (closes #93)#95
Sahil5963 merged 3 commits into
betafrom
feat/response-format-v2

Conversation

@Sahil5963
Copy link
Copy Markdown
Contributor

Summary

Closes #93. Three related fixes so structured-output requests (JSON schema / json mode) work end-to-end across every adapter and so newer OpenAI reasoning models stop returning 400s.

  • responseFormat forwarded everywhere. Unified ResponseFormat type (OpenAI shape) added to RequestLLMConfig, ChatRequest.config, DoGenerateParams, GenerateTextParams. Every adapter translates it to its provider's native field via per-provider sanitizers in adapters/base.ts.
  • max_completion_tokens for o-series + gpt-5. New isOpenAIReasoningModel() + buildOpenAITokenParams() helpers. The user-facing maxTokens field is unchanged; we rename to the correct provider field internally and drop temperature (these models reject it).
  • Capability gating. supportsJsonMode was set in registries but never read. Now generateText / streamText warn when responseFormat is requested on a model that doesn't support it. Flipped Anthropic / xAI / Ollama from falsetrue since their structured output is now GA.

Per-provider translation

Provider Native field
OpenAI Chat / Azure / xAI / Together / Fireworks / OpenRouter response_format
OpenAI Responses API text.format (different shape)
Anthropic Claude 3.5+ output_config.format (schema sanitized — strips numeric/length constraints, forces additionalProperties: false)
Google Gemini responseJsonSchema (schema sanitized — strips oneOf/anyOf/$ref/pattern)
Ollama 0.5+ format ("json" or schema)

Demo

Added /chat/structured in examples/fallback-demo exercising the OpenAI → Anthropic → Gemini fallback chain with a JSON schema — the exact scenario that broke in production.

Overlap with PR #92

PR #92 (cc @ankushchhabradelta4infotech-ai) addresses the same gpt-5 / o-series problem by routing those models through OpenAI's Responses API instead. Both approaches solve item 1 of #93 differently. Items 2 (responseFormat forwarding) and 3 (Anthropic output_config) are not in #92 and are only addressed here. Happy to drop the max_completion_tokens change from this PR if #92 lands first.

Test plan

Out of scope

🤖 Generated with Claude Code

…ken params (#93)

Closes #93. Three related fixes for the LLM SDK so structured output
requests (JSON schema / json_mode) work end-to-end and newer OpenAI
reasoning models stop returning 400s.

1. Forward `responseFormat` through every adapter and provider.
   Added a unified `ResponseFormat` type (OpenAI shape) on
   `RequestLLMConfig`, `ChatRequest.config`, `DoGenerateParams`, and
   `GenerateTextParams`. Each adapter translates it to its provider's
   native field via per-provider sanitizers in `adapters/base.ts`:
     - OpenAI / Azure / xAI / Together / Fireworks / OpenRouter:
       `response_format`
     - OpenAI Responses API: `text.format`
     - Anthropic Claude 3.5+: `output_config.format` (schema sanitized
       — strips numeric/length constraints Anthropic rejects, forces
       `additionalProperties: false`)
     - Google Gemini: `responseJsonSchema` (schema sanitized — strips
       `oneOf`/`anyOf`/`$ref`/`pattern`)
     - Ollama 0.5+: `format` (string `"json"` or schema object)

2. Newer OpenAI models (`o1`/`o3`/`o4`/`gpt-5`) require
   `max_completion_tokens` instead of `max_tokens` and reject
   `temperature`. Added `isOpenAIReasoningModel()` and
   `buildOpenAITokenParams()` helpers; wired into both the legacy
   adapter (`adapters/openai.ts` complete + stream) and the modern
   provider (`providers/openai/provider.ts` doGenerate + doStream).
   The user-facing `maxTokens` field is unchanged; we rename to the
   correct provider field internally.

3. Capability gating. `supportsJsonMode` was set in the model
   registries but never read; now `generateText` and `streamText`
   warn when `responseFormat` is requested on a model that doesn't
   support it. Flipped Anthropic / xAI / Ollama from `false` to
   `true` since their structured-output is now GA.

Demo: added `/chat/structured` route in `examples/fallback-demo` that
exercises the OpenAI → Anthropic → Gemini fallback chain with a JSON
schema — the exact scenario that broke in production.

Verification:
  - `pnpm --filter @yourgpt/llm-sdk typecheck` clean
  - `pnpm --filter @yourgpt/llm-sdk build` clean
  - Manual smoke via fallback-demo route pending live API keys

Note: overlaps with PR #92's gpt-5 handling (which routes reasoning
models through the Responses API). Coordinating with @ankushchhabra
on which approach lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
copilot-playground Ready Ready Preview, Comment May 7, 2026 10:16am
copilot-sdk-docs Ready Ready Preview, Comment May 7, 2026 10:16am

Request Review

Anthropic's structured-output schema subset accepts `anyOf` but not
`oneOf`. Previously we passed `oneOf` through unchanged (Anthropic
rejects) or relied on the user not using it. Now we rename
`oneOf` → `anyOf` recursively in `toAnthropicOutputConfig`.

Matches Vercel AI SDK's `sanitize-json-schema` behavior. Required
for any user-supplied schema with discriminated unions to round-trip
correctly through the Anthropic adapter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New page: docs/llm-sdk/structured-output covering ResponseFormat
  shape, per-provider native field translation, and gotchas
  (Anthropic schema sanitization, Gemini OpenAPI subset, xAI
  additionalProperties default, Vertex AI gap, reasoning model
  token semantics)
- Cross-references from generate-text and stream-text Parameters
  sections via Callout
- Updated meta.json page order

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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