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
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_call:

jobs:
test:
Expand All @@ -15,7 +16,9 @@ jobs:

steps:
- uses: actions/checkout@v4

with:
fetch-depth: 0

- name: Set up Ruby ${{ matrix.ruby-version }}
uses: ruby/setup-ruby@v1
with:
Expand Down
19 changes: 1 addition & 18 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,7 @@ on:

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

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

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

- name: Run test suite
run: ./bin/test
uses: ./.github/workflows/ci.yml

release:
needs: test
Expand Down
8 changes: 8 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Release process

1. Bump the version in `lib/ruby_llm/schema/version.rb`
2. Run `bundle install` to update the gemspec
3. Commit the changes with a message like "Bump version to X.Y.Z"
4. Run `bundle exec rake release:prepare` to create a release branch and push it to GitHub
5. Github Actions will run the tests and publish the gem if they pass
6. Delete the release branch: `git branch -d release/<version> && git push origin --delete release/<version>`
114 changes: 114 additions & 0 deletions spec/ruby_llm/schema/entry_points/class_inheritance_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe RubyLLM::Schema, "class inheritance approach" do
include SchemaBuilders

describe "schema attributes" do
let(:base_schema) { build_schema_class { string :title } }

it "derives schema names from constants, instances, and defaults" do
stub_const("NamedSchema", build_schema_class)
expect(NamedSchema.new.to_json_schema[:name]).to eq("NamedSchema")

expect(base_schema.new.to_json_schema[:name]).to eq("Schema")
expect(base_schema.new("CustomName").to_json_schema[:name]).to eq("CustomName")
end

it "honours description precedence" do
schema_with_description = build_schema_class do
description "Class-level description"
string :title
end

anonymous_output = base_schema.new.to_json_schema
expect(anonymous_output[:description]).to be_nil

class_level_output = schema_with_description.new.to_json_schema
expect(class_level_output[:description]).to eq("Class-level description")

instance_override = schema_with_description.new("Test", description: "Instance description").to_json_schema
expect(instance_override[:description]).to eq("Instance description")
end

it "controls additional properties and strictness" do
default_output = base_schema.new.to_json_schema
expect(default_output[:schema][:additionalProperties]).to eq(false)
expect(default_output[:schema][:strict]).to eq(true)

configured_schema = build_schema_class do
additional_properties true
strict false
string :title
end

configured_output = configured_schema.new.to_json_schema
expect(configured_output[:schema][:additionalProperties]).to eq(true)
expect(configured_output[:schema][:strict]).to eq(false)
end

it "renders structured JSON schema" do
configured_schema = build_schema_class do
description "Test description"
additional_properties false
string :title
integer :count, required: false
end

output = configured_schema.new("ConfiguredSchema").to_json_schema

expect(output).to include(
name: "ConfiguredSchema",
description: "Test description",
schema: hash_including(
type: "object",
properties: {
title: {type: "string"},
count: {type: "integer"}
},
required: [:title],
additionalProperties: false,
strict: true
)
)
end
end

describe "comprehensive scenario" do
let(:schema_class) do
build_schema_class do
description "Comprehensive test schema"
additional_properties true
strict true

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
end
end
end

it "supports full-feature schemas" do
json_output = schema_class.new("TestSchema").to_json_schema

expect(json_output[:name]).to eq("TestSchema")
expect(json_output[:schema][:additionalProperties]).to eq(true)
expect(json_output[:schema][:strict]).to eq(true)
expect(json_output[:schema][:properties].keys).to contain_exactly(
:name, :count, :active, :config, :tags, :status
)
expect(json_output[:schema][:required]).to contain_exactly(:name, :count, :config, :tags, :status)
end
end
end
114 changes: 114 additions & 0 deletions spec/ruby_llm/schema/entry_points/factory_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe RubyLLM::Schema, "factory method (.create) approach" do
include SchemaBuilders

describe "schema attributes" do
let(:base_schema) { build_factory_schema { string :title } }

it "derives schema names from constants, instances, and defaults" do
stub_const("NamedFactorySchema", build_factory_schema { string :title })
expect(NamedFactorySchema.new.to_json_schema[:name]).to eq("NamedFactorySchema")

expect(base_schema.new.to_json_schema[:name]).to eq("Schema")
expect(base_schema.new("CustomName").to_json_schema[:name]).to eq("CustomName")
end

it "honours description precedence" do
schema_with_description = build_factory_schema do
description "Factory description"
string :title
end

anonymous_output = base_schema.new.to_json_schema
expect(anonymous_output[:description]).to be_nil

class_level_output = schema_with_description.new.to_json_schema
expect(class_level_output[:description]).to eq("Factory description")

instance_override = schema_with_description.new("NamedSchema", description: "Instance description").to_json_schema
expect(instance_override[:description]).to eq("Instance description")
end

it "controls additional properties and strictness" do
default_output = base_schema.new.to_json_schema
expect(default_output[:schema][:additionalProperties]).to eq(false)
expect(default_output[:schema][:strict]).to eq(true)

configured_schema = build_factory_schema do
additional_properties true
strict false
string :title
end

configured_output = configured_schema.new.to_json_schema
expect(configured_output[:schema][:additionalProperties]).to eq(true)
expect(configured_output[:schema][:strict]).to eq(false)
end

it "renders structured JSON schema" do
configured_schema = build_factory_schema do
description "Factory test description"
additional_properties false
string :title
integer :count, required: false
end

output = configured_schema.new("FactoryConfiguredSchema").to_json_schema

expect(output).to include(
name: "FactoryConfiguredSchema",
description: "Factory test description",
schema: hash_including(
type: "object",
properties: {
title: {type: "string"},
count: {type: "integer"}
},
required: [:title],
additionalProperties: false,
strict: true
)
)
end
end

describe "comprehensive scenario" do
let(:schema_class) do
build_factory_schema do
description "Comprehensive factory schema"
additional_properties true
strict true

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
end
end
end

it "supports full-feature schemas" do
json_output = schema_class.new("FactorySchema").to_json_schema

expect(json_output[:name]).to eq("FactorySchema")
expect(json_output[:schema][:additionalProperties]).to eq(true)
expect(json_output[:schema][:strict]).to eq(true)
expect(json_output[:schema][:properties].keys).to contain_exactly(
:name, :count, :active, :config, :tags, :status
)
expect(json_output[:schema][:required]).to contain_exactly(:name, :count, :config, :tags, :status)
end
end
end
114 changes: 114 additions & 0 deletions spec/ruby_llm/schema/entry_points/helpers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe RubyLLM::Schema, "helpers module approach" do
include SchemaBuilders

describe "schema attributes" do
it "derives schema names from parameters and defaults" do
named_schema = build_helper_schema("ProvidedName") { string :title }
expect(named_schema.to_json_schema[:name]).to eq("ProvidedName")

default_schema = build_helper_schema { string :title }
expect(default_schema.to_json_schema[:name]).to eq("Schema")
end

it "honours description precedence" do
default_output = build_helper_schema("TestSchema") { string :title }.to_json_schema
expect(default_output[:description]).to be_nil

block_description = build_helper_schema("TestSchema") do
description "Block description"
string :title
end.to_json_schema
expect(block_description[:description]).to eq("Block description")

parameter_override = build_helper_schema(
"TestSchema",
description: "Parameter description"
) do
description "Block description"
string :title
end.to_json_schema
expect(parameter_override[:description]).to eq("Parameter description")
end

it "controls additional properties and strictness" do
default_output = build_helper_schema { string :title }.to_json_schema
expect(default_output[:schema][:additionalProperties]).to eq(false)
expect(default_output[:schema][:strict]).to eq(true)

configured_output = build_helper_schema do
additional_properties true
strict false
string :title
end.to_json_schema

expect(configured_output[:schema][:additionalProperties]).to eq(true)
expect(configured_output[:schema][:strict]).to eq(false)
end

it "renders structured JSON schema" do
json_output = build_helper_schema(
"HelperConfiguredSchema",
description: "Helper test description"
) do
additional_properties false
string :title
integer :count, required: false
end.to_json_schema

expect(json_output).to include(
name: "HelperConfiguredSchema",
description: "Helper test description",
schema: hash_including(
type: "object",
properties: {
title: {type: "string"},
count: {type: "integer"}
},
required: [:title],
additionalProperties: false,
strict: true
)
)
end
end

describe "comprehensive scenario" do
it "supports full-feature schemas" do
json_output = build_helper_schema(
"HelperSchema",
description: "Comprehensive helper schema"
) do
additional_properties true
strict true

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
end
end.to_json_schema

expect(json_output[:name]).to eq("HelperSchema")
expect(json_output[:description]).to eq("Comprehensive helper schema")
expect(json_output[:schema][:additionalProperties]).to eq(true)
expect(json_output[:schema][:strict]).to eq(true)
expect(json_output[:schema][:properties].keys).to contain_exactly(
:name, :count, :active, :config, :tags, :status
)
expect(json_output[:schema][:required]).to contain_exactly(:name, :count, :config, :tags, :status)
end
end
end
Loading