jev-feels 0.2.0 → 1.1.0
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/CHANGELOG.md +25 -0
- data/README.md +280 -78
- data/context7.json +1 -1
- data/lib/feels/active_model.rb +5 -0
- data/lib/feels/string.rb +12 -0
- data/lib/jev/active_model.rb +30 -0
- data/lib/jev/client.rb +16 -20
- data/lib/jev/definition.rb +91 -0
- data/lib/jev/errors.rb +1 -0
- data/lib/jev/feels.rb +12 -0
- data/lib/jev/harness.rb +215 -0
- data/lib/jev/match.rb +28 -0
- data/lib/jev/model.rb +111 -0
- data/lib/jev/query.rb +33 -0
- data/lib/jev/registry.rb +74 -10
- data/lib/jev/result.rb +165 -0
- data/lib/jev/version.rb +1 -1
- data/lib/jev-feels/active_model.rb +3 -0
- data/lib/jev.rb +187 -37
- metadata +15 -4
data/lib/jev/result.rb
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jev
|
|
4
|
+
module Result
|
|
5
|
+
def self.parse(answer, definition)
|
|
6
|
+
raise InvalidResponseError, "Jev response is missing an answer" unless answer.is_a?(Hash)
|
|
7
|
+
|
|
8
|
+
case definition.type
|
|
9
|
+
when :noul then Noul.parse(answer)
|
|
10
|
+
when :choice then Choice.parse(answer)
|
|
11
|
+
when :score then Score.parse(answer, definition)
|
|
12
|
+
else
|
|
13
|
+
raise InvalidResponseError, "unknown Jev definition type: #{definition.type.inspect}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.finite_unit(value, label)
|
|
18
|
+
raise InvalidResponseError, "Jev response is missing #{label}" unless value.is_a?(Numeric)
|
|
19
|
+
|
|
20
|
+
value = Float(value)
|
|
21
|
+
raise InvalidResponseError, "Jev #{label} is not finite" unless value.finite?
|
|
22
|
+
|
|
23
|
+
value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Noul
|
|
27
|
+
attr_reader :probability
|
|
28
|
+
|
|
29
|
+
def initialize(probability:)
|
|
30
|
+
@probability = probability
|
|
31
|
+
freeze
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.parse(answer)
|
|
35
|
+
noul = answer["noul"]
|
|
36
|
+
raise InvalidResponseError, "Jev response is missing a noul probability" unless noul.is_a?(Numeric)
|
|
37
|
+
|
|
38
|
+
noul = Float(noul)
|
|
39
|
+
raise InvalidResponseError, "Jev noul probability is not finite" unless noul.finite?
|
|
40
|
+
|
|
41
|
+
# ponytail: clamp out-of-range noul; raise if Jev starts returning uncalibrated values
|
|
42
|
+
new(probability: noul.clamp(0.0, 1.0))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def type
|
|
46
|
+
:noul
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def collapsed(threshold: 0.5)
|
|
50
|
+
probability >= threshold
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Choice
|
|
55
|
+
attr_reader :choice, :confidence, :probabilities
|
|
56
|
+
|
|
57
|
+
def initialize(choice:, confidence:, probabilities:)
|
|
58
|
+
@choice = choice
|
|
59
|
+
@confidence = confidence
|
|
60
|
+
@probabilities = probabilities
|
|
61
|
+
freeze
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.parse(answer)
|
|
65
|
+
winner = answer["choice"]
|
|
66
|
+
raise InvalidResponseError, "Jev response is missing a choice" if winner.nil?
|
|
67
|
+
|
|
68
|
+
raw = answer["probabilities"]
|
|
69
|
+
raise InvalidResponseError, "Jev response is missing choice probabilities" unless raw.is_a?(Hash)
|
|
70
|
+
|
|
71
|
+
probabilities = raw.to_h { |key, value| [key.to_sym, Result.finite_unit(value, "choice probability")] }
|
|
72
|
+
new(
|
|
73
|
+
choice: winner.to_sym,
|
|
74
|
+
confidence: Result.finite_unit(answer["confidence"], "choice confidence").clamp(0.0, 1.0),
|
|
75
|
+
probabilities: probabilities.freeze
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def type
|
|
80
|
+
:choice
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def collapsed(*)
|
|
84
|
+
choice
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class Score
|
|
89
|
+
attr_reader :score, :confidence, :probabilities, :levels, :level
|
|
90
|
+
|
|
91
|
+
def initialize(score:, confidence:, probabilities:, levels:, level:)
|
|
92
|
+
@score = score
|
|
93
|
+
@confidence = confidence
|
|
94
|
+
@probabilities = probabilities
|
|
95
|
+
@levels = levels
|
|
96
|
+
@level = level
|
|
97
|
+
freeze
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.parse(answer, definition)
|
|
101
|
+
names = definition.level_names
|
|
102
|
+
probabilities = score_probabilities(answer["probabilities"], names)
|
|
103
|
+
score = Result.finite_unit(answer["score"], "score")
|
|
104
|
+
new(
|
|
105
|
+
score: score,
|
|
106
|
+
confidence: Result.finite_unit(answer["confidence"], "score confidence").clamp(0.0, 1.0),
|
|
107
|
+
probabilities: probabilities,
|
|
108
|
+
levels: definition.levels,
|
|
109
|
+
level: named_level(probabilities, names)
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.score_probabilities(raw, names)
|
|
114
|
+
raise InvalidResponseError, "Jev response is missing score probabilities" unless raw.is_a?(Hash)
|
|
115
|
+
|
|
116
|
+
raw.to_h do |key, value|
|
|
117
|
+
index = Integer(key)
|
|
118
|
+
[names ? names.fetch(index, index) : index, Result.finite_unit(value, "score probability")]
|
|
119
|
+
end.freeze
|
|
120
|
+
rescue ArgumentError, TypeError
|
|
121
|
+
raise InvalidResponseError, "Jev score probabilities are not keyed by level number"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def self.named_level(probabilities, names)
|
|
125
|
+
return unless names
|
|
126
|
+
|
|
127
|
+
key, = probabilities.max_by { |_, weight| weight }
|
|
128
|
+
key if key.is_a?(Symbol)
|
|
129
|
+
end
|
|
130
|
+
private_class_method :named_level
|
|
131
|
+
|
|
132
|
+
def type
|
|
133
|
+
:score
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def collapsed(*)
|
|
137
|
+
score
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class Batch
|
|
142
|
+
def initialize(results)
|
|
143
|
+
@results = results.transform_keys(&:to_sym).freeze
|
|
144
|
+
freeze
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def [](key)
|
|
148
|
+
@results[key.to_sym]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def to_h
|
|
152
|
+
@results.transform_values(&:collapsed)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def deconstruct_keys(keys)
|
|
156
|
+
collapsed = to_h
|
|
157
|
+
keys ? collapsed.slice(*keys.map(&:to_sym)) : collapsed
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def type
|
|
161
|
+
:batch
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
data/lib/jev/version.rb
CHANGED
data/lib/jev.rb
CHANGED
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
require_relative "jev/version"
|
|
4
4
|
require_relative "jev/errors"
|
|
5
5
|
require_relative "jev/configuration"
|
|
6
|
+
require_relative "jev/definition"
|
|
6
7
|
require_relative "jev/registry"
|
|
8
|
+
require_relative "jev/result"
|
|
7
9
|
require_relative "jev/transport"
|
|
8
10
|
require_relative "jev/client"
|
|
11
|
+
require_relative "jev/query"
|
|
12
|
+
require_relative "jev/match"
|
|
13
|
+
require_relative "jev/harness"
|
|
9
14
|
require_relative "jev/feels"
|
|
15
|
+
require_relative "jev/model"
|
|
10
16
|
|
|
11
17
|
module Jev
|
|
12
18
|
class << self
|
|
@@ -21,31 +27,23 @@ module Jev
|
|
|
21
27
|
@configuration = Configuration.new
|
|
22
28
|
end
|
|
23
29
|
|
|
24
|
-
def define(scope_or_name, name_or_description, description = nil)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
registry.define(scope_or_name, name_or_description, description)
|
|
29
|
-
else
|
|
30
|
-
raise ArgumentError, "wrong number of arguments (given 3, expected 2)" unless description.nil?
|
|
31
|
-
|
|
32
|
-
registry.define(nil, scope_or_name, name_or_description)
|
|
33
|
-
end
|
|
30
|
+
def define(scope_or_name, name_or_description, description = nil, **options)
|
|
31
|
+
scope, name, instructions = unpack_define(scope_or_name, name_or_description, description)
|
|
32
|
+
extras = adhoc_options(options)
|
|
33
|
+
registry.define(scope, name, Definition.build(name: name, instructions: instructions, **extras))
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def definition(scope_or_name, name = nil)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
found =
|
|
38
|
+
if name.nil?
|
|
39
|
+
registry.fetch(scope_or_name, scope: nil, fallback: false, inherit: false)
|
|
40
|
+
else
|
|
41
|
+
registry.fetch(name, scope: scope_or_name, fallback: false, inherit: false)
|
|
42
|
+
end
|
|
43
|
+
found&.public_value
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def definitions(scope = nil)
|
|
47
|
-
raise ArgumentError, "scope must be a Module" unless scope.nil? || scope.is_a?(Module)
|
|
48
|
-
|
|
49
47
|
registry.all(scope)
|
|
50
48
|
end
|
|
51
49
|
|
|
@@ -54,26 +52,106 @@ module Jev
|
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
def feels(text, scope_or_predicate, predicate = nil)
|
|
57
|
-
|
|
58
|
-
Client.new(configuration).probability(coerce_text(text), resolve(predicate, scope: scope))
|
|
55
|
+
measure_one(text, scope_or_predicate, predicate, id: Client::QUESTION_ID).probability
|
|
59
56
|
end
|
|
60
57
|
|
|
61
|
-
def feels?(text, scope_or_predicate, predicate = nil,
|
|
62
|
-
|
|
58
|
+
def feels?(text, scope_or_predicate, predicate = nil, **opts)
|
|
59
|
+
validate_noul_decision!(opts)
|
|
60
|
+
decide_noul(feels(text, scope_or_predicate, predicate), opts)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def decide(text, scope_or_predicate, predicate = nil, confidence: nil, **options)
|
|
64
|
+
require_adhoc_type(scope_or_predicate, predicate, options, :choices)
|
|
65
|
+
confidence = normalize_unit(confidence, "confidence") unless confidence.nil?
|
|
66
|
+
result = typed_measure(:choice, text, scope_or_predicate, predicate, **options)
|
|
67
|
+
return if confidence && result.confidence < confidence
|
|
68
|
+
|
|
69
|
+
result.choice
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def score(text, scope_or_predicate, predicate = nil, **options)
|
|
73
|
+
require_adhoc_type(scope_or_predicate, predicate, options, :levels)
|
|
74
|
+
typed_measure(:score, text, scope_or_predicate, predicate, **options).score
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def measure(text, scope_or_predicate = nil, predicate = nil, **options, &block)
|
|
78
|
+
if block
|
|
79
|
+
raise ArgumentError, "unknown keyword: #{options.keys.first}" unless options.empty?
|
|
80
|
+
raise ArgumentError, "predicate is not used with a measure block" unless predicate.nil?
|
|
81
|
+
|
|
82
|
+
measure_batch(text, scope_or_predicate, &block)
|
|
83
|
+
else
|
|
84
|
+
raise ArgumentError, "question is required" if scope_or_predicate.nil?
|
|
85
|
+
|
|
86
|
+
measure_one(text, scope_or_predicate, predicate, **options)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def match(text, scope_or_predicate, predicate = nil, confidence: nil, **, &block)
|
|
91
|
+
raise ArgumentError, "Jev.match requires a block" unless block
|
|
92
|
+
|
|
93
|
+
matcher = Matcher.new(decide(text, scope_or_predicate, predicate, confidence: confidence, **))
|
|
94
|
+
block.arity.zero? ? matcher.instance_eval(&block) : yield(matcher)
|
|
95
|
+
matcher.result
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def stub(answers, &)
|
|
99
|
+
Harness.stub(answers, &)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def record(&)
|
|
103
|
+
Harness.record(&)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def replay(tape, &)
|
|
107
|
+
Harness.replay(tape, &)
|
|
63
108
|
end
|
|
64
109
|
|
|
65
110
|
def normalize_threshold(value)
|
|
66
|
-
|
|
111
|
+
normalize_unit(value, "threshold")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def resolve_definition(predicate, scope: nil, **options)
|
|
115
|
+
case predicate
|
|
116
|
+
when Symbol
|
|
117
|
+
raise ArgumentError, "unknown keyword: #{options.keys.first}" unless options.empty?
|
|
118
|
+
|
|
119
|
+
registry.fetch(predicate, scope: scope) ||
|
|
120
|
+
raise(UndefinedDefinition, undefined_message(predicate, scope))
|
|
121
|
+
when String
|
|
122
|
+
Definition.build(name: :adhoc, instructions: predicate, **adhoc_options(options))
|
|
123
|
+
else
|
|
124
|
+
raise ArgumentError, "predicate must be a Symbol or String"
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def name_for_instructions(instructions)
|
|
129
|
+
registry.name_for_instructions(instructions)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def level_names_for(instructions)
|
|
133
|
+
registry.find_by_instructions(instructions)&.level_names
|
|
134
|
+
end
|
|
135
|
+
private :resolve_definition, :name_for_instructions, :level_names_for
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
def normalize_unit(value, name)
|
|
140
|
+
raise ArgumentError, "#{name} must be a Float between 0.0 and 1.0" unless value.is_a?(Numeric)
|
|
67
141
|
|
|
68
142
|
value = Float(value)
|
|
69
143
|
unless value.finite? && value.between?(0.0, 1.0)
|
|
70
|
-
raise ArgumentError, "
|
|
144
|
+
raise ArgumentError, "#{name} must be a Float between 0.0 and 1.0"
|
|
71
145
|
end
|
|
72
146
|
|
|
73
147
|
value
|
|
74
148
|
end
|
|
75
149
|
|
|
76
|
-
|
|
150
|
+
def require_adhoc_type(scope_or_predicate, predicate, options, key)
|
|
151
|
+
_scope, name = unpack_predicate(scope_or_predicate, predicate)
|
|
152
|
+
return unless name.is_a?(String)
|
|
153
|
+
raise ArgumentError, "#{key} is required" unless options.key?(key)
|
|
154
|
+
end
|
|
77
155
|
|
|
78
156
|
attr_reader :registry
|
|
79
157
|
|
|
@@ -86,21 +164,13 @@ module Jev
|
|
|
86
164
|
|
|
87
165
|
def unpack_predicate(scope_or_predicate, predicate)
|
|
88
166
|
return [nil, scope_or_predicate] if predicate.nil?
|
|
89
|
-
raise ArgumentError, "scope must be a Module" unless
|
|
167
|
+
raise ArgumentError, "scope must be a Module, String, or Symbol" unless scoped?(scope_or_predicate)
|
|
90
168
|
|
|
91
169
|
[scope_or_predicate, predicate]
|
|
92
170
|
end
|
|
93
171
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
when Symbol
|
|
97
|
-
registry.fetch(predicate, scope: scope) ||
|
|
98
|
-
raise(UndefinedDefinition, undefined_message(predicate, scope))
|
|
99
|
-
when String
|
|
100
|
-
predicate
|
|
101
|
-
else
|
|
102
|
-
raise ArgumentError, "predicate must be a Symbol or String"
|
|
103
|
-
end
|
|
172
|
+
def scoped?(value)
|
|
173
|
+
value.is_a?(Module) || value.is_a?(String) || value.is_a?(Symbol)
|
|
104
174
|
end
|
|
105
175
|
|
|
106
176
|
def undefined_message(predicate, scope)
|
|
@@ -110,8 +180,88 @@ module Jev
|
|
|
110
180
|
"Undefined Jev definition: #{predicate.inspect}"
|
|
111
181
|
end
|
|
112
182
|
end
|
|
183
|
+
|
|
184
|
+
def unpack_define(scope_or_name, name_or_description, description)
|
|
185
|
+
if description.nil?
|
|
186
|
+
if scope_or_name.is_a?(Module) || name_or_description.is_a?(Symbol)
|
|
187
|
+
raise ArgumentError, "description is required"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
[nil, scope_or_name, name_or_description]
|
|
191
|
+
else
|
|
192
|
+
[scope_or_name, name_or_description, description]
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def adhoc_options(options)
|
|
197
|
+
choices = options.delete(:choices)
|
|
198
|
+
levels = options.delete(:levels)
|
|
199
|
+
raise ArgumentError, "unknown keyword: #{options.keys.first}" unless options.empty?
|
|
200
|
+
|
|
201
|
+
{ choices: choices, levels: levels }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def measure_one(text, scope_or_predicate, predicate, id: nil, **)
|
|
205
|
+
scope, predicate = unpack_predicate(scope_or_predicate, predicate)
|
|
206
|
+
definition = resolve_definition(predicate, scope: scope, **)
|
|
207
|
+
question_id = id || (predicate.is_a?(Symbol) ? predicate.to_s : definition.type.to_s)
|
|
208
|
+
Client.new(configuration).ask(coerce_text(text), { question_id => definition }).fetch(question_id)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def typed_measure(type, text, scope_or_predicate, predicate, **)
|
|
212
|
+
result = measure_one(text, scope_or_predicate, predicate, **)
|
|
213
|
+
return result if result.type == type
|
|
214
|
+
|
|
215
|
+
label = predicate_label(scope_or_predicate, predicate)
|
|
216
|
+
raise ArgumentError, "#{label} is a #{result.type} definition, not a #{type}"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def predicate_label(scope_or_predicate, predicate)
|
|
220
|
+
(_scope, name) = unpack_predicate(scope_or_predicate, predicate)
|
|
221
|
+
name.inspect
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def measure_batch(text, scope, &)
|
|
225
|
+
questions = collect_questions(scope, &)
|
|
226
|
+
answers = Client.new(configuration).ask(coerce_text(text), questions.transform_keys(&:to_s))
|
|
227
|
+
Result::Batch.new(questions.keys.to_h { |key| [key, answers.fetch(key.to_s)] })
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def collect_questions(scope)
|
|
231
|
+
query = Query.new(scope)
|
|
232
|
+
yield query
|
|
233
|
+
raise ArgumentError, "measure block must declare at least one question" if query.questions.empty?
|
|
234
|
+
|
|
235
|
+
query.questions
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def validate_noul_decision!(opts)
|
|
239
|
+
unknown = opts.keys - %i[threshold at_least]
|
|
240
|
+
raise ArgumentError, "unknown keyword: #{unknown.first}" unless unknown.empty?
|
|
241
|
+
if opts.key?(:at_least) && opts.key?(:threshold)
|
|
242
|
+
raise ArgumentError,
|
|
243
|
+
"cannot use threshold: and at_least: together"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
normalize_unit(opts[:at_least], "at_least") if opts.key?(:at_least)
|
|
247
|
+
normalize_threshold(opts[:threshold]) if opts.key?(:threshold)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def decide_noul(probability, opts)
|
|
251
|
+
if opts.key?(:at_least)
|
|
252
|
+
at_least = normalize_unit(opts[:at_least], "at_least")
|
|
253
|
+
return true if probability >= at_least
|
|
254
|
+
return false if probability + at_least <= 1.0
|
|
255
|
+
|
|
256
|
+
nil
|
|
257
|
+
else
|
|
258
|
+
probability >= normalize_threshold(opts.fetch(:threshold, configuration.threshold))
|
|
259
|
+
end
|
|
260
|
+
end
|
|
113
261
|
end
|
|
114
262
|
|
|
263
|
+
private_constant :Query, :Matcher, :Harness
|
|
264
|
+
|
|
115
265
|
reset_configuration!
|
|
116
266
|
@registry = Registry.new
|
|
117
267
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jev-feels
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maxim Veysgeym
|
|
@@ -11,27 +11,38 @@ cert_chain: []
|
|
|
11
11
|
date: 2026-09-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
|
-
Tiny Ruby API for semantic text
|
|
15
|
-
|
|
14
|
+
Tiny Ruby API for semantic text decisions. Jev.feels?(:urgent), Jev.decide,
|
|
15
|
+
and Jev.score are ordinary Ruby, not an SDK session. Jev stays an
|
|
16
|
+
implementation detail.
|
|
16
17
|
email:
|
|
17
18
|
- Qew7@users.noreply.github.com
|
|
18
19
|
executables: []
|
|
19
20
|
extensions: []
|
|
20
21
|
extra_rdoc_files: []
|
|
21
22
|
files:
|
|
23
|
+
- CHANGELOG.md
|
|
22
24
|
- LICENSE
|
|
23
25
|
- README.md
|
|
24
26
|
- context7.json
|
|
25
27
|
- lib/feels.rb
|
|
28
|
+
- lib/feels/active_model.rb
|
|
26
29
|
- lib/feels/string.rb
|
|
27
30
|
- lib/jev-feels.rb
|
|
31
|
+
- lib/jev-feels/active_model.rb
|
|
28
32
|
- lib/jev-feels/string.rb
|
|
29
33
|
- lib/jev.rb
|
|
34
|
+
- lib/jev/active_model.rb
|
|
30
35
|
- lib/jev/client.rb
|
|
31
36
|
- lib/jev/configuration.rb
|
|
37
|
+
- lib/jev/definition.rb
|
|
32
38
|
- lib/jev/errors.rb
|
|
33
39
|
- lib/jev/feels.rb
|
|
40
|
+
- lib/jev/harness.rb
|
|
41
|
+
- lib/jev/match.rb
|
|
42
|
+
- lib/jev/model.rb
|
|
43
|
+
- lib/jev/query.rb
|
|
34
44
|
- lib/jev/registry.rb
|
|
45
|
+
- lib/jev/result.rb
|
|
35
46
|
- lib/jev/transport.rb
|
|
36
47
|
- lib/jev/version.rb
|
|
37
48
|
homepage: https://github.com/Qew7/jev-feels
|
|
@@ -40,7 +51,7 @@ licenses:
|
|
|
40
51
|
metadata:
|
|
41
52
|
homepage_uri: https://github.com/Qew7/jev-feels
|
|
42
53
|
source_code_uri: https://github.com/Qew7/jev-feels
|
|
43
|
-
changelog_uri: https://github.com/Qew7/jev-feels/blob/
|
|
54
|
+
changelog_uri: https://github.com/Qew7/jev-feels/blob/master/CHANGELOG.md
|
|
44
55
|
allowed_push_host: https://rubygems.org
|
|
45
56
|
post_install_message:
|
|
46
57
|
rdoc_options: []
|