Releases: crmne/schematist
Release list
v1.1.0
Schematist now covers the whole Draft 2020-12 vocabulary. 1.0 emitted Draft 2020-12 documents but couldn't express every schema the spec allows. This closes the five places where it fell short.
A schema does not have to be an object
A type with a name declares a property. Without a name it declares what the schema itself is.
class Tags < Schematist::Schema
array of: :string, unique: true # the whole schema is an array
end
class Id < Schematist::Schema
one_of do # the whole schema is a choice
string
integer
end
end
class Person < Schematist::Schema
raw({ "$ref" => "https://example.com/person.json" })
endIt works inside define too, so a definition can be a reusable string, a shared enum, an array, or a union, not just an object:
define :status do
string enum: %w[draft sent paid]
endObjects also take min_properties, max_properties, unevaluated_properties and unevaluated_items at the root, alongside additional_properties. Closes #59 and #61.
Conditionals hold any schema
if, then and else each hold any schema in Draft 2020-12. A branch was stuck with a required list plus nine validation keys, so it couldn't ask for a nested object, an array, a reference, or any composition. A branch is backed by a schema class now, so the whole DSL works inside one:
given kind: "business" do
requires :vat_id
object :tax_details do
string :vat_number
end
otherwise do
validates :vat_id, type: :string
end
endgiven still matches on property values, and takes a schema outright when the condition is something else:
given({ required: %w[tax_id] }) { requires :summary }requires and validates stay as shorthands. A dependency that only lists fields still emits dependentRequired; one that describes a schema upgrades to dependentSchemas. Closes #60.
Open ended tuples
A tuple is exactly its prefix unless you give it somewhere to put the rest. That covers both of the usual Draft 2020-12 array shapes:
tuple :event, of: :string do # prefixItems, then a typed tail
string
integer
end
tuple :pair, unevaluated_items: false do # closed after the prefix
string
string
endunevaluated_items: is on the composition builders too, alongside unevaluated_properties:. Closes #62.
Wider keywords
enum and const belong to the validation vocabulary and apply to any type, so enum works on boolean and null now. format isn't type restricted either, so integer :id, format: "int64" works. Closes #63.
const on null stays out on purpose. {"type":"null"} and {"type":"null","const":null} accept and reject the same things, so it buys you nothing, and raw covers it if you want the exact bytes.
Fixed
- Cycle detection only walked a definition's properties, so a
$refburied in a union was invisible. It walks the whole thing now, which matters more now that definitions can be unions. - An object with no block and no
of:schema raisedwrong number of arguments (given 0, expected 1..3). It says what's wrong now.
Also
ruby_llm-schema 1.0.0 is on RubyGems. It depends on Schematist and aliases RubyLLM::Schema, so the old constant keeps resolving while you migrate. It warns on load, and it's the last release of that name.
v1.0.0
RubyLLM::Schema is now Schematist. Schematist is a general purpose JSON Schema DSL that emits Draft 2020-12 schemas. Trapping that inside another gem's namespace was a disservice to anyone looking for a great JSON Schema DSL.
gem 'schematist' # was: gem 'ruby_llm-schema'
class Person < Schematist::Schema # was: RubyLLM::Schema
endBreaking
to_json_schemareturns a Draft 2020-12 document with string keys. The old{name:, description:, schema:}wrapper was OpenAI'sresponse_formatshape, and building that belongs in whatever talks to the provider.strictis gone. It's an OpenAI request flag, not a JSON Schema keyword.- Errors moved up a level:
Schematist::ValidationError, notRubyLLM::Schema::ValidationError. RubyLLM::Helpersis nowSchematist::Helpers.
A final ruby_llm-schema 1.0.0 depends on this gem and aliases the old constants, so RubyLLM::Schema keeps resolving while you migrate.
New
Schematist now speaks most of Draft 2020-12:
- Composition:
all_of,one_of,none_ofjoinany_of - Unevaluated:
unevaluated_properties:andunevaluated_items: - Object keys:
min_properties:,max_properties:,keysforpropertyNames,keys_matchingforpatternProperties - Arrays:
unique:foruniqueItems,tupleforprefixItems,containswithmin:/max: - Annotations:
title,description,default,examples,deprecated,read_only,write_only, as keywords or inside the block - Encoded content:
content_encoding:,content_media_type:,content_schema - Core keywords:
$id,$anchor,$comment,$dynamicAnchor,$dynamicRef,$vocabulary - Boolean and raw schemas:
any_schema,no_schema, andrawfor anything the DSL doesn't model conston every primitive, andgreater_than:/less_than:for the exclusive bounds
Runtime values. Any value can be a proc, resolved when the document is rendered, so one schema class gives you a different document per instance:
string :role, enum: -> { @account.roles.pluck(:name) }No runtime dependencies.
Fixed
number, integer, boolean and null didn't accept a block, so integer(:a) { title "A" } dropped the annotation silently instead of raising.
v0.4.0
Support for Dependencies and Conditionals
Adds given, dependent, and inline requires: DSL methods for expressing JSON Schema conditionals and dependencies.
requires: inline property dependencies
Maps to dependentRequired. The simplest form for "if this property is present, require those":
class PaymentSchema < RubyLLM::Schema
number :credit_card, required: false, requires: %i[billing_address cvv]
string :billing_address, required: false
string :cvv, required: false
enddependent property dependencies with validations
Use a block when you need validates, upgrades output to dependentSchemas:
dependent :credit_card do
requires :billing_address
validates :billing_address, type: :string, min_length: 1
endgiven if/then/else
Condition values are auto-coerced: strings → const, arrays → enum, regexps → pattern, hashes → raw schema.
class ShippingSchema < RubyLLM::Schema
boolean :domestic
string :state, required: false
string :country, required: false
given domestic: true do
requires :state
otherwise do
requires :country
end
end
endAll three propagate through nested schemas via of: and define/$defs.
Thanks to @marcoroth for his PR #31
Changelog
- 7ea8076 Updated badges
- 2c5629e Add support for Dependencies and Conditionals (#31)
- 1e2608c Bump version to 0.4.0
Full Changelog: v0.3.1...v0.4.0
v0.3.1
Changelog
- e27c009 Standardize project tooling and workflows
- f8c0f6b Add Java platform to lockfile
- 0b51172 Stop tracking Bundler lockfile
- 2a42247 Document RubyLLM tool parameter schemas
- 4013e15 Fix duplicate required properties
- 32bdc82 Return nil from schema property declarations
- 148bfd0 Add irb development dependency
- 7a72a07 Add Flay and Codecov coverage checks
- d210a9c Bump version to 0.3.1
- 5956c00 Create GitHub release automatically
Full Changelog: v0.3.0...v0.3.1
v0.3.0
What's Changed
- Enable support for structured outputs with Anthropic by allowing omission of strict, by @vojto in #28
- Update readme with simple example integration with RubyLLM
New Contributors
Full Changelog: v0.2.5...v0.3.0
v0.2.5
- Add support for schema naming via DSL #23
Full Changelog: v0.2.4...v0.2.5
v0.2.4
v0.2.3
What's Changed
- Add support for a oneOf type by @smcabrera in #25
New Contributors
- @smcabrera made their first contribution in #25
Full Changelog: v0.2.1...v0.2.3
v0.2.1
What's Changed
🚀 New Features
- Recursive schemas by @danielfriis in #16
- Nested schemas by @danielfriis in #18
🔧 Other improvements
- Refactor DSL by @danielfriis in #17
Full Changelog: v0.1.9...v0.2.1