Skip to content

Commit 4975a5b

Browse files
committed
Refactor DSL
1 parent 6aca707 commit 4975a5b

File tree

6 files changed

+227
-196
lines changed

6 files changed

+227
-196
lines changed

lib/ruby_llm/schema/dsl.rb

Lines changed: 10 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -1,198 +1,17 @@
11
# frozen_string_literal: true
22

3+
require_relative "dsl/schema_builders"
4+
require_relative "dsl/primitive_types"
5+
require_relative "dsl/complex_types"
6+
require_relative "dsl/utilities"
7+
38
module RubyLLM
49
class Schema
510
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-
additionalProperties: sub_schema.additional_properties
86-
}
87-
end
88-
89-
def reference(schema_name)
90-
if schema_name == :root
91-
{"$ref" => "#"}
92-
else
93-
{"$ref" => "#/$defs/#{schema_name}"}
94-
end
95-
end
96-
97-
# Schema building methods
98-
def build_property_schema(type, **options, &)
99-
case type
100-
when :string
101-
{
102-
type: "string",
103-
enum: options[:enum],
104-
description: options[:description],
105-
minLength: options[:minLength],
106-
maxLength: options[:maxLength],
107-
pattern: options[:pattern],
108-
format: options[:format]
109-
}.compact
110-
when :number
111-
{
112-
type: "number",
113-
description: options[:description],
114-
minimum: options[:minimum],
115-
maximum: options[:maximum],
116-
multipleOf: options[:multipleOf]
117-
}.compact
118-
when :integer
119-
{
120-
type: "integer",
121-
description: options[:description],
122-
minimum: options[:minimum],
123-
maximum: options[:maximum],
124-
multipleOf: options[:multipleOf]
125-
}.compact
126-
when :boolean
127-
{type: "boolean", description: options[:description]}.compact
128-
when :null
129-
{type: "null", description: options[:description]}.compact
130-
when :object
131-
# If the reference option is provided, return the reference
132-
return reference(options[:reference]) if options[:reference]
133-
134-
sub_schema = Class.new(Schema)
135-
136-
# Evaluate the block and capture the result
137-
result = sub_schema.class_eval(&)
138-
139-
# If the block returned a reference and no properties were added, use the reference
140-
if result.is_a?(Hash) && result["$ref"] && sub_schema.properties.empty?
141-
result.merge(options[:description] ? {description: options[:description]} : {})
142-
else
143-
{
144-
type: "object",
145-
properties: sub_schema.properties,
146-
required: sub_schema.required_properties,
147-
additionalProperties: sub_schema.additional_properties,
148-
description: options[:description]
149-
}.compact
150-
end
151-
when :any_of
152-
schemas = collect_property_schemas_from_block(&)
153-
{
154-
anyOf: schemas
155-
}.compact
156-
else
157-
raise InvalidSchemaTypeError, type
158-
end
159-
end
160-
161-
private
162-
163-
def add_property(name, definition, required:)
164-
properties[name.to_sym] = definition
165-
required_properties << name.to_sym if required
166-
end
167-
168-
def determine_array_items(of, &)
169-
return collect_property_schemas_from_block(&).first if block_given?
170-
return build_property_schema(of) if primitive_type?(of)
171-
return reference(of) if of.is_a?(Symbol)
172-
173-
raise InvalidArrayTypeError, of
174-
end
175-
176-
def collect_property_schemas_from_block(&block)
177-
schemas = []
178-
schema_builder = self # Capture the current context that has build_property_schema
179-
180-
context = Object.new
181-
context.define_singleton_method(:string) { |name = nil, **options| schemas << schema_builder.build_property_schema(:string, **options) }
182-
context.define_singleton_method(:number) { |name = nil, **options| schemas << schema_builder.build_property_schema(:number, **options) }
183-
context.define_singleton_method(:integer) { |name = nil, **options| schemas << schema_builder.build_property_schema(:integer, **options) }
184-
context.define_singleton_method(:boolean) { |name = nil, **options| schemas << schema_builder.build_property_schema(:boolean, **options) }
185-
context.define_singleton_method(:null) { |name = nil, **options| schemas << schema_builder.build_property_schema(:null, **options) }
186-
context.define_singleton_method(:object) { |name = nil, **options, &blk| schemas << schema_builder.build_property_schema(:object, **options, &blk) }
187-
context.define_singleton_method(:any_of) { |name = nil, **options, &blk| schemas << schema_builder.build_property_schema(:any_of, **options, &blk) }
188-
189-
context.instance_eval(&block)
190-
schemas
191-
end
192-
193-
def primitive_type?(type)
194-
type.is_a?(Symbol) && PRIMITIVE_TYPES.include?(type)
195-
end
11+
include SchemaBuilders
12+
include PrimitiveTypes
13+
include ComplexTypes
14+
include Utilities
19615
end
19716
end
198-
end
17+
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
module RubyLLM
4+
class Schema
5+
module DSL
6+
module ComplexTypes
7+
def object(name, required: true, **options, &block)
8+
add_property(name, object_schema(**options, &block), required: required)
9+
end
10+
11+
def array(name, required: true, **options, &block)
12+
add_property(name, array_schema(**options, &block), required: required)
13+
end
14+
15+
def any_of(name, required: true, **options, &block)
16+
add_property(name, any_of_schema(**options, &block), required: required)
17+
end
18+
19+
def optional(name, **options, &block)
20+
any_of(name, **options) do
21+
instance_eval(&block)
22+
null
23+
end
24+
end
25+
end
26+
end
27+
end
28+
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# frozen_string_literal: true
2+
3+
module RubyLLM
4+
class Schema
5+
module DSL
6+
module PrimitiveTypes
7+
def string(name, required: true, **options)
8+
add_property(name, string_schema(**options), required: required)
9+
end
10+
11+
def number(name, required: true, **options)
12+
add_property(name, number_schema(**options), required: required)
13+
end
14+
15+
def integer(name, required: true, **options)
16+
add_property(name, integer_schema(**options), required: required)
17+
end
18+
19+
def boolean(name, required: true, **options)
20+
add_property(name, boolean_schema(**options), required: required)
21+
end
22+
23+
def null(name, required: true, **options)
24+
add_property(name, null_schema(**options), required: required)
25+
end
26+
end
27+
end
28+
end
29+
end
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# frozen_string_literal: true
2+
3+
module RubyLLM
4+
class Schema
5+
module DSL
6+
module SchemaBuilders
7+
def string_schema(enum: nil, description: nil, min_length: nil, max_length: nil, pattern: nil, format: nil)
8+
{
9+
type: "string",
10+
enum: enum,
11+
description: description,
12+
minLength: min_length,
13+
maxLength: max_length,
14+
pattern: pattern,
15+
format: format
16+
}.compact
17+
end
18+
19+
def number_schema(description: nil, minimum: nil, maximum: nil, multiple_of: nil)
20+
{
21+
type: "number",
22+
description: description,
23+
minimum: minimum,
24+
maximum: maximum,
25+
multipleOf: multiple_of
26+
}.compact
27+
end
28+
29+
def integer_schema(description: nil, minimum: nil, maximum: nil, multiple_of: nil)
30+
{
31+
type: "integer",
32+
description: description,
33+
minimum: minimum,
34+
maximum: maximum,
35+
multipleOf: multiple_of
36+
}.compact
37+
end
38+
39+
def boolean_schema(description: nil)
40+
{type: "boolean", description: description}.compact
41+
end
42+
43+
def null_schema(description: nil)
44+
{type: "null", description: description}.compact
45+
end
46+
47+
def object_schema(reference: nil, description: nil, &block)
48+
if reference
49+
reference(reference)
50+
else
51+
sub_schema = Class.new(Schema)
52+
result = sub_schema.class_eval(&block)
53+
54+
# If the block returned a reference and no properties were added, use the reference
55+
if result.is_a?(Hash) && result["$ref"] && sub_schema.properties.empty?
56+
result.merge(description ? {description: description} : {})
57+
else
58+
{
59+
type: "object",
60+
properties: sub_schema.properties,
61+
required: sub_schema.required_properties,
62+
additionalProperties: sub_schema.additional_properties,
63+
description: description
64+
}.compact
65+
end
66+
end
67+
end
68+
69+
def array_schema(of: nil, description: nil, min_items: nil, max_items: nil, &block)
70+
items = determine_array_items(of, &block)
71+
72+
{
73+
type: "array",
74+
description: description,
75+
items: items,
76+
minItems: min_items,
77+
maxItems: max_items
78+
}.compact
79+
end
80+
81+
def any_of_schema(description: nil, &block)
82+
schemas = collect_schemas_from_block(&block)
83+
84+
{
85+
description: description,
86+
anyOf: schemas
87+
}.compact
88+
end
89+
end
90+
end
91+
end
92+
end

0 commit comments

Comments
 (0)