ruby_llm-schema 0.1.6 → 0.1.8

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: d6c0f93a31976be978ea63b955ff89a9d9fa3e2c927457396ecda8a104d5a8df
4
- data.tar.gz: fc2113a8e86d73ac532d6ab03d95ea234678196fbe400a8e2656a9da6f6930ba
3
+ metadata.gz: 2a5add3bd7f57004c32b88a3fb676370622b0d94931b3e8ad46b2aa021da96f7
4
+ data.tar.gz: 1d371d0595ba66e8e0a31b42f524be07569cf837eb2a044a560cad8f412ea17d
5
5
  SHA512:
6
- metadata.gz: e49c81df9deed1904351e88d1a887fa58ed321ee9a96d03ca1fc3bebb5f981bfb263634e3005286f1c774322ec12943ffc7cb3f69ac42b57839290e6e6407f6c
7
- data.tar.gz: 3bb762ef34a9cc9b36c9bf4ad8ee74d659dc3dbdf26169b3654e896fab2d33f8e82e9d5976d228b6766c139e631af1f0aea547e988138d95de5db4a3d889ab71
6
+ metadata.gz: e558d54dbe37fde9df32d7c0aa3116d95687e8f1b76e9d6260f10025242671ce67bdb5f44f6020a757dc38ec693e764c11e20388420c6fd65f7ccc7efa391394
7
+ data.tar.gz: d6ba597b33675f573bb3b9d728f128a2bfc431a47e6c87ef2ed37d8c5866a8475eae5f20eb1b670edf31dfd3bdf7d7e79a6329ef68550538930eb4a8aa13030e
data/README.md CHANGED
@@ -1,7 +1,22 @@
1
1
  # RubyLLM::Schema
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/ruby_llm-schema.svg)](https://rubygems.org/gems/ruby_llm-schema)
4
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/danielfriis/ruby_llm-schema/blob/main/LICENSE.txt)
5
+ [![CI](https://github.com/danielfriis/ruby_llm-schema/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/danielfriis/ruby_llm-schema/actions/workflows/ci.yml)
6
+
3
7
  A Ruby DSL for creating JSON schemas with a clean, Rails-inspired API. Perfect for defining structured data schemas for LLM function calling or structured outputs.
4
8
 
9
+ ## Use Cases
10
+
11
+ Structured output is a powerful tool for LLMs to generate consistent and predictable responses.
12
+
13
+ Some ideal use cases:
14
+
15
+ - Extracting *metadata, topics, and summary* from articles or blog posts
16
+ - Organizing unstructured feedback or reviews with *sentiment and summary*
17
+ - Defining structured *actions* from user messages or emails
18
+ - Extracting *entities and relationships* from documents
19
+
5
20
  ### Simple Example
6
21
 
7
22
  ```ruby
@@ -122,29 +137,83 @@ end
122
137
  puts person_schema.to_json
123
138
  ```
124
139
 
125
- ## Field Types
140
+ ## Schema Property Types
141
+
142
+ A schema is a collection of properties, which can be of different types. Each type has its own set of properties you can set.
143
+
144
+ All property types can (along with the required `name` key) be set with a `description` and a `required` flag (default is `true`).
145
+
146
+ ```ruby
147
+ string :name, description: "Person's full name"
148
+ number :age, description: "Person's age", required: false
149
+ boolean :is_active, description: "Whether the person is active"
150
+ null :placeholder, description: "A placeholder property"
151
+ ```
152
+
153
+ ⚠️ 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.
154
+
155
+ ```ruby
156
+ any_of :name, description: "Person's full name" do
157
+ string
158
+ null
159
+ end
160
+ ```
161
+
162
+ ### Strings
163
+
164
+ String types support the following properties:
165
+
166
+ - `enum`: an array of allowed values (e.g. `enum: ["on", "off"]`)
167
+ - `pattern`: a regex pattern (e.g. `pattern: "\\d+"`)
168
+ - `format`: a format string (e.g. `format: "email"`)
169
+ - `min_length`: the minimum length of the string (e.g. `min_length: 3`)
170
+ - `max_length`: the maximum length of the string (e.g. `max_length: 10`)
171
+
172
+ Please consult the LLM provider documentation for the available formats and patterns.
173
+
174
+ ```ruby
175
+ string :name, description: "Person's full name"
176
+ string :email, format: "email"
177
+ string :phone, pattern: "\\d+"
178
+ string :status, enum: ["on", "off"]
179
+ string :code, min_length: 3, max_length: 10
180
+ ```
181
+
182
+ ### Numbers
183
+
184
+ Number types support the following properties:
126
185
 
127
- ### Primitive Types
186
+ - `multiple_of`: a multiple of the number (e.g. `multiple_of: 0.01`)
187
+ - `minimum`: the minimum value of the number (e.g. `minimum: 0`)
188
+ - `maximum`: the maximum value of the number (e.g. `maximum: 100`)
128
189
 
129
190
  ```ruby
130
- string :name # Required string
131
- string :title, required: false # Optional string
132
- string :status, enum: ["on", "off"] # String with enum values
133
- string :email, format: "email" # String with format validation
134
- string :code, min_length: 3, max_length: 10 # String with length constraints
135
- string :pattern_field, pattern: "\\d+" # String with regex pattern
191
+ number :price, minimum: 0, maximum: 100
192
+ number :amount, multiple_of: 0.01
193
+ ```
136
194
 
137
- number :count # Required number
138
- number :price, minimum: 0, maximum: 100 # Number with range constraints
139
- number :amount, multiple_of: 0.01 # Number with precision constraints
195
+ ### Booleans
140
196
 
141
- integer :id # Required integer
142
- boolean :active # Required boolean
143
- null :placeholder # Null type
197
+ ```ruby
198
+ boolean :is_active
144
199
  ```
145
200
 
201
+ Boolean types doesn't support any additional properties.
202
+
203
+ ### Null
204
+
205
+ ```ruby
206
+ null :placeholder
207
+ ```
208
+
209
+ Null types doesn't support any additional properties.
210
+
146
211
  ### Arrays
147
212
 
213
+ 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.
214
+
215
+ An array can have a `min_items` and `max_items` option to set the minimum and maximum number of items in the array.
216
+
148
217
  ```ruby
149
218
  array :tags, of: :string # Array of strings
150
219
  array :scores, of: :number # Array of numbers
@@ -160,6 +229,8 @@ end
160
229
 
161
230
  ### Objects
162
231
 
232
+ Objects types expect a block with the properties of the object.
233
+
163
234
  ```ruby
164
235
  object :user do
165
236
  string :name
@@ -174,6 +245,8 @@ end
174
245
 
175
246
  ### Union Types (anyOf)
176
247
 
248
+ Union types are a way to specify that a property can be one of several types.
249
+
177
250
  ```ruby
178
251
  any_of :value do
179
252
  string
@@ -189,6 +262,8 @@ end
189
262
 
190
263
  ### Schema Definitions and References
191
264
 
265
+ You can define sub-schemas and reference them in other schemas.
266
+
192
267
  ```ruby
193
268
  class MySchema < RubyLLM::Schema
194
269
  define :location do
@@ -196,9 +271,14 @@ class MySchema < RubyLLM::Schema
196
271
  string :longitude
197
272
  end
198
273
 
274
+ # Using a reference in an array
199
275
  array :coordinates, of: :location
200
-
201
- object :home_location do
276
+
277
+ # Using a reference in an object via the `reference` option
278
+ object :home_location, reference: :location
279
+
280
+ # Using a reference in an object via block
281
+ object :user do
202
282
  reference :location
203
283
  end
204
284
  end
data/Rakefile CHANGED
@@ -3,3 +3,10 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
5
  require "standard/rake"
6
+
7
+ # Load custom tasks
8
+ Dir.glob("lib/tasks/**/*.rake").each { |r| load r }
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task default: %i[standard spec]
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ class Schema
5
+ module DSL
6
+ # Primitive type methods
7
+ def string(name = nil, enum: nil, description: nil, required: true, min_length: nil, max_length: nil, pattern: nil, format: nil)
8
+ options = {
9
+ enum: enum,
10
+ description: description,
11
+ minLength: min_length,
12
+ maxLength: max_length,
13
+ pattern: pattern,
14
+ format: format
15
+ }.compact
16
+
17
+ add_property(name, build_property_schema(:string, **options), required: required)
18
+ end
19
+
20
+ def number(name = nil, description: nil, required: true, minimum: nil, maximum: nil, multiple_of: nil)
21
+ options = {
22
+ description: description,
23
+ minimum: minimum,
24
+ maximum: maximum,
25
+ multipleOf: multiple_of
26
+ }.compact
27
+
28
+ add_property(name, build_property_schema(:number, **options), required: required)
29
+ end
30
+
31
+ def integer(name = nil, description: nil, required: true)
32
+ add_property(name, build_property_schema(:integer, description: description), required: required)
33
+ end
34
+
35
+ def boolean(name = nil, description: nil, required: true)
36
+ add_property(name, build_property_schema(:boolean, description: description), required: required)
37
+ end
38
+
39
+ def null(name = nil, description: nil, required: true)
40
+ add_property(name, build_property_schema(:null, description: description), required: required)
41
+ end
42
+
43
+ # Complex type methods
44
+ def object(name = nil, reference: nil, description: nil, required: true, &block)
45
+ add_property(name, build_property_schema(:object, description: description, reference: reference, &block), required: required)
46
+ end
47
+
48
+ def array(name, of: nil, description: nil, required: true, min_items: nil, max_items: nil, &block)
49
+ items = determine_array_items(of, &block)
50
+
51
+ add_property(name, {
52
+ type: "array",
53
+ description: description,
54
+ items: items,
55
+ minItems: min_items,
56
+ maxItems: max_items
57
+ }.compact, required: required)
58
+ end
59
+
60
+ def any_of(name = nil, required: true, description: nil, &block)
61
+ schemas = collect_property_schemas_from_block(&block)
62
+
63
+ add_property(name, {
64
+ description: description,
65
+ anyOf: schemas
66
+ }.compact, required: required)
67
+ end
68
+
69
+ def optional(name, description: nil, &block)
70
+ any_of(name, description: description) do
71
+ instance_eval(&block)
72
+ null
73
+ end
74
+ end
75
+
76
+ # Schema definition and reference methods
77
+ def define(name, &)
78
+ sub_schema = Class.new(Schema)
79
+ sub_schema.class_eval(&)
80
+
81
+ definitions[name] = {
82
+ type: "object",
83
+ properties: sub_schema.properties,
84
+ required: sub_schema.required_properties
85
+ }
86
+ end
87
+
88
+ def reference(schema_name)
89
+ {"$ref" => "#/$defs/#{schema_name}"}
90
+ end
91
+
92
+ # Schema building methods
93
+ def build_property_schema(type, **options, &)
94
+ case type
95
+ when :string
96
+ {
97
+ type: "string",
98
+ enum: options[:enum],
99
+ description: options[:description],
100
+ minLength: options[:minLength],
101
+ maxLength: options[:maxLength],
102
+ pattern: options[:pattern],
103
+ format: options[:format]
104
+ }.compact
105
+ when :number
106
+ {
107
+ type: "number",
108
+ description: options[:description],
109
+ minimum: options[:minimum],
110
+ maximum: options[:maximum],
111
+ multipleOf: options[:multipleOf]
112
+ }.compact
113
+ when :integer
114
+ {
115
+ type: "integer",
116
+ description: options[:description],
117
+ minimum: options[:minimum],
118
+ maximum: options[:maximum],
119
+ multipleOf: options[:multipleOf]
120
+ }.compact
121
+ when :boolean
122
+ {type: "boolean", description: options[:description]}.compact
123
+ when :null
124
+ {type: "null", description: options[:description]}.compact
125
+ when :object
126
+ # If the reference option is provided, return the reference
127
+ return reference(options[:reference]) if options[:reference]
128
+
129
+ sub_schema = Class.new(Schema)
130
+
131
+ # Evaluate the block and capture the result
132
+ result = sub_schema.class_eval(&)
133
+
134
+ # If the block returned a reference and no properties were added, use the reference
135
+ if result.is_a?(Hash) && result["$ref"] && sub_schema.properties.empty?
136
+ result.merge(options[:description] ? {description: options[:description]} : {})
137
+ else
138
+ {
139
+ type: "object",
140
+ properties: sub_schema.properties,
141
+ required: sub_schema.required_properties,
142
+ additionalProperties: additional_properties,
143
+ description: options[:description]
144
+ }.compact
145
+ end
146
+ when :any_of
147
+ schemas = collect_property_schemas_from_block(&)
148
+ {
149
+ anyOf: schemas
150
+ }.compact
151
+ else
152
+ raise InvalidSchemaTypeError, type
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def add_property(name, definition, required:)
159
+ properties[name.to_sym] = definition
160
+ required_properties << name.to_sym if required
161
+ end
162
+
163
+ def determine_array_items(of, &)
164
+ return collect_property_schemas_from_block(&).first if block_given?
165
+ return build_property_schema(of) if primitive_type?(of)
166
+ return reference(of) if of.is_a?(Symbol)
167
+
168
+ raise InvalidArrayTypeError, of
169
+ end
170
+
171
+ def collect_property_schemas_from_block(&block)
172
+ schemas = []
173
+ schema_builder = self # Capture the current context that has build_property_schema
174
+
175
+ context = Object.new
176
+ context.define_singleton_method(:string) { |name = nil, **options| schemas << schema_builder.build_property_schema(:string, **options) }
177
+ context.define_singleton_method(:number) { |name = nil, **options| schemas << schema_builder.build_property_schema(:number, **options) }
178
+ context.define_singleton_method(:integer) { |name = nil, **options| schemas << schema_builder.build_property_schema(:integer, **options) }
179
+ context.define_singleton_method(:boolean) { |name = nil, **options| schemas << schema_builder.build_property_schema(:boolean, **options) }
180
+ context.define_singleton_method(:null) { |name = nil, **options| schemas << schema_builder.build_property_schema(:null, **options) }
181
+ context.define_singleton_method(:object) { |name = nil, **options, &blk| schemas << schema_builder.build_property_schema(:object, **options, &blk) }
182
+ context.define_singleton_method(:any_of) { |name = nil, **options, &blk| schemas << schema_builder.build_property_schema(:any_of, **options, &blk) }
183
+
184
+ context.instance_eval(&block)
185
+ schemas
186
+ end
187
+
188
+ def primitive_type?(type)
189
+ type.is_a?(Symbol) && PRIMITIVE_TYPES.include?(type)
190
+ end
191
+ end
192
+ end
193
+ end
@@ -7,4 +7,4 @@ module RubyLLM
7
7
  schema_class.new(name, description: description)
8
8
  end
9
9
  end
10
- end
10
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ class Schema
5
+ module JsonOutput
6
+ def to_json_schema
7
+ validate! # Validate schema before generating JSON
8
+
9
+ {
10
+ name: @name,
11
+ description: @description || self.class.description,
12
+ schema: {
13
+ :type => "object",
14
+ :properties => self.class.properties,
15
+ :required => self.class.required_properties,
16
+ :additionalProperties => self.class.additional_properties,
17
+ :strict => self.class.strict,
18
+ "$defs" => self.class.definitions
19
+ }
20
+ }
21
+ end
22
+
23
+ def to_json(*_args)
24
+ validate! # Validate schema before generating JSON string
25
+ JSON.pretty_generate(to_json_schema)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -32,7 +32,7 @@ module RubyLLM
32
32
 
33
33
  # Initialize all nodes as WHITE (no mark)
34
34
  marks = Hash.new { WHITE }
35
-
35
+
36
36
  # Visit each unmarked node
37
37
  definitions.each_key do |node|
38
38
  visit(node, definitions, marks) if marks[node] == WHITE
@@ -43,7 +43,7 @@ module RubyLLM
43
43
  def visit(node, definitions, marks)
44
44
  # If node has a permanent mark, return
45
45
  return if marks[node] == BLACK
46
-
46
+
47
47
  # If node has a temporary mark, we found a cycle
48
48
  if marks[node] == GRAY
49
49
  raise ValidationError, "Circular reference detected involving '#{node}'"
@@ -51,7 +51,7 @@ module RubyLLM
51
51
 
52
52
  # Mark node with temporary mark
53
53
  marks[node] = GRAY
54
-
54
+
55
55
  # Visit all adjacent nodes (dependencies)
56
56
  definition = definitions[node]
57
57
  if definition && definition[:properties]
@@ -62,14 +62,14 @@ module RubyLLM
62
62
  end
63
63
  end
64
64
  end
65
-
65
+
66
66
  # Mark node with permanent mark
67
67
  marks[node] = BLACK
68
68
  end
69
69
 
70
70
  def extract_references(property)
71
71
  references = []
72
-
72
+
73
73
  case property
74
74
  when Hash
75
75
  if property["$ref"]
@@ -92,4 +92,4 @@ module RubyLLM
92
92
  end
93
93
  end
94
94
  end
95
- end
95
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyLlm
4
4
  class Schema
5
- VERSION = "0.1.6"
5
+ VERSION = "0.1.8"
6
6
  end
7
7
  end
@@ -1,15 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "schema/version"
4
- require_relative "schema/property_schema_collector"
5
4
  require_relative "schema/errors"
6
5
  require_relative "schema/helpers"
7
6
  require_relative "schema/validator"
7
+ require_relative "schema/dsl"
8
+ require_relative "schema/json_output"
8
9
  require "json"
9
- require "set"
10
10
 
11
11
  module RubyLLM
12
12
  class Schema
13
+ extend DSL
14
+ include JsonOutput
15
+
13
16
  PRIMITIVE_TYPES = %i[string number integer boolean null].freeze
14
17
 
15
18
  class << self
@@ -19,6 +22,18 @@ module RubyLLM
19
22
  schema_class
20
23
  end
21
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
+
22
37
  def description(description = nil)
23
38
  @description = description if description
24
39
  @description
@@ -36,149 +51,6 @@ module RubyLLM
36
51
  @strict = value
37
52
  end
38
53
 
39
- def string(name = nil, enum: nil, description: nil, required: true, min_length: nil, max_length: nil, pattern: nil, format: nil)
40
- options = {
41
- enum: enum,
42
- description: description,
43
- minLength: min_length,
44
- maxLength: max_length,
45
- pattern: pattern,
46
- format: format
47
- }.compact
48
-
49
- add_property(name, build_property_schema(:string, **options), required: required)
50
- end
51
-
52
- def number(name = nil, description: nil, required: true, minimum: nil, maximum: nil, multiple_of: nil)
53
- options = {
54
- description: description,
55
- minimum: minimum,
56
- maximum: maximum,
57
- multipleOf: multiple_of
58
- }.compact
59
-
60
- add_property(name, build_property_schema(:number, **options), required: required)
61
- end
62
-
63
- def integer(name = nil, description: nil, required: true)
64
- add_property(name, build_property_schema(:integer, description: description), required: required)
65
- end
66
-
67
- def boolean(name = nil, description: nil, required: true)
68
- add_property(name, build_property_schema(:boolean, description: description), required: required)
69
- end
70
-
71
- def null(name = nil, description: nil, required: true)
72
- add_property(name, build_property_schema(:null, description: description), required: required)
73
- end
74
-
75
- def object(name = nil, description: nil, required: true, &block)
76
- add_property(name, build_property_schema(:object, description: description, &block), required: required)
77
- end
78
-
79
- def array(name, of: nil, description: nil, required: true, min_items: nil, max_items: nil, &block)
80
- items = determine_array_items(of, &block)
81
-
82
- add_property(name, {
83
- type: "array",
84
- description: description,
85
- items: items,
86
- minItems: min_items,
87
- maxItems: max_items
88
- }.compact, required: required)
89
- end
90
-
91
- def any_of(name, required: true, description: nil, &block)
92
- schemas = collect_property_schemas_from_block(&block)
93
-
94
- add_property(name, {
95
- description: description,
96
- anyOf: schemas
97
- }.compact, required: required)
98
- end
99
-
100
- def optional(name, description: nil, &block)
101
- any_of(name, description: description) do
102
- instance_eval(&block)
103
- null
104
- end
105
- end
106
-
107
- def define(name, &)
108
- sub_schema = Class.new(Schema)
109
- sub_schema.class_eval(&)
110
-
111
- definitions[name] = {
112
- type: "object",
113
- properties: sub_schema.properties,
114
- required: sub_schema.required_properties
115
- }
116
- end
117
-
118
- def reference(schema_name)
119
- {"$ref" => "#/$defs/#{schema_name}"}
120
- end
121
-
122
- def properties
123
- @properties ||= {}
124
- end
125
-
126
- def required_properties
127
- @required_properties ||= []
128
- end
129
-
130
- def definitions
131
- @definitions ||= {}
132
- end
133
-
134
- def build_property_schema(type, **options, &)
135
- case type
136
- when :string
137
- {
138
- type: "string",
139
- enum: options[:enum],
140
- description: options[:description],
141
- minLength: options[:minLength],
142
- maxLength: options[:maxLength],
143
- pattern: options[:pattern],
144
- format: options[:format]
145
- }.compact
146
- when :number
147
- {
148
- type: "number",
149
- description: options[:description],
150
- minimum: options[:minimum],
151
- maximum: options[:maximum],
152
- multipleOf: options[:multipleOf]
153
- }.compact
154
- when :integer
155
- {
156
- type: "integer",
157
- description: options[:description],
158
- minimum: options[:minimum],
159
- maximum: options[:maximum],
160
- multipleOf: options[:multipleOf]
161
- }.compact
162
- when :boolean
163
- {type: "boolean", description: options[:description]}.compact
164
- when :null
165
- {type: "null", description: options[:description]}.compact
166
- when :object
167
- sub_schema = Class.new(Schema)
168
- sub_schema.class_eval(&)
169
-
170
- {
171
- type: "object",
172
- properties: sub_schema.properties,
173
- required: sub_schema.required_properties,
174
- additionalProperties: additional_properties,
175
- description: options[:description]
176
- }.compact
177
- else
178
- raise InvalidSchemaTypeError, type
179
- end
180
- end
181
-
182
54
  def validate!
183
55
  validator = Validator.new(self)
184
56
  validator.validate!
@@ -188,31 +60,6 @@ module RubyLLM
188
60
  validator = Validator.new(self)
189
61
  validator.valid?
190
62
  end
191
-
192
- private
193
-
194
- def add_property(name, definition, required:)
195
- properties[name.to_sym] = definition
196
- required_properties << name.to_sym if required
197
- end
198
-
199
- def determine_array_items(of, &)
200
- return collect_property_schemas_from_block(&).first if block_given?
201
- return build_property_schema(of) if primitive_type?(of)
202
- return reference(of) if of.is_a?(Symbol)
203
-
204
- raise InvalidArrayTypeError, of
205
- end
206
-
207
- def collect_property_schemas_from_block(&)
208
- collector = PropertySchemaCollector.new
209
- collector.collect(&)
210
- collector.schemas
211
- end
212
-
213
- def primitive_type?(type)
214
- type.is_a?(Symbol) && PRIMITIVE_TYPES.include?(type)
215
- end
216
63
  end
217
64
 
218
65
  def initialize(name = nil, description: nil)
@@ -220,28 +67,6 @@ module RubyLLM
220
67
  @description = description
221
68
  end
222
69
 
223
- def to_json_schema
224
- validate! # Validate schema before generating JSON
225
-
226
- {
227
- name: @name,
228
- description: @description || self.class.description,
229
- schema: {
230
- :type => "object",
231
- :properties => self.class.properties,
232
- :required => self.class.required_properties,
233
- :additionalProperties => self.class.additional_properties,
234
- :strict => self.class.strict,
235
- "$defs" => self.class.definitions
236
- }
237
- }
238
- end
239
-
240
- def to_json(*_args)
241
- validate! # Validate schema before generating JSON string
242
- JSON.pretty_generate(to_json_schema)
243
- end
244
-
245
70
  def validate!
246
71
  self.class.validate!
247
72
  end
@@ -252,7 +77,7 @@ module RubyLLM
252
77
 
253
78
  def method_missing(method_name, ...)
254
79
  if respond_to_missing?(method_name)
255
- send(method_name, ...)
80
+ self.class.send(method_name, ...)
256
81
  else
257
82
  super
258
83
  end
@@ -1,23 +1,22 @@
1
1
  namespace :release do
2
2
  desc "Release a new version of the gem"
3
- task :version do
3
+ task :version, [:message] do |t, args|
4
4
  # Load the current version from version.rb
5
- require_relative '../../lib/ruby_llm/schema/version'
5
+ require_relative "../../lib/ruby_llm/schema/version"
6
6
  version = RubyLlm::Schema::VERSION
7
-
7
+
8
8
  puts "Releasing version #{version}..."
9
-
10
- # Prompt for release message
11
- print "Enter release message (optional): "
12
- release_message = STDIN.gets.chomp
13
-
14
- # Create git tag with message
15
- if release_message.empty?
16
- system "git tag v#{version}"
9
+
10
+ # Create git tag with optional message
11
+ # rake release:version["Fix critical bug in schema validation"]
12
+ if args[:message]
13
+ system "git tag -a v#{version} -m \"#{args[:message]}\""
14
+ puts "Created annotated tag v#{version} with message: #{args[:message]}"
17
15
  else
18
- system "git tag -a v#{version} -m \"#{release_message}\""
16
+ system "git tag v#{version}"
17
+ puts "Created lightweight tag v#{version}"
19
18
  end
20
-
19
+
21
20
  system "git push origin main"
22
21
  system "git push origin v#{version}"
23
22
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm-schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Friis
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-08-08 00:00:00.000000000 Z
10
+ date: 2025-08-22 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rspec
@@ -48,9 +48,10 @@ files:
48
48
  - README.md
49
49
  - Rakefile
50
50
  - lib/ruby_llm/schema.rb
51
+ - lib/ruby_llm/schema/dsl.rb
51
52
  - lib/ruby_llm/schema/errors.rb
52
53
  - lib/ruby_llm/schema/helpers.rb
53
- - lib/ruby_llm/schema/property_schema_collector.rb
54
+ - lib/ruby_llm/schema/json_output.rb
54
55
  - lib/ruby_llm/schema/validator.rb
55
56
  - lib/ruby_llm/schema/version.rb
56
57
  - lib/tasks/release.rake
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- class Schema
5
- class PropertySchemaCollector
6
- attr_reader :schemas
7
-
8
- def initialize
9
- @schemas = []
10
- end
11
-
12
- def collect(&)
13
- instance_eval(&)
14
- end
15
-
16
- def string(**options)
17
- @schemas << Schema.build_property_schema(:string, **options)
18
- end
19
-
20
- def number(**options)
21
- @schemas << Schema.build_property_schema(:number, **options)
22
- end
23
-
24
- def integer(**options)
25
- @schemas << Schema.build_property_schema(:integer, **options)
26
- end
27
-
28
- def boolean(**options)
29
- @schemas << Schema.build_property_schema(:boolean, **options)
30
- end
31
-
32
- def null(**options)
33
- @schemas << Schema.build_property_schema(:null, **options)
34
- end
35
-
36
- def object(...)
37
- @schemas << Schema.build_property_schema(:object, ...)
38
- end
39
- end
40
- end
41
- end