Skip to content

Commit 6b2ae6b

Browse files
authored
Add support for a oneOf type (#25)
1 parent 55c296f commit 6b2ae6b

File tree

4 files changed

+64
-2
lines changed

4 files changed

+64
-2
lines changed

lib/ruby_llm/schema.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def method_missing(method_name, ...)
8484
end
8585

8686
def respond_to_missing?(method_name, include_private = false)
87-
%i[string number integer boolean array object any_of null].include?(method_name) || super
87+
%i[string number integer boolean array object any_of one_of null].include?(method_name) || super
8888
end
8989
end
9090
end

lib/ruby_llm/schema/dsl/complex_types.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ def any_of(name, description: nil, required: true, **options, &block)
1616
add_property(name, any_of_schema(description: description, **options, &block), required: required)
1717
end
1818

19+
def one_of(name, description: nil, required: true, **options, &block)
20+
add_property(name, one_of_schema(description: description, **options, &block), required: required)
21+
end
22+
1923
def optional(name, description: nil, &block)
2024
any_of(name, description: description) do
2125
instance_eval(&block)

lib/ruby_llm/schema/dsl/schema_builders.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,15 @@ def any_of_schema(description: nil, &block)
9696
}.compact
9797
end
9898

99+
def one_of_schema(description: nil, &block)
100+
schemas = collect_schemas_from_block(&block)
101+
102+
{
103+
description: description,
104+
oneOf: schemas
105+
}.compact
106+
end
107+
99108
private
100109

101110
def determine_array_items(of, &)

spec/ruby_llm/schema_spec.rb

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,32 @@
154154
end
155155
end
156156
end
157+
158+
it "supports arrays of oneOf types" do
159+
schema_class.array :items do
160+
one_of :value do
161+
string :alphanumeric
162+
number :numeric
163+
end
164+
end
165+
end
166+
167+
it "supports basic oneOf with primitive types" do
168+
schema_class.one_of :status do
169+
string enum: %w[active inactive]
170+
integer
171+
boolean
172+
end
173+
174+
properties = schema_class.properties
175+
one_of_schemas = properties[:status][:oneOf]
176+
177+
expect(one_of_schemas).to include(
178+
{type: "string", enum: %w[active inactive]},
179+
{type: "integer"},
180+
{type: "boolean"}
181+
)
182+
end
157183
end
158184

159185
# ===========================================
@@ -221,6 +247,29 @@
221247
expect(object_schema[:properties][:nested_field]).to eq({type: "string"})
222248
end
223249

250+
it "supports one_of with mixed types including objects" do
251+
schema_class.one_of :exclusive_field do
252+
string enum: %w[option1 option2]
253+
integer
254+
object do
255+
string :nested_field
256+
end
257+
null
258+
end
259+
260+
properties = schema_class.properties
261+
one_of_schemas = properties[:exclusive_field][:oneOf]
262+
263+
expect(one_of_schemas).to include(
264+
{type: "string", enum: %w[option1 option2]},
265+
{type: "integer"},
266+
{type: "null"}
267+
)
268+
269+
object_schema = one_of_schemas.find { |s| s[:type] == "object" }
270+
expect(object_schema[:properties][:nested_field]).to eq({type: "string"})
271+
end
272+
224273
it "supports reference to a defined schema by block" do
225274
schema_class.define :address do
226275
string :street
@@ -368,7 +417,7 @@
368417
it "supports method delegation for schema methods" do
369418
instance = schema_class.new
370419

371-
expect(instance).to respond_to(:string, :number, :integer, :boolean, :array, :object, :any_of, :null)
420+
expect(instance).to respond_to(:string, :number, :integer, :boolean, :array, :object, :any_of, :one_of, :null)
372421
expect(instance).not_to respond_to(:unknown_method)
373422
end
374423

0 commit comments

Comments
 (0)