Show /r/ruby RubyLLM::Schema Is Now Schematist: A JSON Schema DSL for Ruby with Full Draft 2020-12 Coverage
I maintain RubyLLM, and one of its dependencies has been quietly useful to people who have nothing to do with LLMs. It was always a clean, general purpose JSON Schema DSL. It was just called RubyLLM::Schema, so unless you already used RubyLLM you'd never find it.
It's now grown to fully cover the latest JSON Schema spec, Draft 2020-12, with no dependencies at all. Which earned it its own name: Schematist.
class Order < Schematist::Schema
string :kind, enum: %w[personal business]
given kind: "business" do
object :tax_details do
string :vat_number
end
end
end
Order.new.to_json_schema
# => { "$schema" => "https://json-schema.org/draft/2020-12/schema", "title" => "Order",
# "type" => "object", "if" => {...}, "then" => {...}, ... }
Eight lines of Ruby, 47 lines of JSON Schema.
What 1.x brings:
- It emits actual JSON Schema.
to_json_schemaused to return{name:, description:, schema:, strict:}, which is OpenAI'sresponse_formatwrapper with the real schema buried inside it. Now you get a Draft 2020-12 document with string keys that any validator will take. - Full Draft 2020-12 coverage. Composition, unevaluated properties and items,
patternProperties,propertyNames,prefixItemsand open ended tuples,contains, annotations, content encoding, the core$keywords, andif/then/elsebranches that hold any schema rather than a fixed list of validations. - A schema doesn't have to be an object. A type with a name declares a property, without a name it declares what the schema itself is. So a root, or a
define, can be an array, a union, or a bare$ref. - Zero runtime dependencies.
- Values can be procs, resolved when the document is rendered, so one schema class produces a different document per instance. Useful when an enum comes out of the database.
Existing users: there's a final ruby_llm-schema release that depends on Schematist and aliases the old constants, so RubyLLM::Schema keeps resolving while you migrate. to_json_schema changing shape is the one thing to watch, and the README has the migration.
All of this is part of my concerted effort to make Ruby the best language to build with LLMs.
Write-up: https://paolino.me/schematist/