Skip to content

Add structured output support for Anthropic provider#528

Closed
vojto wants to merge 8 commits intocrmne:mainfrom
vojto:feature/anthropic-structured-outputs
Closed

Add structured output support for Anthropic provider#528
vojto wants to merge 8 commits intocrmne:mainfrom
vojto:feature/anthropic-structured-outputs

Conversation

@vojto
Copy link
Copy Markdown

@vojto vojto commented Dec 5, 2025

Hey, I tried to support structured output for Anthropic.

The only problem is that RubyLLM::Schema currently adds strict: true to each schema it creates. Here's a PR in that repo to fix that: danielfriis/ruby_llm-schema#28

Until that is merged, the workaround is to create this subclass of RubyLLM::Schema:

class AnthropicSchema < RubyLLM::Schema
  def to_json_schema
    result = super
    result[:schema].delete(:strict)
    result[:schema].delete("strict")
    result
  end
end

AI generated:

Summary

  • Implements structured outputs using Anthropic's structured-outputs-2025-11-13 beta API
  • Adds structured output capability detection for Claude 4+ models
  • Includes comprehensive test coverage for the new functionality

Changes

  • Anthropic Provider: Added complete method override to inject the structured-outputs beta header when schema is provided
  • Capabilities: Added supports_structured_output? method to detect Claude 4+ models that support structured outputs
  • Chat: Implemented output_format parameter with json_schema type in payload rendering
  • Tests: Added specs for beta header handling and output_format payload generation
  • Refactoring: Extracted claude3_or_newer? and claude4_or_newer? helper methods for cleaner version detection

Test plan

  • Added unit tests for beta header handling
  • Added unit tests for output_format payload generation
  • Verified structured output capability detection for Claude 4+ models
  • Manual testing with actual Anthropic API (recommended)

🤖 Generated with Claude Code

@vojto vojto marked this pull request as draft December 5, 2025 08:00
@vojto vojto marked this pull request as ready for review December 5, 2025 18:10
@tpaulshippy
Copy link
Copy Markdown
Contributor

Do you plan to add or modify specs that hit the real API and produce new/updated VCR cassettes?

@vojto
Copy link
Copy Markdown
Author

vojto commented Dec 8, 2025

@tpaulshippy that should be done now.

I edited models.json manually, because I don't have all the API keys to run rake models:update. I'm assuming someone else will update it after merging the PR?

I modified models_to_test.rb a little - made a separate array for models that we wanna test for schema. I added haiku-4.5 and sonnet-4.5 to this list, and kept gpt-4.1-nano and gemini-2.5-flash that was already there.

@vojto
Copy link
Copy Markdown
Author

vojto commented Dec 18, 2025

ping @crmne

@vojto
Copy link
Copy Markdown
Author

vojto commented Jan 8, 2026

@crmne supporting PR has been merged danielfriis/ruby_llm-schema#28

vojto and others added 6 commits January 12, 2026 09:32
Implements structured outputs using Anthropic's structured-outputs-2025-11-13 beta API.

Changes:
- Add structured output capability detection for Claude 4+ models
- Implement output_format parameter with json_schema type in chat payload
- Add anthropic-beta header handling to append structured-outputs beta version
- Add comprehensive specs for structured output functionality
- Refactor model version detection helpers (claude3_or_newer, claude4_or_newer)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Move schema validation logic into dedicated class to reduce complexity
in the Chat module and improve separation of concerns.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The spec file legitimately tests two separate classes (Provider and Chat module) that are closely related but have distinct responsibilities. Disabling this cop for this file is the appropriate solution.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@vojto vojto force-pushed the feature/anthropic-structured-outputs branch from ec98fd6 to 1e2215a Compare January 12, 2026 08:34
vojto and others added 2 commits January 12, 2026 09:38
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@smcabrera
Copy link
Copy Markdown

My team is eagerly awaiting this one! Thanks @vojto for the work here and thanks @crmne for reviewing! 🙏

@blaz
Copy link
Copy Markdown

blaz commented Jan 22, 2026

Waiting for this also!

@llenodo
Copy link
Copy Markdown
Contributor

llenodo commented Jan 22, 2026

Same!

@thomaswitt
Copy link
Copy Markdown

Same.

@vojto
Copy link
Copy Markdown
Author

vojto commented Jan 27, 2026

The main branch has changed quite a bit, so if anyone wants to get it up to date, please feel free to do so.

crmne added a commit that referenced this pull request Mar 1, 2026
## What this does

Adds native structured output support for Anthropic Claude 4.5+ models
using the GA output_config API. When with_schema is called on a chat
using a supported Anthropic model, the schema is sent via
output_config.format with type json_schema, and the response is
automatically parsed as JSON.

Because I can't contribute to PR #528, I opened this one.

## Type of change

- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [x] Documentation
- [ ] Performance improvement

## Scope check

- [x] I read the [Contributing
Guide](https://github.com/crmne/ruby_llm/blob/main/CONTRIBUTING.md)
- [x] This aligns with RubyLLM's focus on **LLM communication**
- [x] This isn't application-specific logic that belongs in user code
- [x] This benefits most users, not just my specific use case

## Required for new features

- [ ] I opened an issue **before** writing code and received maintainer
approval
- [x] Linked issue: #501 

**PRs for new features or enhancements without a prior approved issue
will be closed.**

## Quality check

- [x] I ran `overcommit --install` and all hooks pass
- [x] I tested my changes thoroughly
- [x] For provider changes: Re-recorded VCR cassettes with `bundle exec
rake vcr:record[provider_name]`
  - [x] All tests pass: `bundle exec rspec`
- [x] I updated documentation if needed
- [x] I didn't modify auto-generated files manually (`models.json`,
`aliases.json`)

## AI-generated code

- [x] I used AI tools to help write this code
- [x] I have reviewed and understand all generated code (required if
above is checked)

## API changes

- [ ] Breaking change
- [x] New public methods/classes
- [ ] Changed method signatures
- [ ] No API changes

---------

Co-authored-by: Carmine Paolino <carmine@paolino.me>
@crmne
Copy link
Copy Markdown
Owner

crmne commented Mar 1, 2026

Merged same functionality in #608

@crmne crmne closed this Mar 1, 2026
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.

7 participants