Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: ['3.1', '3.2', '3.3', '3.4']

steps:
- uses: actions/checkout@v4

- name: Set up Ruby ${{ matrix.ruby-version }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true

- name: Run Standard (linting)
run: bundle exec rake standard

- name: Run RSpec (tests)
run: bundle exec rake spec
4 changes: 4 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "standard/rake"

RSpec::Core::RakeTask.new(:spec)

task default: %i[standard spec]
3 changes: 1 addition & 2 deletions lib/ruby_llm/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
require_relative "schema/dsl"
require_relative "schema/json_output"
require "json"
require "set"

module RubyLLM
class Schema
extend DSL
include JsonOutput

PRIMITIVE_TYPES = %i[string number integer boolean null].freeze

class << self
Expand Down
8 changes: 4 additions & 4 deletions lib/ruby_llm/schema/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def string(name = nil, enum: nil, description: nil, required: true, min_length:
pattern: pattern,
format: format
}.compact

add_property(name, build_property_schema(:string, **options), required: required)
end

Expand All @@ -24,7 +24,7 @@ def number(name = nil, description: nil, required: true, minimum: nil, maximum:
maximum: maximum,
multipleOf: multiple_of
}.compact

add_property(name, build_property_schema(:number, **options), required: required)
end

Expand Down Expand Up @@ -161,7 +161,7 @@ def determine_array_items(of, &)
def collect_property_schemas_from_block(&block)
schemas = []
schema_builder = self # Capture the current context that has build_property_schema

context = Object.new
context.define_singleton_method(:string) { |name = nil, **options| schemas << schema_builder.build_property_schema(:string, **options) }
context.define_singleton_method(:number) { |name = nil, **options| schemas << schema_builder.build_property_schema(:number, **options) }
Expand All @@ -170,7 +170,7 @@ def collect_property_schemas_from_block(&block)
context.define_singleton_method(:null) { |name = nil, **options| schemas << schema_builder.build_property_schema(:null, **options) }
context.define_singleton_method(:object) { |name = nil, **options, &blk| schemas << schema_builder.build_property_schema(:object, **options, &blk) }
context.define_singleton_method(:any_of) { |name = nil, **options, &blk| schemas << schema_builder.build_property_schema(:any_of, **options, &blk) }

context.instance_eval(&block)
schemas
end
Expand Down
2 changes: 1 addition & 1 deletion lib/ruby_llm/schema/json_output.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Schema
module JsonOutput
def to_json_schema
validate! # Validate schema before generating JSON

{
name: @name,
description: @description || self.class.description,
Expand Down
10 changes: 5 additions & 5 deletions lib/ruby_llm/schema/validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def validate_circular_references!

# Initialize all nodes as WHITE (no mark)
marks = Hash.new { WHITE }

# Visit each unmarked node
definitions.each_key do |node|
visit(node, definitions, marks) if marks[node] == WHITE
Expand All @@ -43,15 +43,15 @@ def validate_circular_references!
def visit(node, definitions, marks)
# If node has a permanent mark, return
return if marks[node] == BLACK

# If node has a temporary mark, we found a cycle
if marks[node] == GRAY
raise ValidationError, "Circular reference detected involving '#{node}'"
end

# Mark node with temporary mark
marks[node] = GRAY

# Visit all adjacent nodes (dependencies)
definition = definitions[node]
if definition && definition[:properties]
Expand All @@ -62,14 +62,14 @@ def visit(node, definitions, marks)
end
end
end

# Mark node with permanent mark
marks[node] = BLACK
end

def extract_references(property)
references = []

case property
when Hash
if property["$ref"]
Expand Down
8 changes: 4 additions & 4 deletions lib/tasks/release.rake
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ namespace :release do
desc "Release a new version of the gem"
task :version, [:message] do |t, args|
# Load the current version from version.rb
require_relative '../../lib/ruby_llm/schema/version'
require_relative "../../lib/ruby_llm/schema/version"
version = RubyLlm::Schema::VERSION

puts "Releasing version #{version}..."

# Create git tag with optional message
# rake release:version["Fix critical bug in schema validation"]
if args[:message]
Expand All @@ -16,7 +16,7 @@ namespace :release do
system "git tag v#{version}"
puts "Created lightweight tag v#{version}"
end

system "git push origin main"
system "git push origin v#{version}"

Expand Down
14 changes: 7 additions & 7 deletions spec/ruby_llm/schema_class_inheritance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

describe "name" do
it "uses class name when provided (via constant)" do
TestNamedSchema = Class.new(described_class)
stub_const("TestNamedSchema", Class.new(described_class))
instance = TestNamedSchema.new
expect(instance.to_json_schema[:name]).to eq("TestNamedSchema")
end
Expand Down Expand Up @@ -79,13 +79,13 @@
string :name, description: "Name field"
integer :count
boolean :active, required: false

object :config do
string :setting
end

array :tags, of: :string

any_of :status do
string
null
Expand Down Expand Up @@ -121,7 +121,7 @@
description: "Test description",
schema: hash_including(
type: "object",
properties: { title: { type: "string" } },
properties: {title: {type: "string"}},
required: [:title],
additionalProperties: false,
strict: true
Expand All @@ -141,8 +141,8 @@

expect(json_output).to include(
name: "ConfiguredSchema",
description: "Instance description",
description: "Instance description"
)
end
end
end
end
16 changes: 8 additions & 8 deletions spec/ruby_llm/schema_factory_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
describe "configuration options" do
describe "name" do
it "uses class name when provided (via constant assignment)" do
NamedFactorySchema = described_class.create do
stub_const("NamedFactorySchema", described_class.create do
string :title
end
end)

instance = NamedFactorySchema.new
expect(instance.to_json_schema[:name]).to eq("NamedFactorySchema")
Expand Down Expand Up @@ -96,13 +96,13 @@
string :name, description: "Name field"
integer :count
boolean :active, required: false

object :config do
string :setting
end

array :tags, of: :string

any_of :status do
string
null
Expand Down Expand Up @@ -138,7 +138,7 @@
description: "Factory test description",
schema: hash_including(
type: "object",
properties: { title: { type: "string" } },
properties: {title: {type: "string"}},
required: [:title],
additionalProperties: false,
strict: true
Expand All @@ -158,8 +158,8 @@

expect(json_output).to include(
name: "FactoryConfiguredSchema",
description: "Instance description",
description: "Instance description"
)
end
end
end
end
10 changes: 5 additions & 5 deletions spec/ruby_llm/schema_helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@
string :name, description: "Name field"
integer :count
boolean :active, required: false

object :config do
string :setting
end

array :tags, of: :string

any_of :status do
string
null
Expand Down Expand Up @@ -131,12 +131,12 @@
description: "Helper test description",
schema: hash_including(
type: "object",
properties: { title: { type: "string" } },
properties: {title: {type: "string"}},
required: [:title],
additionalProperties: false,
strict: true
)
)
end
end
end
end
Loading