ruby_llm-schema 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21c3e0ee2e5a1034eb571749cc2f27af5db9e4abaf07e2df677325f9270db6f1
4
- data.tar.gz: 57db5c67a751f4e31f1ed2d56bba8bac76ddb989fee7b49add969b1c50be844e
3
+ metadata.gz: 6bfabbb94f9550f5bae72fc6ad6046ba1fa6f9e99723c4fea969822bd76f33f9
4
+ data.tar.gz: 5b97078d6f40a912a366c4d8f26333ead3504126e9a2abd3d8d658f81d168283
5
5
  SHA512:
6
- metadata.gz: cd93b24110859806ee43a5a794353849e51273797696d84469b998c69d9e93e761ef817ede4d89377e34e227f8b6115d2e9133d58496bfcb2399f7720e4087e3
7
- data.tar.gz: 1153950f2917a95e00744cb832a2c3ce7b1bd61965314f5652dc4337333d3b9f258d52b01257e62739b997bbd6ede840c9a7adfcd339477dc996bca093bf2a0d
6
+ metadata.gz: adc5528fd26d989854bbf0d6255bda4123fa6df18d96722c38b47a7414ff8c53006212f8c987932d190913553bad3764870038589ebb20a070ebc38e034de87c
7
+ data.tar.gz: 85d522c3b21f5949ce3ed5dba3aef643836d16b5daaca44c2d6a8beeec67e6cfae65ca126e4dbd49edf434f0134aeecb715216c6f3da793d0445a38baa2442fb
data/README.md CHANGED
@@ -1,528 +1,28 @@
1
- # RubyLLM::Schema
1
+ # ruby_llm-schema (deprecated)
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/ruby_llm-schema.svg)](https://rubygems.org/gems/ruby_llm-schema)
4
- [![Gem Downloads](https://img.shields.io/gem/dt/ruby_llm-schema)](https://rubygems.org/gems/ruby_llm-schema)
5
- [![codecov](https://codecov.io/gh/crmne/ruby_llm-schema/branch/main/graph/badge.svg)](https://codecov.io/gh/crmne/ruby_llm-schema)
6
- [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
3
+ This gem is now [schematist](https://github.com/crmne/schematist). This directory holds the
4
+ final `ruby_llm-schema` release, which does nothing but depend on schematist and alias
5
+ `RubyLLM::Schema` to `Schematist::Schema`.
7
6
 
8
- A Ruby DSL for creating JSON schemas with a clean, Rails-inspired API.
7
+ It lives in the schematist repo rather than its own, so the alias cannot drift from the gem
8
+ it forwards to. The spec suite loads it and checks the aliases still resolve.
9
9
 
10
- Originally created by [Daniel Friis](https://github.com/danielfriis).
10
+ ## Building and releasing
11
11
 
12
- ## Use Cases
13
-
14
- JSON Schema is useful wherever Ruby code needs to describe structured data in a portable format.
15
-
16
- Some ideal use cases:
17
-
18
- - Defining API request and response shapes
19
- - Describing configuration files or structured payloads
20
- - Sharing validation contracts across systems
21
- - Generating structured output schemas for LLM workflows
22
- - Defining structured parameters for RubyLLM tools
23
-
24
- ### Simple Example
25
-
26
- ```ruby
27
- class PersonSchema < RubyLLM::Schema
28
- string :name, description: "Person's full name"
29
- number :age, description: "Age in years", minimum: 0, maximum: 120
30
- boolean :active, required: false
31
-
32
- object :address do
33
- string :street
34
- string :city
35
- string :country, required: false
36
- end
37
-
38
- array :tags, of: :string, description: "User tags"
39
-
40
- array :contacts do
41
- object do
42
- string :email, format: "email"
43
- string :phone, required: false
44
- end
45
- end
46
-
47
- any_of :status do
48
- string enum: ["active", "pending", "inactive"]
49
- null
50
- end
51
- end
52
-
53
- # Usage
54
- schema = PersonSchema.new
55
- puts schema.to_json
56
- ```
57
-
58
- ### RubyLLM structured output
59
-
60
- ```ruby
61
- class PersonSchema < RubyLLM::Schema
62
- string :name, description: "Person's full name"
63
- integer :age, description: "Person's age in years"
64
- string :city, required: false, description: "City where they live"
65
- end
66
-
67
- # Use it natively with RubyLLM
68
- chat = RubyLLM.chat
69
- response = chat.with_schema(PersonSchema)
70
- .ask("Generate a person named Alice who is 30 years old and lives in New York")
71
-
72
- # The response is automatically parsed from JSON
73
- puts response.content # => {"name" => "Alice", "age" => 30}
74
- puts response.content.class # => Hash
75
- ```
76
-
77
- ### RubyLLM tools
78
-
79
- RubyLLM tools can use schema classes for structured parameters. This is useful when the same argument shape is shared across tools or elsewhere in your app.
80
-
81
- ```ruby
82
- class SearchParams < RubyLLM::Schema
83
- string :query, description: "Search query"
84
- integer :limit, required: false, description: "Maximum results"
85
- end
86
-
87
- class SearchDocuments < RubyLLM::Tool
88
- desc "Searches internal documents"
89
- params SearchParams
90
-
91
- def execute(query:, limit: 10)
92
- DocumentSearch.call(query:, limit:)
93
- end
94
- end
95
- ```
96
-
97
- For tool-specific parameters, define the schema inline with `params do ... end`.
98
-
99
- ```ruby
100
- class Weather < RubyLLM::Tool
101
- desc "Gets current weather"
102
-
103
- params do
104
- string :city, description: "City name"
105
- string :units, enum: %w[celsius fahrenheit], required: false
106
- end
107
-
108
- def execute(city:, units: "celsius")
109
- WeatherAPI.current(city:, units:)
110
- end
111
- end
112
- ```
113
-
114
- ## Installation
115
-
116
- Add this line to your application's Gemfile:
117
-
118
- ```ruby
119
- gem 'ruby_llm-schema'
120
- ```
121
-
122
- And then execute:
123
-
124
- ```bash
125
- bundle install
126
- ```
127
-
128
- Or install it yourself as:
12
+ The root gemspec builds schematist. This one has to be built from this directory:
129
13
 
130
14
  ```bash
131
- gem install ruby_llm-schema
132
- ```
133
-
134
- ## Usage
135
-
136
- Three approaches for creating schemas:
137
-
138
- ### Class Inheritance
139
-
140
- ```ruby
141
- class PersonSchema < RubyLLM::Schema
142
- string :name, description: "Person's full name"
143
- number :age
144
- boolean :active, required: false
145
-
146
- object :address do
147
- string :street
148
- string :city
149
- end
150
-
151
- array :tags, of: :string
152
- end
153
-
154
- schema = PersonSchema.new
155
- puts schema.to_json
156
- ```
157
-
158
- ### Factory Method
159
-
160
- ```ruby
161
- PersonSchema = RubyLLM::Schema.create do
162
- string :name, description: "Person's full name"
163
- number :age
164
- boolean :active, required: false
165
-
166
- object :address do
167
- string :street
168
- string :city
169
- end
170
-
171
- array :tags, of: :string
172
- end
173
-
174
- schema = PersonSchema.new
175
- puts schema.to_json
176
- ```
177
-
178
- ### Global Helper
179
-
180
- ```ruby
181
- require 'ruby_llm/schema'
182
- include RubyLLM::Helpers
183
-
184
- person_schema = schema "PersonData", description: "A person object" do
185
- string :name, description: "Person's full name"
186
- number :age
187
- boolean :active, required: false
188
-
189
- object :address do
190
- string :street
191
- string :city
192
- end
193
-
194
- array :tags, of: :string
195
- end
196
-
197
- puts person_schema.to_json
198
- ```
199
-
200
- ## Schema Property Types
201
-
202
- A schema is a collection of properties, which can be of different types. Each type has its own set of properties you can set.
203
-
204
- All property types can (along with the required `name` key) be set with a `description` and a `required` flag (default is `true`).
205
-
206
- ```ruby
207
- string :name, description: "Person's full name"
208
- number :age, description: "Person's age", required: false
209
- boolean :is_active, description: "Whether the person is active"
210
- null :placeholder, description: "A placeholder property"
211
- ```
212
-
213
- ⚠️ Please consult the LLM provider documentation for any limitations or restrictions. For example, as of now, OpenAI requires all properties to be required. In that case, you can use the `any_of` method to make a property optional.
214
-
215
- ```ruby
216
- any_of :name, description: "Person's full name" do
217
- string
218
- null
219
- end
220
- ```
221
-
222
- ### Strings
223
-
224
- String types support the following properties:
225
-
226
- - `enum`: an array of allowed values (e.g. `enum: ["on", "off"]`)
227
- - `pattern`: a regex pattern (e.g. `pattern: "\\d+"`)
228
- - `format`: a format string (e.g. `format: "email"`)
229
- - `min_length`: the minimum length of the string (e.g. `min_length: 3`)
230
- - `max_length`: the maximum length of the string (e.g. `max_length: 10`)
231
-
232
- Please consult the LLM provider documentation for the available formats and patterns.
233
-
234
- ```ruby
235
- string :name, description: "Person's full name"
236
- string :email, format: "email"
237
- string :phone, pattern: "\\d+"
238
- string :status, enum: ["on", "off"]
239
- string :code, min_length: 3, max_length: 10
240
- ```
241
-
242
- ### Numbers
243
-
244
- Number types support the following properties:
245
-
246
- - `multiple_of`: a multiple of the number (e.g. `multiple_of: 0.01`)
247
- - `minimum`: the minimum value of the number (e.g. `minimum: 0`)
248
- - `maximum`: the maximum value of the number (e.g. `maximum: 100`)
249
-
250
- ```ruby
251
- number :price, minimum: 0, maximum: 100
252
- number :amount, multiple_of: 0.01
253
- ```
254
-
255
- ### Booleans
256
-
257
- ```ruby
258
- boolean :is_active
259
- ```
260
-
261
- Boolean types doesn't support any additional properties.
262
-
263
- ### Null
264
-
265
- ```ruby
266
- null :placeholder
267
- ```
268
-
269
- Null types doesn't support any additional properties.
270
-
271
- ### Arrays
272
-
273
- An array is a list of items. You can set the type of the items in the array with the `of` option or by passing a block with the `object` method.
274
-
275
- An array can have a `min_items` and `max_items` option to set the minimum and maximum number of items in the array.
276
-
277
- ```ruby
278
- array :tags, of: :string # Array of strings
279
- array :scores, of: :number # Array of numbers
280
- array :items, min_items: 1, max_items: 10 # Array with size constraints
281
-
282
- array :items do # Array of objects
283
- object do
284
- string :name
285
- number :price
286
- end
287
- end
288
- ```
289
-
290
- ### Objects
291
-
292
- Objects types expect a block with the properties of the object.
293
-
294
- ```ruby
295
- object :user do
296
- string :name
297
- number :age
298
- end
299
-
300
- object :settings, description: "User preferences" do
301
- boolean :notifications
302
- string :theme, enum: ["light", "dark"]
303
- end
304
- ```
305
-
306
- ### Union Types (anyOf)
307
-
308
- Union types are a way to specify that a property can be one of several types.
309
-
310
- ```ruby
311
- any_of :value do
312
- string
313
- number
314
- null
315
- end
316
-
317
- any_of :identifier do
318
- string description: "Username"
319
- number description: "User ID"
320
- end
321
- ```
322
-
323
- ### Schema Definitions and References
324
-
325
- You can define sub-schemas and reference them in other schemas, or reference the root schema to generate recursive schemas.
326
-
327
- ```ruby
328
- class MySchema < RubyLLM::Schema
329
- define :location do
330
- string :latitude
331
- string :longitude
332
- end
333
-
334
- # Using a reference in an array
335
- array :coordinates, of: :location
336
-
337
- # Using a reference in an object via the `reference` option
338
- object :home_location, reference: :location
339
-
340
- # Using a reference in an object via block
341
- object :user do
342
- reference :location
343
- end
344
-
345
- # Using a reference to the root schema
346
- object :ui_schema do
347
- string :element, enum: ["input", "button"]
348
- string :label
349
- object :sub_schema, reference: :root
350
- end
351
- end
352
- ```
353
-
354
- ### Nested Schemas
355
-
356
- You can embed existing schema classes directly within objects or arrays for reusable schema composition.
357
-
358
- ```ruby
359
- class PersonSchema < RubyLLM::Schema
360
- string :name
361
- integer :age
362
- end
363
-
364
- class CompanySchema < RubyLLM::Schema
365
- # Using 'of' parameter
366
- object :ceo, of: PersonSchema
367
- array :employees, of: PersonSchema
368
-
369
- # Using Schema.new in block
370
- object :founder do
371
- PersonSchema.new
372
- end
373
- end
374
-
375
- schema = CompanySchema.new
376
- schema.to_json_schema
377
- # =>
378
- # {
379
- # "name":"CompanySchema",
380
- # "description":"nil",
381
- # "schema":{
382
- # "type":"object",
383
- # "properties":{
384
- # "ceo":{
385
- # "type":"object",
386
- # "properties":{
387
- # "name":{
388
- # "type":"string"
389
- # },
390
- # "age":{
391
- # "type":"integer"
392
- # }
393
- # },
394
- # "required":[
395
- # :"name",
396
- # :"age"
397
- # ],
398
- # "additionalProperties":false
399
- # },
400
- # "employees":{
401
- # "type":"array",
402
- # "items":{
403
- # "type":"object",
404
- # "properties":{
405
- # "name":{
406
- # "type":"string"
407
- # },
408
- # "age":{
409
- # "type":"integer"
410
- # }
411
- # },
412
- # "required":[
413
- # :"name",
414
- # :"age"
415
- # ],
416
- # "additionalProperties":false
417
- # }
418
- # },
419
- # "founder":{
420
- # "type":"object",
421
- # "properties":{
422
- # "name":{
423
- # "type":"string"
424
- # },
425
- # "age":{
426
- # "type":"integer"
427
- # }
428
- # },
429
- # "required":[
430
- # :"name",
431
- # :"age"
432
- # ],
433
- # "additionalProperties":false
434
- # }
435
- # },
436
- # "required":[
437
- # :"ceo",
438
- # :"employees",
439
- # :"founder"
440
- # ],
441
- # "additionalProperties":false,
442
- # "strict":true
443
- # }
444
- # }
445
- ```
446
-
447
- ### Dependencies
448
-
449
- Use `requires:` inline or `dependent` block to express that the presence of one property requires others. Maps to [`dependentRequired`](https://json-schema.org/understanding-json-schema/reference/conditionals#dependentRequired) (Draft 2019-09) and [`dependentSchemas`](https://json-schema.org/understanding-json-schema/reference/conditionals#dependentSchemas) (Draft 2019-09). Check your provider's documentation for compatibility.
450
-
451
- ```ruby
452
- class PaymentSchema < RubyLLM::Schema
453
- string :name
454
- number :credit_card, required: false, requires: %i[billing_address cvv]
455
- string :billing_address, required: false
456
- string :cvv, required: false
457
- end
15
+ cd compat/ruby_llm-schema
16
+ gem build ruby_llm-schema.gemspec
17
+ gem push ruby_llm-schema-1.0.0.gem
458
18
  ```
459
19
 
460
- Use a `dependent` block when you also need validations — this upgrades the output to `dependentSchemas`:
20
+ schematist 1.0.0 must be on RubyGems first, since this gem depends on it.
461
21
 
462
- ```ruby
463
- dependent :credit_card do
464
- requires :billing_address
465
- validates :billing_address, type: :string, min_length: 1
466
- end
467
- ```
468
-
469
- ### Conditionals
470
-
471
- Use `given` to add [JSON Schema `if`/`then`/`else`](https://json-schema.org/understanding-json-schema/reference/conditionals#ifthenelse) (Draft 7) rules. Condition values are automatically coerced: strings → `const`, arrays → `enum`, regexps → `pattern`, hashes → raw schema.
472
-
473
- ```ruby
474
- class OrderSchema < RubyLLM::Schema
475
- string :status, enum: ["pending", "shipped", "cancelled"]
476
- string :tracking_number, required: false
477
- string :cancellation_reason, required: false
478
-
479
- given status: "shipped" do
480
- requires :tracking_number
481
- end
482
-
483
- given status: "cancelled" do
484
- requires :cancellation_reason
485
- validates :cancellation_reason, type: :string, min_length: 1
486
- end
487
- end
488
- ```
489
-
490
- `validates` supports: `type:`, `not_value:`, `min_length:`, `max_length:`, `pattern:` (string or regexp), `enum:`, `const:`, `minimum:`, `maximum:`.
491
-
492
- Use `otherwise` for an `else` branch:
493
-
494
- ```ruby
495
- given domestic: true do
496
- requires :state
497
-
498
- otherwise do
499
- requires :country
500
- end
501
- end
502
- ```
503
-
504
- Conditions propagate through nested schemas via `of:`.
505
-
506
- ## JSON Output
507
-
508
- ```ruby
509
- schema = PersonSchema.new
510
- schema.to_json_schema
511
- # => {
512
- # name: "PersonSchema",
513
- # description: nil,
514
- # schema: {
515
- # type: "object",
516
- # properties: { ... },
517
- # required: [...],
518
- # additionalProperties: false,
519
- # strict: true
520
- # }
521
- # }
522
-
523
- puts schema.to_json # Pretty JSON string
524
- ```
22
+ The version tracks the schematist release it forwards to.
525
23
 
526
- ## License
24
+ ## What the alias cannot forward
527
25
 
528
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
26
+ - `strict` was removed. It is an OpenAI `response_format` flag, not a JSON Schema keyword.
27
+ - `to_json_schema` returns a Draft 2020-12 document with string keys, not the
28
+ `{name:, description:, schema:}` provider envelope. Build that envelope where you send it.
@@ -1,99 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "schema/version"
4
- require_relative "schema/errors"
5
- require_relative "schema/helpers"
6
- require_relative "schema/validator"
7
- require_relative "schema/dsl"
8
- require_relative "schema/json_output"
9
- require "json"
3
+ require "schematist"
10
4
 
11
- module RubyLLM
12
- class Schema
13
- extend DSL
14
- include JsonOutput
15
-
16
- PRIMITIVE_TYPES = %i[string number integer boolean null].freeze
17
-
18
- class << self
19
- def create(&block)
20
- schema_class = Class.new(Schema)
21
- schema_class.class_eval(&block)
22
- schema_class
23
- end
24
-
25
- def properties
26
- @properties ||= {}
27
- end
28
-
29
- def required_properties
30
- @required_properties ||= []
31
- end
32
-
33
- def definitions
34
- @definitions ||= {}
35
- end
36
-
37
- def name(name = nil)
38
- @schema_name = name if name
39
- return @schema_name if defined?(@schema_name)
40
-
41
- super()
42
- end
43
-
44
- def description(description = nil)
45
- @description = description if description
46
- @description
47
- end
5
+ warn <<~DEPRECATION
6
+ [DEPRECATION] ruby_llm-schema is now schematist, and this gem only forwards to it.
48
7
 
49
- def additional_properties(value = nil)
50
- return @additional_properties ||= false if value.nil?
8
+ gem 'schematist' # was: gem 'ruby_llm-schema'
9
+ class Person < Schematist::Schema; end # was: RubyLLM::Schema
51
10
 
52
- @additional_properties = value
53
- end
11
+ Two things the aliases below cannot forward: `strict` is gone, since it is an OpenAI
12
+ response_format flag rather than a JSON Schema keyword, and `to_json_schema` now returns
13
+ a Draft 2020-12 document instead of the provider envelope. See
14
+ https://github.com/crmne/schematist#migrating-from-ruby_llm-schema
15
+ DEPRECATION
54
16
 
55
- def strict(*args)
56
- if args.empty?
57
- instance_variable_defined?(:@strict) ? @strict : true
58
- else
59
- @strict = args.first
60
- end
61
- end
62
-
63
- def validate!
64
- validator = Validator.new(self)
65
- validator.validate!
66
- end
67
-
68
- def valid?
69
- validator = Validator.new(self)
70
- validator.valid?
71
- end
72
- end
73
-
74
- def initialize(name = nil, description: nil)
75
- @name = name || self.class.name || "Schema"
76
- @description = description
77
- end
78
-
79
- def validate!
80
- self.class.validate!
81
- end
82
-
83
- def valid?
84
- self.class.valid?
85
- end
86
-
87
- def method_missing(method_name, ...)
88
- if respond_to_missing?(method_name)
89
- self.class.send(method_name, ...)
90
- else
91
- super
92
- end
93
- end
17
+ module RubyLLM
18
+ Schema = Schematist::Schema
19
+ Helpers = Schematist::Helpers
20
+ end
94
21
 
95
- def respond_to_missing?(method_name, include_private = false)
96
- %i[string number integer boolean array object any_of one_of null].include?(method_name) || super
97
- end
98
- end
22
+ # The error hierarchy moved from RubyLLM::Schema::X to Schematist::X. RubyLLM::Schema and
23
+ # Schematist::Schema are the same object, so these also become reachable as
24
+ # Schematist::Schema::X. Harmless — they are aliases to the very same classes — and it is
25
+ # the only way to keep `rescue RubyLLM::Schema::ValidationError` working.
26
+ %i[
27
+ Error InvalidSchemaTypeError InvalidArrayTypeError InvalidObjectTypeError
28
+ InvalidSchemaError ValidationError LimitExceededError
29
+ ].each do |name|
30
+ RubyLLM::Schema.const_set(name, Schematist.const_get(name))
99
31
  end