easy_talk 3.2.0 → 3.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 +4 -4
- data/.rubocop.yml +15 -43
- data/CHANGELOG.md +105 -0
- data/README.md +510 -2018
- data/docs/json_schema_compliance.md +140 -26
- data/docs/primitive-schema-rfc.md +894 -0
- data/examples/ruby_llm/Gemfile +12 -0
- data/examples/ruby_llm/structured_output.rb +47 -0
- data/examples/ruby_llm/tools_integration.rb +49 -0
- data/lib/easy_talk/builders/base_builder.rb +2 -1
- data/lib/easy_talk/builders/boolean_builder.rb +2 -1
- data/lib/easy_talk/builders/collection_helpers.rb +4 -0
- data/lib/easy_talk/builders/composition_builder.rb +7 -2
- data/lib/easy_talk/builders/integer_builder.rb +2 -1
- data/lib/easy_talk/builders/null_builder.rb +4 -1
- data/lib/easy_talk/builders/number_builder.rb +4 -1
- data/lib/easy_talk/builders/object_builder.rb +64 -3
- data/lib/easy_talk/builders/registry.rb +15 -1
- data/lib/easy_talk/builders/string_builder.rb +3 -1
- data/lib/easy_talk/builders/temporal_builder.rb +7 -0
- data/lib/easy_talk/builders/tuple_builder.rb +89 -0
- data/lib/easy_talk/builders/typed_array_builder.rb +4 -2
- data/lib/easy_talk/builders/union_builder.rb +5 -1
- data/lib/easy_talk/configuration.rb +17 -2
- data/lib/easy_talk/errors.rb +1 -0
- data/lib/easy_talk/errors_helper.rb +3 -0
- data/lib/easy_talk/extensions/ruby_llm_compatibility.rb +58 -0
- data/lib/easy_talk/json_schema_equality.rb +46 -0
- data/lib/easy_talk/keywords.rb +0 -1
- data/lib/easy_talk/model.rb +42 -1
- data/lib/easy_talk/model_helper.rb +4 -0
- data/lib/easy_talk/naming_strategies.rb +4 -0
- data/lib/easy_talk/property.rb +7 -0
- data/lib/easy_talk/ref_helper.rb +6 -0
- data/lib/easy_talk/schema.rb +1 -0
- data/lib/easy_talk/schema_definition.rb +52 -6
- data/lib/easy_talk/schema_methods.rb +36 -5
- data/lib/easy_talk/sorbet_extension.rb +1 -0
- data/lib/easy_talk/type_introspection.rb +45 -1
- data/lib/easy_talk/types/tuple.rb +77 -0
- data/lib/easy_talk/validation_adapters/active_model_adapter.rb +350 -62
- data/lib/easy_talk/validation_adapters/active_model_schema_validation.rb +106 -0
- data/lib/easy_talk/validation_adapters/base.rb +12 -0
- data/lib/easy_talk/validation_adapters/none_adapter.rb +9 -0
- data/lib/easy_talk/validation_builder.rb +1 -0
- data/lib/easy_talk/version.rb +1 -1
- data/lib/easy_talk.rb +1 -0
- metadata +17 -4
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
2
3
|
|
|
3
4
|
require 'bigdecimal'
|
|
4
5
|
|
|
@@ -31,6 +32,8 @@ module EasyTalk
|
|
|
31
32
|
}.freeze
|
|
32
33
|
|
|
33
34
|
class << self
|
|
35
|
+
extend T::Sig
|
|
36
|
+
|
|
34
37
|
# Check if type represents a boolean (T::Boolean or TrueClass/FalseClass).
|
|
35
38
|
#
|
|
36
39
|
# @param type [Object] The type to check
|
|
@@ -41,6 +44,7 @@ module EasyTalk
|
|
|
41
44
|
# boolean_type?(TrueClass) # => true
|
|
42
45
|
# boolean_type?(FalseClass) # => true
|
|
43
46
|
# boolean_type?(String) # => false
|
|
47
|
+
sig { params(type: T.untyped).returns(T::Boolean) }
|
|
44
48
|
def boolean_type?(type)
|
|
45
49
|
return false if type.nil?
|
|
46
50
|
return true if [TrueClass, FalseClass].include?(type)
|
|
@@ -58,6 +62,23 @@ module EasyTalk
|
|
|
58
62
|
false
|
|
59
63
|
end
|
|
60
64
|
|
|
65
|
+
# Check if a resolved type class represents a boolean union ([TrueClass, FalseClass]).
|
|
66
|
+
#
|
|
67
|
+
# This is useful when checking resolved type classes rather than raw Sorbet types.
|
|
68
|
+
# The internal representation of T::Boolean resolves to [TrueClass, FalseClass].
|
|
69
|
+
#
|
|
70
|
+
# @param type_class [Object] The resolved type class to check
|
|
71
|
+
# @return [Boolean] true if the type class is a boolean union array
|
|
72
|
+
#
|
|
73
|
+
# @example
|
|
74
|
+
# boolean_union_type?([TrueClass, FalseClass]) # => true
|
|
75
|
+
# boolean_union_type?(TrueClass) # => false
|
|
76
|
+
# boolean_union_type?(String) # => false
|
|
77
|
+
sig { params(type_class: T.untyped).returns(T::Boolean) }
|
|
78
|
+
def boolean_union_type?(type_class)
|
|
79
|
+
type_class.is_a?(Array) && type_class.sort_by(&:name) == [FalseClass, TrueClass].sort_by(&:name)
|
|
80
|
+
end
|
|
81
|
+
|
|
61
82
|
# Check if type is a typed array (T::Array[...]).
|
|
62
83
|
#
|
|
63
84
|
# @param type [Object] The type to check
|
|
@@ -66,12 +87,30 @@ module EasyTalk
|
|
|
66
87
|
# @example
|
|
67
88
|
# typed_array?(T::Array[String]) # => true
|
|
68
89
|
# typed_array?(Array) # => false
|
|
90
|
+
sig { params(type: T.untyped).returns(T::Boolean) }
|
|
69
91
|
def typed_array?(type)
|
|
70
92
|
return false if type.nil?
|
|
71
93
|
|
|
72
94
|
type.is_a?(T::Types::TypedArray)
|
|
73
95
|
end
|
|
74
96
|
|
|
97
|
+
# Check if type is any array type (plain Array, T::Array[...], or T::Tuple[...]).
|
|
98
|
+
#
|
|
99
|
+
# @param type [Object] The type to check
|
|
100
|
+
# @return [Boolean] true if the type is an array type
|
|
101
|
+
#
|
|
102
|
+
# @example
|
|
103
|
+
# array_type?(Array) # => true
|
|
104
|
+
# array_type?(T::Array[String]) # => true
|
|
105
|
+
# array_type?(T::Tuple[String, Integer]) # => true
|
|
106
|
+
# array_type?(String) # => false
|
|
107
|
+
sig { params(type: T.untyped).returns(T::Boolean) }
|
|
108
|
+
def array_type?(type)
|
|
109
|
+
return false if type.nil?
|
|
110
|
+
|
|
111
|
+
type == Array || type.is_a?(T::Types::TypedArray) || type.is_a?(EasyTalk::Types::Tuple)
|
|
112
|
+
end
|
|
113
|
+
|
|
75
114
|
# Check if type is nilable (T.nilable(...)).
|
|
76
115
|
#
|
|
77
116
|
# @param type [Object] The type to check
|
|
@@ -80,6 +119,7 @@ module EasyTalk
|
|
|
80
119
|
# @example
|
|
81
120
|
# nilable_type?(T.nilable(String)) # => true
|
|
82
121
|
# nilable_type?(String) # => false
|
|
122
|
+
sig { params(type: T.untyped).returns(T::Boolean) }
|
|
83
123
|
def nilable_type?(type)
|
|
84
124
|
return false if type.nil?
|
|
85
125
|
|
|
@@ -90,6 +130,7 @@ module EasyTalk
|
|
|
90
130
|
#
|
|
91
131
|
# @param type [Object] The type to check
|
|
92
132
|
# @return [Boolean] true if the type is a primitive
|
|
133
|
+
sig { params(type: T.untyped).returns(T::Boolean) }
|
|
93
134
|
def primitive_type?(type)
|
|
94
135
|
return false if type.nil?
|
|
95
136
|
|
|
@@ -111,6 +152,7 @@ module EasyTalk
|
|
|
111
152
|
# json_schema_type(Float) # => 'number'
|
|
112
153
|
# json_schema_type(BigDecimal) # => 'number'
|
|
113
154
|
# json_schema_type(String) # => 'string'
|
|
155
|
+
sig { params(type: T.untyped).returns(String) }
|
|
114
156
|
def json_schema_type(type)
|
|
115
157
|
return 'object' if type.nil?
|
|
116
158
|
return 'boolean' if boolean_type?(type)
|
|
@@ -133,11 +175,12 @@ module EasyTalk
|
|
|
133
175
|
# get_type_class(String) # => String
|
|
134
176
|
# get_type_class(T::Boolean) # => [TrueClass, FalseClass]
|
|
135
177
|
# get_type_class(T::Array[String]) # => Array
|
|
178
|
+
sig { params(type: T.untyped).returns(T.untyped) }
|
|
136
179
|
def get_type_class(type)
|
|
137
180
|
return nil if type.nil?
|
|
138
181
|
return type if type.is_a?(Class)
|
|
139
182
|
return type.raw_type if type.respond_to?(:raw_type)
|
|
140
|
-
return Array if
|
|
183
|
+
return Array if type.is_a?(T::Types::TypedArray) || type.is_a?(EasyTalk::Types::Tuple)
|
|
141
184
|
return [TrueClass, FalseClass] if boolean_type?(type)
|
|
142
185
|
|
|
143
186
|
if nilable_type?(type)
|
|
@@ -155,6 +198,7 @@ module EasyTalk
|
|
|
155
198
|
#
|
|
156
199
|
# @example
|
|
157
200
|
# extract_inner_type(T.nilable(String)) # => String
|
|
201
|
+
sig { params(type: T.untyped).returns(T.untyped) }
|
|
158
202
|
def extract_inner_type(type)
|
|
159
203
|
return type if type.nil?
|
|
160
204
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasyTalk
|
|
4
|
+
module Types
|
|
5
|
+
# Represents a tuple type for arrays with positional type validation.
|
|
6
|
+
#
|
|
7
|
+
# A tuple is an array where each position has a specific type. This class
|
|
8
|
+
# stores the types for each position.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic tuple
|
|
11
|
+
# T::Tuple[String, Integer] # First item must be String, second must be Integer
|
|
12
|
+
#
|
|
13
|
+
# @example With additional_items constraint
|
|
14
|
+
# property :flags, T::Tuple[T::Boolean, T::Boolean], additional_items: false
|
|
15
|
+
#
|
|
16
|
+
class Tuple
|
|
17
|
+
extend T::Sig
|
|
18
|
+
|
|
19
|
+
# @return [Array<Object>] The types for each position in the tuple
|
|
20
|
+
sig { returns(T::Array[T.untyped]) }
|
|
21
|
+
attr_reader :types
|
|
22
|
+
|
|
23
|
+
# Creates a new Tuple instance with the given positional types.
|
|
24
|
+
#
|
|
25
|
+
# @param types [Array] The types for each position in the tuple
|
|
26
|
+
# @raise [ArgumentError] if types is empty or contains nil values
|
|
27
|
+
sig { params(types: T.untyped).void }
|
|
28
|
+
def initialize(*types)
|
|
29
|
+
raise ArgumentError, 'Tuple requires at least one type' if types.empty?
|
|
30
|
+
raise ArgumentError, 'Tuple types cannot be nil' if types.any?(&:nil?)
|
|
31
|
+
|
|
32
|
+
@types = types.freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns a string representation of the tuple type.
|
|
36
|
+
#
|
|
37
|
+
# @return [String] A human-readable representation
|
|
38
|
+
sig { returns(String) }
|
|
39
|
+
def to_s
|
|
40
|
+
type_names = @types.map { |t| (t.respond_to?(:name) && t.name) || t.to_s }
|
|
41
|
+
"T::Tuple[#{type_names.join(', ')}]"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the name of this type (used by Property for error messages).
|
|
45
|
+
#
|
|
46
|
+
# @return [String] The type name
|
|
47
|
+
sig { returns(String) }
|
|
48
|
+
def name
|
|
49
|
+
to_s
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Add T::Tuple module for bracket syntax
|
|
56
|
+
module T
|
|
57
|
+
# Provides tuple type syntax: T::Tuple[Type1, Type2, ...]
|
|
58
|
+
#
|
|
59
|
+
# Creates a tuple type that validates array elements by position.
|
|
60
|
+
#
|
|
61
|
+
# @example Basic usage
|
|
62
|
+
# property :coordinates, T::Tuple[Float, Float]
|
|
63
|
+
# property :record, T::Tuple[String, Integer, T::Boolean]
|
|
64
|
+
#
|
|
65
|
+
# @example With additional_items constraint
|
|
66
|
+
# property :flags, T::Tuple[T::Boolean, T::Boolean], additional_items: false
|
|
67
|
+
#
|
|
68
|
+
module Tuple
|
|
69
|
+
# Creates a new Tuple type with the given positional types.
|
|
70
|
+
#
|
|
71
|
+
# @param types [Array] The types for each position
|
|
72
|
+
# @return [EasyTalk::Types::Tuple] A new Tuple instance
|
|
73
|
+
def self.[](*types)
|
|
74
|
+
EasyTalk::Types::Tuple.new(*types)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|