Skip to content

fix: use Anthropic's native output_format for structured output#26

Closed
03-CiprianoG wants to merge 3 commits intoHelicone:mainfrom
03-CiprianoG:fix/anthropic-structured-output
Closed

fix: use Anthropic's native output_format for structured output#26
03-CiprianoG wants to merge 3 commits intoHelicone:mainfrom
03-CiprianoG:fix/anthropic-structured-output

Conversation

@03-CiprianoG
Copy link
Copy Markdown

Fixes #25

Problem

When using Output.object({ schema }) or generateObject() with Anthropic models via the Helicone gateway, the model returns prose instead of JSON because response_format (OpenAI's parameter) is sent but Anthropic ignores it.

See #25 for full details, reproduction steps, and request logs.

Solution

Detect Anthropic models (this.modelId.startsWith('anthropic/')) in buildRequestBody() and use Anthropic's native output_format parameter instead of OpenAI's response_format.

Before (broken for Anthropic)

{
  "response_format": {
    "type": "json_schema",
    "json_schema": { "schema": {...}, "strict": true, "name": "response" }
  }
}

Anthropic ignores response_format → model returns prose → NoObjectGeneratedError.

After (works for all providers)

Anthropic models → uses output_format (native Anthropic API):

{
  "output_format": {
    "type": "json_schema",
    "json_schema": { "schema": {...}, "name": "response" }
  }
}

All other models → continues using response_format (OpenAI-compatible), unchanged.

Changes

  • src/helicone-language-model.ts: Updated buildRequestBody() to branch on this.modelId.startsWith('anthropic/') when handling responseFormat. Anthropic models get output_format, others get response_format as before.

Notes

  • Anthropic's output_format does not support strict: true (OpenAI-specific), so it's omitted for Anthropic models.
  • The Helicone gateway passes unknown body fields through to the underlying provider, so output_format reaches Anthropic's API correctly.
  • This approach is consistent with how @ai-sdk/anthropic handles structured output natively (via the outputFormat structured output mode).

When using `Output.object({ schema })` or `generateObject()` with
Anthropic models via the Helicone gateway, the AI SDK passes
`responseFormat: { type: "json", schema }` to `doGenerate()`.

Previously, this was always converted to OpenAI's
`response_format: { type: "json_schema", json_schema: { schema } }`
format. The Helicone gateway passes this through to Anthropic, but
Anthropic ignores the `response_format` field entirely since it's
not part of their API spec. The model then returns prose/markdown
instead of JSON, causing `NoObjectGeneratedError`.

This fix detects Anthropic models (modelId starting with
`anthropic/`) and uses Anthropic's native `output_format` parameter
instead:

```json
{
  "output_format": {
    "type": "json_schema",
    "json_schema": {
      "schema": { ... },
      "name": "response"
    }
  }
}
```

Non-Anthropic models continue using `response_format` as before.
The Helicone gateway accepts model IDs in two formats:
- `anthropic/claude-sonnet-4-6` (provider/model)
- `claude-4.6-sonnet/anthropic` (model/provider)

The Anthropic detection now checks both startsWith and endsWith
to handle either format.
Use modelId.includes('anthropic') to handle any model ID format
(anthropic/model, model/anthropic, etc).
@03-CiprianoG
Copy link
Copy Markdown
Author

Closing this PR — the root cause is in the AI Gateway, not the AI SDK provider.

The gateway's toAnthropic() translation in packages/llm-mapper/transform/providers/openai/request/toAnthropic.ts ignores response_format entirely when translating OpenAI requests to Anthropic format. It should map response_format: { type: "json_schema", json_schema: { schema } } to Anthropic's native output_config: { format: { type: "json_schema", schema } }.

Filed a proper issue and PR on the gateway repo instead:

  • Issue: (will link below)
  • PR: (will link below)

The output_format approach in this PR also doesn't work because the gateway rejects unknown body fields (Unrecognized key: "output_format").

@03-CiprianoG
Copy link
Copy Markdown
Author

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.

Anthropic structured output (Output.object / generateObject) broken — response_format ignored

1 participant