-
-
Notifications
You must be signed in to change notification settings - Fork 230
Structured output & json mode #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
39a594d
feat(core): add structured output with JSON schema validation
kieranklaassen 290764a
test: add tests and VCR cassette for structured output
kieranklaassen 1a766d7
docs: add documentation for structured output feature
kieranklaassen 16ce84a
chore: update changelog for v1.3.0
kieranklaassen 9816968
docs: update internal contribution guide
kieranklaassen 2d30f10
feat(core): add system schema guidance for JSON output in chat
kieranklaassen a651e2d
refactor(core): update Gemini capabilities to support JSON mode and r…
kieranklaassen 0513cea
fix(providers): update render_payload methods to accept chat parameter
kieranklaassen 5a749d2
refactor(gemini): use supports_structured_output instead of json_mode
kieranklaassen a1e01d4
refactor(providers): update parse_completion_response method to accep…
kieranklaassen 376156e
refactor(chat): enhance with_output_schema method to include strict m…
kieranklaassen 96a9d9c
refactor(providers): update supports_structured_output method signatu…
kieranklaassen 87ddf79
Delete CHANGELOG.md
kieranklaassen 642b3c9
docs(README): add examples for accessing structured data in user profile
kieranklaassen 39c3902
docs(structured-output): enhance documentation for strict and non-str…
kieranklaassen fb39411
docs(structured-output): expand implementation details and limitation…
kieranklaassen 126bebf
refactor(acts_as): simplify extract_content method implementation
kieranklaassen 21dea58
test(acts_as): update tests for JSON and Hash content handling
kieranklaassen 44a77d5
fix(acts_as): update content assignment in message update
kieranklaassen e7ee70d
feat(structured-output): implement structured output parsing and enha…
kieranklaassen 68d39eb
refactor(gemini): introduce shared utility methods and enhance struct…
kieranklaassen 320c611
refactor(deepseek): remove unused render_payload method from chat pro…
kieranklaassen 98ff547
fix(models): update structured output support and adjust timestamps
kieranklaassen 65c2215
refactor: rename output_schema methods to response_format for clarity
kieranklaassen 15dc0e4
refactor(chat): enhance response_format handling and add JSON guidance
kieranklaassen 2d19063
refactor(chat): improve model compatibility checks and enhance JSON g…
kieranklaassen 78ba898
refactor(chat): clarify response_format documentation and error handling
kieranklaassen a20c1b7
feat(json): add support for JSON mode and enhance response format han…
kieranklaassen 370ef1d
feat(capabilities): add method to check model support for JSON mode
kieranklaassen b8cc7ec
feat(capabilities): add method to check model support for JSON mode a…
kieranklaassen 932bc11
feat(models): add support for JSON mode across multiple providers
kieranklaassen cc13503
refactor(chat): enhance with_response_format method and update docume…
kieranklaassen 6993978
fix(version): downgrade version to 1.2.0
kieranklaassen 3de661f
feat(models): add support for JSON mode check in Bedrock model specs
kieranklaassen 09d78a6
refactor(chat): update response format handling in OpenAI provider
kieranklaassen eb2f95b
refactor(readme): streamline badge layout and improve formatting
kieranklaassen 0f1e4d8
docs(structured-output): update documentation for schema-based output…
kieranklaassen 17b179d
refactor(chat): update methods to use response_format instead of chat…
kieranklaassen acfc00c
chore(.gitignore): add CLAUDE.md to ignore list
kieranklaassen f93bed3
Delete CLAUDE.md
kieranklaassen 90b57f7
refactor(structured-output): update compatibility checks and paramete…
kieranklaassen 5dfe022
docs(README): improve badge layout and update structured output descr…
kieranklaassen 4a66560
docs(structured-output): streamline Rails integration section and rem…
kieranklaassen 2eb7790
docs(rails): update structured output section and add link to structu…
kieranklaassen f778328
docs(structured-output): enhance guide with new features, error handl…
kieranklaassen 02af5b2
docs(index): update structured output description to remove 'validati…
kieranklaassen 1897092
refactor(chat): improve response format handling and compatibility ch…
kieranklaassen a9ee1c5
style
kieranklaassen 0ac9a3d
refactor(chat): add response_format parameter to complete method for …
kieranklaassen 2c72684
Merge main into json-schemas and resolve conflicts
kieranklaassen 0dc953c
refactor(chat): remove redundant comments in parse_completion_respons…
kieranklaassen 837e951
refactor(parser): simplify parse_structured_output method by removing…
kieranklaassen ad061b5
refactor(chat): integrate structured output parser and enhance parse_…
kieranklaassen 43f9c95
docs(chat): clarify comment on response format requirements for JSON …
kieranklaassen fc64702
refactor(chat): update response format key check to use :json_schema …
kieranklaassen 629c29c
refactor(chat): enhance guidance handling for response formats and im…
kieranklaassen 7153695
refactor(chat): streamline message handling by adding new message cal…
kieranklaassen fa06863
docs(rules): add comprehensive documentation for ActiveRecord integra…
kieranklaassen 3a9c515
chore(rules): remove outdated documentation for ActiveRecord integrat…
kieranklaassen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,3 +57,4 @@ Gemfile.lock | |
# .rubocop-https?--* | ||
|
||
repomix-output.* | ||
CLAUDE.md |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
--- | ||
layout: default | ||
title: Structured Output | ||
parent: Guides | ||
nav_order: 7 | ||
--- | ||
|
||
# Structured Output | ||
{: .no_toc .d-inline-block } | ||
|
||
New (v1.3.0) | ||
{: .label .label-green } | ||
|
||
Get structured, well-formatted data from language models by providing a JSON schema. Use the `with_response_format` method to ensure the AI returns data that matches your schema instead of free-form text. | ||
{: .fs-6 .fw-300 } | ||
|
||
## Table of contents | ||
{: .no_toc .text-delta } | ||
|
||
1. TOC | ||
{:toc} | ||
|
||
--- | ||
|
||
After reading this guide, you will know: | ||
|
||
* How to use JSON schemas to get structured data from language models | ||
* How to request simple JSON responses without a specific schema | ||
* How to work with models that may not officially support structured output | ||
* How to handle errors related to structured output | ||
* Best practices for creating effective JSON schemas | ||
|
||
## Getting Structured Data with Schemas | ||
|
||
The most powerful way to get structured data is by providing a JSON schema that defines the exact format you need: | ||
|
||
```ruby | ||
# Define your JSON schema | ||
schema = { | ||
type: "object", | ||
properties: { | ||
name: { type: "string" }, | ||
age: { type: "integer" }, | ||
interests: { type: "array", items: { type: "string" } } | ||
}, | ||
required: ["name", "age", "interests"] | ||
} | ||
|
||
# Request data that follows this schema | ||
response = RubyLLM.chat(model: "gpt-4o") | ||
.with_response_format(schema) | ||
.ask("Create a profile for a Ruby developer") | ||
|
||
# Access the structured data as a Hash | ||
puts response.content["name"] # => "Ruby Smith" | ||
puts response.content["age"] # => 32 | ||
puts response.content["interests"] # => ["Metaprogramming", "Rails", "Testing"] | ||
``` | ||
|
||
RubyLLM intelligently adapts based on each model's capabilities: | ||
|
||
- For models with native schema support (like GPT-4o): Uses the provider's API-level schema validation | ||
- For other models: Automatically adds schema instructions to the system message | ||
|
||
## Simple JSON Mode | ||
|
||
When you just need well-formed JSON without a specific structure: | ||
|
||
```ruby | ||
response = RubyLLM.chat(model: "gpt-4.1-nano") | ||
.with_response_format(:json) | ||
.ask("Create a profile for a Ruby developer") | ||
|
||
# The response will be valid JSON but with a format chosen by the model | ||
puts response.content.keys # => ["name", "bio", "skills", "experience", "github"] | ||
``` | ||
|
||
This simpler approach uses OpenAI's `response_format: {type: "json_object"}` parameter, guaranteeing valid JSON output without enforcing a specific schema structure. | ||
|
||
## Working with Unsupported Models | ||
|
||
To use structured output with models that don't officially support it, set `assume_supported: true`: | ||
|
||
```ruby | ||
response = RubyLLM.chat(model: "gemini-2.0-flash") | ||
.with_response_format(schema, assume_supported: true) | ||
.ask("Create a profile for a Ruby developer") | ||
``` | ||
|
||
This bypasses compatibility checks and inserts the schema as system instructions. Most modern models can follow these instructions to produce properly formatted JSON, even without native schema support. | ||
|
||
## Error Handling | ||
|
||
RubyLLM provides specialized error classes for structured output that help you handle different types of issues: | ||
|
||
### UnsupportedStructuredOutputError | ||
|
||
Raised when a model doesn't support the structured output format and `assume_supported` is false: | ||
|
||
```ruby | ||
begin | ||
# Try to use structured output with a model that doesn't support it | ||
response = RubyLLM.chat(model: "gemini-2.0-flash") | ||
.with_response_format(schema) | ||
.ask("Create a profile for a Ruby developer") | ||
rescue RubyLLM::UnsupportedStructuredOutputError => e | ||
puts "This model doesn't support structured output: #{e.message}" | ||
# Fall back to non-structured output or a different model | ||
end | ||
``` | ||
|
||
### InvalidStructuredOutput | ||
|
||
Raised if the model returns a response that can't be parsed as valid JSON: | ||
|
||
```ruby | ||
begin | ||
response = RubyLLM.chat(model: "gpt-4o") | ||
.with_response_format(schema) | ||
.ask("Create a profile for a Ruby developer") | ||
rescue RubyLLM::InvalidStructuredOutput => e | ||
puts "The model returned invalid JSON: #{e.message}" | ||
# Handle the error, perhaps by retrying or using a simpler schema | ||
end | ||
``` | ||
|
||
Note: RubyLLM checks that responses are valid JSON but doesn't verify schema conformance (required fields, data types, etc.). For full schema validation, use a library like `json-schema`. | ||
|
||
## With ActiveRecord and Rails | ||
|
||
For Rails integration details with structured output, please see the [Rails guide](rails.md#working-with-structured-output). | ||
|
||
## Best Practices for JSON Schemas | ||
|
||
When creating schemas for structured output, follow these guidelines: | ||
|
||
1. **Keep it simple**: Start with the minimum structure needed. More complex schemas can confuse the model. | ||
2. **Be specific with types**: Use appropriate JSON Schema types (`string`, `number`, `boolean`, `array`, `object`) for your data. | ||
3. **Include descriptions**: Add a `description` field to each property to help guide the model. | ||
4. **Mark required fields**: Use the `required` array to indicate which properties must be included. | ||
5. **Provide examples**: When possible, include `examples` for complex properties. | ||
6. **Test thoroughly**: Different models have varying levels of schema compliance. | ||
|
||
## Example: Complex Schema | ||
|
||
Here's an example of a more complex schema for inventory data: | ||
|
||
```ruby | ||
schema = { | ||
type: "object", | ||
properties: { | ||
products: { | ||
type: "array", | ||
items: { | ||
type: "object", | ||
properties: { | ||
name: { | ||
type: "string", | ||
description: "Name of the product" | ||
}, | ||
price: { | ||
type: "number", | ||
description: "Price in dollars" | ||
}, | ||
in_stock: { | ||
type: "boolean", | ||
description: "Whether the item is currently available" | ||
}, | ||
categories: { | ||
type: "array", | ||
items: { type: "string" }, | ||
description: "List of categories this product belongs to" | ||
} | ||
}, | ||
required: ["name", "price", "in_stock"] | ||
} | ||
}, | ||
total_products: { | ||
type: "integer", | ||
description: "Total number of products in inventory" | ||
} | ||
}, | ||
required: ["products", "total_products"] | ||
} | ||
|
||
inventory = RubyLLM.chat(model: "gpt-4o") | ||
.with_response_format(schema) | ||
.ask("Create an inventory for a Ruby gem store") | ||
``` | ||
|
||
## Limitations | ||
|
||
When working with structured output, be aware of these limitations: | ||
|
||
* Schema validation is only available at the API level for certain models (primarily OpenAI models) | ||
* RubyLLM validates that responses are valid JSON but doesn't verify schema conformance | ||
* For full schema validation, use a library like `json-schema` to verify output | ||
* Models may occasionally deviate from the schema despite instructions | ||
* Complex, deeply nested schemas may reduce compliance | ||
|
||
RubyLLM handles the complexity of supporting different model capabilities, so you can focus on your application logic rather than provider-specific implementation details. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.