ruby_llm-schema 0.3.0 → 0.3.1

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: 228903cdd6e6080b7c687147ecc609349601d315da71e861b1d900f7c252ac00
4
- data.tar.gz: ecdc3e346f33710ceff0d68c8eb13266956bddc1a36852f02128e3d3f2d94ff5
3
+ metadata.gz: fceccc2292efb7c17ce5457a08d6a18ddc3debd920052a1d8e368d74e450547e
4
+ data.tar.gz: b16fafc541af3b9f1a1e3a96f81be21175a380606f2e40325afa29d76108daea
5
5
  SHA512:
6
- metadata.gz: ca4ee64989087583e4a15e3b608b7e867034f0682a5f67753efeedee2f98bdc60ce933ff9a0823c93a633d43a9258e550c51b4d3fdbcddc0b3b2462ff40c5586
7
- data.tar.gz: 03affcdc52ba81f9c7c5d62fcd23e58f800dad7be7580df6c73d6c00b7d1941db0eacd867c83e075aee51090d52e418159472fe72b456533d4cbf1c1141c5ecc
6
+ metadata.gz: 984b11c9fcfcaf68f0892a9a7b9c10064436aa59279f0a0992ebae5c6d20fbcfd1e6afbef8376bf7a9239aaaea246dd9f8c7f6a3d9521509789c8e334e3e0363
7
+ data.tar.gz: e9658cff5e3e1f0912eab47fe8438a5dfd5bf52928dd0198ff39131ecf1aeb5d3b2a8e08344feab0b363ed6a9a27747c1489fcfadefd8f30af4d03c77fb601d8
data/README.md CHANGED
@@ -1,21 +1,24 @@
1
1
  # RubyLLM::Schema
2
2
 
3
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)
4
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/crmne/ruby_llm-schema/blob/main/LICENSE)
5
+ [![CI](https://github.com/crmne/ruby_llm-schema/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/crmne/ruby_llm-schema/actions/workflows/main.yml)
6
6
 
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.
7
+ A Ruby DSL for creating JSON schemas with a clean, Rails-inspired API.
8
+
9
+ Originally created by [Daniel Friis](https://github.com/danielfriis).
8
10
 
9
11
  ## Use Cases
10
12
 
11
- Structured output is a powerful tool for LLMs to generate consistent and predictable responses.
13
+ JSON Schema is useful wherever Ruby code needs to describe structured data in a portable format.
12
14
 
13
15
  Some ideal use cases:
14
16
 
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
17
+ - Defining API request and response shapes
18
+ - Describing configuration files or structured payloads
19
+ - Sharing validation contracts across systems
20
+ - Generating structured output schemas for LLM workflows
21
+ - Defining structured parameters for RubyLLM tools
19
22
 
20
23
  ### Simple Example
21
24
 
@@ -24,22 +27,22 @@ class PersonSchema < RubyLLM::Schema
24
27
  string :name, description: "Person's full name"
25
28
  number :age, description: "Age in years", minimum: 0, maximum: 120
26
29
  boolean :active, required: false
27
-
30
+
28
31
  object :address do
29
32
  string :street
30
33
  string :city
31
34
  string :country, required: false
32
35
  end
33
-
36
+
34
37
  array :tags, of: :string, description: "User tags"
35
-
38
+
36
39
  array :contacts do
37
40
  object do
38
41
  string :email, format: "email"
39
42
  string :phone, required: false
40
43
  end
41
44
  end
42
-
45
+
43
46
  any_of :status do
44
47
  string enum: ["active", "pending", "inactive"]
45
48
  null
@@ -51,7 +54,7 @@ schema = PersonSchema.new
51
54
  puts schema.to_json
52
55
  ```
53
56
 
54
- ### Most common use case with RubyLLM
57
+ ### RubyLLM structured output
55
58
 
56
59
  ```ruby
57
60
  class PersonSchema < RubyLLM::Schema
@@ -70,6 +73,43 @@ puts response.content # => {"name" => "Alice", "age" => 30}
70
73
  puts response.content.class # => Hash
71
74
  ```
72
75
 
76
+ ### RubyLLM tools
77
+
78
+ 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.
79
+
80
+ ```ruby
81
+ class SearchParams < RubyLLM::Schema
82
+ string :query, description: "Search query"
83
+ integer :limit, required: false, description: "Maximum results"
84
+ end
85
+
86
+ class SearchDocuments < RubyLLM::Tool
87
+ desc "Searches internal documents"
88
+ params SearchParams
89
+
90
+ def execute(query:, limit: 10)
91
+ DocumentSearch.call(query:, limit:)
92
+ end
93
+ end
94
+ ```
95
+
96
+ For tool-specific parameters, define the schema inline with `params do ... end`.
97
+
98
+ ```ruby
99
+ class Weather < RubyLLM::Tool
100
+ desc "Gets current weather"
101
+
102
+ params do
103
+ string :city, description: "City name"
104
+ string :units, enum: %w[celsius fahrenheit], required: false
105
+ end
106
+
107
+ def execute(city:, units: "celsius")
108
+ WeatherAPI.current(city:, units:)
109
+ end
110
+ end
111
+ ```
112
+
73
113
  ## Installation
74
114
 
75
115
  Add this line to your application's Gemfile:
@@ -101,12 +141,12 @@ class PersonSchema < RubyLLM::Schema
101
141
  string :name, description: "Person's full name"
102
142
  number :age
103
143
  boolean :active, required: false
104
-
144
+
105
145
  object :address do
106
146
  string :street
107
147
  string :city
108
148
  end
109
-
149
+
110
150
  array :tags, of: :string
111
151
  end
112
152
 
@@ -121,12 +161,12 @@ PersonSchema = RubyLLM::Schema.create do
121
161
  string :name, description: "Person's full name"
122
162
  number :age
123
163
  boolean :active, required: false
124
-
164
+
125
165
  object :address do
126
166
  string :street
127
167
  string :city
128
168
  end
129
-
169
+
130
170
  array :tags, of: :string
131
171
  end
132
172
 
@@ -144,12 +184,12 @@ person_schema = schema "PersonData", description: "A person object" do
144
184
  string :name, description: "Person's full name"
145
185
  number :age
146
186
  boolean :active, required: false
147
-
187
+
148
188
  object :address do
149
189
  string :street
150
190
  string :city
151
191
  end
152
-
192
+
153
193
  array :tags, of: :string
154
194
  end
155
195
 
@@ -269,7 +309,7 @@ Union types are a way to specify that a property can be one of several types.
269
309
  ```ruby
270
310
  any_of :value do
271
311
  string
272
- number
312
+ number
273
313
  null
274
314
  end
275
315
 
@@ -289,7 +329,7 @@ class MySchema < RubyLLM::Schema
289
329
  string :latitude
290
330
  string :longitude
291
331
  end
292
-
332
+
293
333
  # Using a reference in an array
294
334
  array :coordinates, of: :location
295
335
 
@@ -324,7 +364,7 @@ class CompanySchema < RubyLLM::Schema
324
364
  # Using 'of' parameter
325
365
  object :ceo, of: PersonSchema
326
366
  array :employees, of: PersonSchema
327
-
367
+
328
368
  # Using Schema.new in block
329
369
  object :founder do
330
370
  PersonSchema.new
@@ -118,21 +118,19 @@ module RubyLLM
118
118
 
119
119
  def determine_object_reference(of, description = nil)
120
120
  result = case of
121
- when Symbol
122
- reference(of)
123
- when Class
124
- if schema_class?(of)
125
- schema_class_to_inline_schema(of)
126
- else
127
- raise InvalidObjectTypeError, "Invalid object type: #{of.inspect}. Class must inherit from RubyLLM::Schema."
128
- end
129
- else
130
- if schema_class?(of)
131
- schema_class_to_inline_schema(of)
132
- else
133
- raise InvalidObjectTypeError, "Invalid object type: #{of.inspect}. Must be a symbol reference, a Schema class, or a Schema instance."
134
- end
135
- end
121
+ when Symbol
122
+ reference(of)
123
+ when Class
124
+ raise InvalidObjectTypeError, "Invalid object type: #{of.inspect}. Class must inherit from RubyLLM::Schema." unless schema_class?(of)
125
+
126
+ schema_class_to_inline_schema(of)
127
+
128
+ else
129
+ raise InvalidObjectTypeError, "Invalid object type: #{of.inspect}. Must be a symbol reference, a Schema class, or a Schema instance." unless schema_class?(of)
130
+
131
+ schema_class_to_inline_schema(of)
132
+
133
+ end
136
134
 
137
135
  description ? result.merge(description: description) : result
138
136
  end
@@ -147,7 +145,7 @@ module RubyLLM
147
145
  schema_builder.methods.grep(/_schema$/).each do |schema_method|
148
146
  type_name = schema_method.to_s.sub(/_schema$/, "")
149
147
 
150
- context.define_singleton_method(type_name) do |name = nil, **options, &blk|
148
+ context.define_singleton_method(type_name) do |_name = nil, **options, &blk|
151
149
  schemas << schema_builder.send(schema_method, **options, &blk)
152
150
  end
153
151
  end
@@ -164,10 +162,10 @@ module RubyLLM
164
162
  def schema_class_to_inline_schema(schema_class_or_instance)
165
163
  # Handle both Schema classes and Schema instances
166
164
  schema_class = if schema_class_or_instance.is_a?(Class)
167
- schema_class_or_instance
168
- else
169
- schema_class_or_instance.class
170
- end
165
+ schema_class_or_instance
166
+ else
167
+ schema_class_or_instance.class
168
+ end
171
169
 
172
170
  # Directly convert schema class to inline object schema
173
171
  {
@@ -178,10 +176,10 @@ module RubyLLM
178
176
  }.tap do |schema|
179
177
  # For instances, prefer instance description over class description
180
178
  description = if schema_class_or_instance.is_a?(Class)
181
- schema_class.description
182
- else
183
- schema_class_or_instance.instance_variable_get(:@description) || schema_class.description
184
- end
179
+ schema_class.description
180
+ else
181
+ schema_class_or_instance.instance_variable_get(:@description) || schema_class.description
182
+ end
185
183
  schema[:description] = description if description
186
184
  end
187
185
  end
@@ -28,8 +28,16 @@ module RubyLLM
28
28
  private
29
29
 
30
30
  def add_property(name, definition, required:)
31
- properties[name.to_sym] = definition
32
- required_properties << name.to_sym if required
31
+ property_name = name.to_sym
32
+
33
+ properties[property_name] = definition
34
+ if required
35
+ required_properties << property_name unless required_properties.include?(property_name)
36
+ else
37
+ required_properties.delete(property_name)
38
+ end
39
+
40
+ nil
33
41
  end
34
42
 
35
43
  def primitive_type?(type)
@@ -13,18 +13,10 @@ module RubyLLM
13
13
  end
14
14
 
15
15
  # Raised when an invalid array type is specified
16
- class InvalidArrayTypeError < Error
17
- def initialize(message)
18
- super
19
- end
20
- end
16
+ class InvalidArrayTypeError < Error; end
21
17
 
22
18
  # Raised when an invalid object type is specified
23
- class InvalidObjectTypeError < Error
24
- def initialize(message)
25
- super
26
- end
27
- end
19
+ class InvalidObjectTypeError < Error; end
28
20
 
29
21
  # Raised when schema definition is invalid
30
22
  class InvalidSchemaError < Error; end
@@ -4,7 +4,7 @@ module RubyLLM
4
4
  class Schema
5
5
  module JsonOutput
6
6
  def to_json_schema
7
- validate! # Validate schema before generating JSON
7
+ validate! # Validate schema before generating JSON
8
8
 
9
9
  schema_hash = {
10
10
  type: "object",
@@ -26,7 +26,7 @@ module RubyLLM
26
26
  end
27
27
 
28
28
  def to_json(*_args)
29
- validate! # Validate schema before generating JSON string
29
+ validate! # Validate schema before generating JSON string
30
30
  JSON.pretty_generate(to_json_schema)
31
31
  end
32
32
  end
@@ -45,9 +45,7 @@ module RubyLLM
45
45
  return if marks[node] == BLACK
46
46
 
47
47
  # If node has a temporary mark, we found a cycle
48
- if marks[node] == GRAY
49
- raise ValidationError, "Circular reference detected involving '#{node}'"
50
- end
48
+ raise ValidationError, "Circular reference detected involving '#{node}'" if marks[node] == GRAY
51
49
 
52
50
  # Mark node with temporary mark
53
51
  marks[node] = GRAY
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module RubyLlm
3
+ module RubyLLM
4
4
  class Schema
5
- VERSION = "0.3.0"
5
+ VERSION = '0.3.1'
6
6
  end
7
7
  end
@@ -48,6 +48,7 @@ module RubyLLM
48
48
 
49
49
  def additional_properties(value = nil)
50
50
  return @additional_properties ||= false if value.nil?
51
+
51
52
  @additional_properties = value
52
53
  end
53
54
 
@@ -1,53 +1,8 @@
1
- # 1. Bump the version
2
- # Update VERSION in lib/ruby_llm/schema/version.rb
3
-
4
- # 2. Update Gemfile.lock
5
- # bundle install
6
-
7
- # 3. Commit everything
8
- # git add .
9
- # git commit -m "Bump version to X.Y.Z"
10
-
11
- # 4. Push to main
12
- # git push origin main
13
-
14
- # 5. Trigger the release
15
- # bundle exec rake release:prepare
16
-
17
- # 6. Delete the release branch locally and remotely
18
- # git branch -d release/X.Y.Z
19
- # git push origin --delete release/X.Y.Z
1
+ # frozen_string_literal: true
20
2
 
21
3
  namespace :release do
22
- desc "Prepare and push the release branch to trigger the automated pipeline"
4
+ desc 'Prepare for release'
23
5
  task :prepare do
24
- abort "Git working directory not clean. Commit or stash changes first." unless `git status --porcelain`.strip.empty?
25
- abort "Not on main branch. Releases must be run from main branch" unless `git rev-parse --abbrev-ref HEAD`.strip == "main"
26
-
27
- require_relative "../ruby_llm/schema/version"
28
- version = RubyLlm::Schema::VERSION or abort "Could not determine version"
29
-
30
- branch = "release/#{version}"
31
-
32
- if system("git rev-parse --quiet --verify refs/tags/v#{version} > /dev/null 2>&1")
33
- abort "Tag v#{version} already exists. Bump the version first."
34
- end
35
-
36
- if system("git show-ref --verify --quiet refs/heads/#{branch}")
37
- abort "Local branch #{branch} already exists. Remove it or choose a new version."
38
- end
39
-
40
- sh "git fetch origin"
41
-
42
- if system("git ls-remote --exit-code --heads origin #{branch} > /dev/null 2>&1")
43
- abort "Release branch #{branch} already exists on origin. Remove it or choose a new version."
44
- end
45
-
46
- sh "git checkout -b #{branch}"
47
- sh "git push -u origin #{branch}"
48
-
49
- puts "Release branch #{branch} pushed. GitHub Actions will run tests and publish if they pass."
50
- ensure
51
- system "git checkout main"
6
+ sh 'overcommit --run'
52
7
  end
53
8
  end
metadata CHANGED
@@ -1,55 +1,24 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm-schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Friis
8
- autorequire:
9
- bindir: exe
8
+ bindir: bin
10
9
  cert_chain: []
11
- date: 2026-01-08 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rspec
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '3.0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '3.0'
27
- - !ruby/object:Gem::Dependency
28
- name: standard
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- description:
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: A compact Ruby DSL for building standards-oriented JSON Schema documents
13
+ from Ruby.
42
14
  email:
43
15
  - d@friis.me
44
16
  executables: []
45
17
  extensions: []
46
18
  extra_rdoc_files: []
47
19
  files:
48
- - ".rspec"
49
- - LICENSE.txt
20
+ - LICENSE
50
21
  - README.md
51
- - RELEASE.md
52
- - Rakefile
53
22
  - lib/ruby_llm/schema.rb
54
23
  - lib/ruby_llm/schema/dsl.rb
55
24
  - lib/ruby_llm/schema/dsl/complex_types.rb
@@ -62,15 +31,15 @@ files:
62
31
  - lib/ruby_llm/schema/validator.rb
63
32
  - lib/ruby_llm/schema/version.rb
64
33
  - lib/tasks/release.rake
65
- homepage: https://github.com/danielfriis/ruby_llm-schema
34
+ homepage: https://github.com/crmne/ruby_llm-schema#readme
66
35
  licenses:
67
36
  - MIT
68
37
  metadata:
69
- homepage_uri: https://github.com/danielfriis/ruby_llm-schema
70
- source_code_uri: https://github.com/danielfriis/ruby_llm-schema
71
- changelog_uri: https://github.com/danielfriis/ruby_llm-schema/blob/main/CHANGELOG.md
38
+ homepage_uri: https://github.com/crmne/ruby_llm-schema#readme
39
+ source_code_uri: https://github.com/crmne/ruby_llm-schema
40
+ changelog_uri: https://github.com/crmne/ruby_llm-schema/releases
41
+ bug_tracker_uri: https://github.com/crmne/ruby_llm-schema/issues
72
42
  rubygems_mfa_required: 'true'
73
- post_install_message:
74
43
  rdoc_options: []
75
44
  require_paths:
76
45
  - lib
@@ -78,15 +47,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
78
47
  requirements:
79
48
  - - ">="
80
49
  - !ruby/object:Gem::Version
81
- version: 3.1.0
50
+ version: 3.1.3
82
51
  required_rubygems_version: !ruby/object:Gem::Requirement
83
52
  requirements:
84
53
  - - ">="
85
54
  - !ruby/object:Gem::Version
86
55
  version: '0'
87
56
  requirements: []
88
- rubygems_version: 3.4.19
89
- signing_key:
57
+ rubygems_version: 4.0.10
90
58
  specification_version: 4
91
- summary: A simple and clean Ruby DSL for creating JSON schemas.
59
+ summary: A simple Ruby DSL for creating JSON schemas.
92
60
  test_files: []
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --require spec_helper
2
- --color
3
- --format documentation
data/RELEASE.md DELETED
@@ -1,8 +0,0 @@
1
- # Release process
2
-
3
- 1. Bump the version in `lib/ruby_llm/schema/version.rb`
4
- 2. Run `bundle install` to update the gemspec
5
- 3. Commit the changes with a message like "Bump version to X.Y.Z"
6
- 4. Run `bundle exec rake release:prepare` to create a release branch and push it to GitHub
7
- 5. Github Actions will run the tests and publish the gem if they pass
8
- 6. Delete the release branch: `git branch -d release/<version> && git push origin --delete release/<version>`
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
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]
File without changes