lluminary 0.1.0 → 0.1.2
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/lib/lluminary/config.rb +11 -1
- data/lib/lluminary/field_description.rb +34 -29
- data/lib/lluminary/provider_error.rb +2 -1
- data/lib/lluminary/providers/base.rb +5 -1
- data/lib/lluminary/providers/bedrock.rb +32 -30
- data/lib/lluminary/providers/openai.rb +28 -21
- data/lib/lluminary/providers/test.rb +9 -14
- data/lib/lluminary/result.rb +5 -2
- data/lib/lluminary/schema.rb +9 -16
- data/lib/lluminary/schema_model.rb +13 -10
- data/lib/lluminary/task.rb +84 -59
- data/lib/lluminary/validation_error.rb +2 -1
- data/lib/lluminary/version.rb +3 -2
- data/lib/lluminary.rb +23 -7
- data/spec/examples/analyze_text_spec.rb +7 -4
- data/spec/examples/color_analyzer_spec.rb +22 -22
- data/spec/examples/content_analyzer_spec.rb +27 -44
- data/spec/examples/historical_event_analyzer_spec.rb +18 -15
- data/spec/examples/price_analyzer_spec.rb +22 -28
- data/spec/examples/quote_task_spec.rb +9 -8
- data/spec/examples/sentiment_analysis_spec.rb +13 -10
- data/spec/examples/summarize_text_spec.rb +7 -4
- data/spec/lluminary/config_spec.rb +28 -26
- data/spec/lluminary/field_description_spec.rb +19 -21
- data/spec/lluminary/providers/base_spec.rb +11 -8
- data/spec/lluminary/providers/bedrock_spec.rb +47 -57
- data/spec/lluminary/providers/openai_spec.rb +27 -35
- data/spec/lluminary/providers/test_spec.rb +21 -16
- data/spec/lluminary/result_spec.rb +17 -10
- data/spec/lluminary/schema_model_spec.rb +31 -22
- data/spec/lluminary/schema_spec.rb +95 -107
- data/spec/lluminary/task_spec.rb +366 -300
- data/spec/spec_helper.rb +7 -2
- metadata +40 -22
data/lib/lluminary/task.rb
CHANGED
@@ -1,10 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "ostruct"
|
3
|
+
require_relative "schema"
|
4
|
+
require_relative "validation_error"
|
5
|
+
require_relative "field_description"
|
6
|
+
require "json"
|
6
7
|
|
7
8
|
module Lluminary
|
9
|
+
# Base class for all Lluminary tasks.
|
10
|
+
# Provides the core functionality for defining and running LLM-powered tasks.
|
11
|
+
#
|
12
|
+
# @example Creating a custom task
|
13
|
+
# class MyTask < Lluminary::Task
|
14
|
+
# def run
|
15
|
+
# # Task implementation
|
16
|
+
# end
|
17
|
+
# end
|
8
18
|
class Task
|
9
19
|
class << self
|
10
20
|
def input_schema(&block)
|
@@ -18,19 +28,20 @@ module Lluminary
|
|
18
28
|
end
|
19
29
|
|
20
30
|
def use_provider(provider_name, **config)
|
21
|
-
provider_class =
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
provider_class =
|
32
|
+
case provider_name
|
33
|
+
when :openai
|
34
|
+
require_relative "providers/openai"
|
35
|
+
Providers::OpenAI
|
36
|
+
when :test
|
37
|
+
require_relative "providers/test"
|
38
|
+
Providers::Test
|
39
|
+
when :bedrock
|
40
|
+
require_relative "providers/bedrock"
|
41
|
+
Providers::Bedrock
|
42
|
+
else
|
43
|
+
raise ArgumentError, "Unknown provider: #{provider_name}"
|
44
|
+
end
|
34
45
|
|
35
46
|
# Merge global config with task-specific config
|
36
47
|
global_config = Lluminary.config.provider_config(provider_name)
|
@@ -48,15 +59,14 @@ module Lluminary
|
|
48
59
|
end
|
49
60
|
|
50
61
|
def provider
|
51
|
-
@provider ||=
|
52
|
-
|
53
|
-
|
54
|
-
|
62
|
+
@provider ||=
|
63
|
+
begin
|
64
|
+
require_relative "providers/test"
|
65
|
+
Providers::Test.new
|
66
|
+
end
|
55
67
|
end
|
56
68
|
|
57
|
-
|
58
|
-
@provider = provider
|
59
|
-
end
|
69
|
+
attr_writer :provider
|
60
70
|
|
61
71
|
def input_fields
|
62
72
|
@input_schema&.fields || {}
|
@@ -98,7 +108,7 @@ module Lluminary
|
|
98
108
|
validate_input!
|
99
109
|
response = self.class.provider.call(prompt, self)
|
100
110
|
process_response(response)
|
101
|
-
|
111
|
+
|
102
112
|
self
|
103
113
|
end
|
104
114
|
|
@@ -107,13 +117,12 @@ module Lluminary
|
|
107
117
|
end
|
108
118
|
|
109
119
|
def validate_input!
|
110
|
-
|
111
|
-
|
112
|
-
end
|
120
|
+
return if @input.valid?
|
121
|
+
raise ValidationError, @input.errors.full_messages.join(", ")
|
113
122
|
end
|
114
123
|
|
115
124
|
def prompt
|
116
|
-
|
125
|
+
<<~PROMPT
|
117
126
|
#{task_prompt}
|
118
127
|
|
119
128
|
#{json_schema_example}
|
@@ -134,23 +143,32 @@ module Lluminary
|
|
134
143
|
# Merge the parsed response first, then validate
|
135
144
|
if @parsed_response.is_a?(Hash)
|
136
145
|
# Get datetime fields from schema
|
137
|
-
datetime_fields =
|
146
|
+
datetime_fields =
|
147
|
+
self
|
148
|
+
.class
|
149
|
+
.output_fields
|
150
|
+
.select { |_, field| field[:type] == :datetime }
|
151
|
+
.keys
|
138
152
|
|
139
153
|
# Convert datetime fields
|
140
154
|
converted_response = @parsed_response.dup
|
141
155
|
datetime_fields.each do |field_name|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
156
|
+
unless converted_response.key?(field_name.to_s) &&
|
157
|
+
converted_response[field_name.to_s].is_a?(String)
|
158
|
+
next
|
159
|
+
end
|
160
|
+
begin
|
161
|
+
converted_response[field_name.to_s] = DateTime.parse(
|
162
|
+
converted_response[field_name.to_s]
|
163
|
+
)
|
164
|
+
rescue ArgumentError
|
165
|
+
# Leave as string, validation will fail
|
148
166
|
end
|
149
167
|
end
|
150
168
|
|
151
169
|
@output.attributes.merge!(converted_response)
|
152
170
|
end
|
153
|
-
|
171
|
+
|
154
172
|
# Validate after merging
|
155
173
|
@output.valid?
|
156
174
|
|
@@ -186,29 +204,36 @@ module Lluminary
|
|
186
204
|
end
|
187
205
|
|
188
206
|
def generate_field_descriptions
|
189
|
-
fields
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
207
|
+
fields
|
208
|
+
.map do |name, field|
|
209
|
+
# Get validations for this field
|
210
|
+
validations =
|
211
|
+
self
|
212
|
+
.class
|
213
|
+
.instance_variable_get(:@output_schema)
|
214
|
+
&.validations_for(name) || []
|
215
|
+
field_with_validations = field.merge(validations: validations)
|
216
|
+
FieldDescription.new(name, field_with_validations).to_schema_s
|
217
|
+
end
|
218
|
+
.join("\n\n")
|
195
219
|
end
|
196
220
|
|
197
221
|
def example_json
|
198
|
-
json =
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
222
|
+
json =
|
223
|
+
fields.each_with_object({}) do |(name, field), hash|
|
224
|
+
hash[name] = case field[:type]
|
225
|
+
when :string
|
226
|
+
"your #{name} here"
|
227
|
+
when :integer
|
228
|
+
0
|
229
|
+
when :datetime
|
230
|
+
"2024-01-01T12:00:00+00:00"
|
231
|
+
when :boolean
|
232
|
+
true
|
233
|
+
when :float
|
234
|
+
0.0
|
235
|
+
end
|
236
|
+
end
|
212
237
|
|
213
238
|
JSON.pretty_generate(json)
|
214
239
|
end
|
@@ -221,4 +246,4 @@ module Lluminary
|
|
221
246
|
)
|
222
247
|
end
|
223
248
|
end
|
224
|
-
end
|
249
|
+
end
|
data/lib/lluminary/version.rb
CHANGED
data/lib/lluminary.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
|
-
|
2
|
-
require_relative 'lluminary/result'
|
3
|
-
require_relative 'lluminary/task'
|
4
|
-
require_relative 'lluminary/providers/base'
|
5
|
-
require_relative 'lluminary/providers/openai'
|
6
|
-
require_relative 'lluminary/config'
|
1
|
+
# frozen_string_literal: true
|
7
2
|
|
3
|
+
require_relative "lluminary/version"
|
4
|
+
require_relative "lluminary/result"
|
5
|
+
require_relative "lluminary/task"
|
6
|
+
require_relative "lluminary/providers/base"
|
7
|
+
require_relative "lluminary/providers/openai"
|
8
|
+
require_relative "lluminary/config"
|
9
|
+
|
10
|
+
# Lluminary is a framework for building and running LLM-powered tasks.
|
11
|
+
# It provides a structured way to define tasks, their inputs and outputs,
|
12
|
+
# and handles the interaction with various LLM providers.
|
13
|
+
#
|
14
|
+
# @example Creating a simple task
|
15
|
+
# class MyTask < Lluminary::Task
|
16
|
+
# def run
|
17
|
+
# # Task implementation
|
18
|
+
# end
|
19
|
+
# end
|
8
20
|
module Lluminary
|
9
21
|
class << self
|
10
22
|
def config
|
@@ -14,5 +26,9 @@ module Lluminary
|
|
14
26
|
def configure
|
15
27
|
yield config
|
16
28
|
end
|
29
|
+
|
30
|
+
def reset_configuration
|
31
|
+
@config = Config.new
|
32
|
+
end
|
17
33
|
end
|
18
|
-
end
|
34
|
+
end
|
@@ -1,8 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/analyze_text"
|
3
4
|
|
4
5
|
RSpec.describe AnalyzeText do
|
5
|
-
let(:text) {
|
6
|
+
let(:text) { <<~TEXT }
|
7
|
+
Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write.
|
8
|
+
TEXT
|
6
9
|
|
7
10
|
it "analyzes text" do
|
8
11
|
result = described_class.call(text: text)
|
@@ -18,4 +21,4 @@ RSpec.describe AnalyzeText do
|
|
18
21
|
expect(json).to have_key("analysis")
|
19
22
|
expect(json["analysis"]).to eq(result.output.analysis)
|
20
23
|
end
|
21
|
-
end
|
24
|
+
end
|
@@ -1,42 +1,42 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/color_analyzer"
|
3
4
|
|
4
5
|
RSpec.describe ColorAnalyzer do
|
5
|
-
describe
|
6
|
+
describe "#call" do
|
6
7
|
it 'returns "red" for a description strongly suggesting the color red' do
|
7
|
-
result = described_class.call(
|
8
|
-
|
9
|
-
|
8
|
+
result = described_class.call(image_description: <<~DESCRIPTION)
|
9
|
+
A bright red sports car parked in front of a red brick building at sunset. The car's glossy red paint reflects the warm light, making it appear even more vibrant. A red stop sign stands nearby, and red roses bloom in a garden beside the building.
|
10
|
+
DESCRIPTION
|
10
11
|
|
11
12
|
expect(result.output.color_name).to eq("red")
|
12
13
|
expect(result.output.valid?).to be true
|
13
14
|
end
|
14
15
|
|
15
|
-
it
|
16
|
+
it "returns invalid output when description strongly suggests orange" do
|
16
17
|
# This should fail validation because "orange" is not a CSS Level 1 color
|
17
|
-
result = described_class.call(
|
18
|
-
|
19
|
-
|
18
|
+
result = described_class.call(image_description: <<~DESCRIPTION)
|
19
|
+
A field of ripe oranges under a bright orange sunset. The fruit glows with a warm orange hue, and the sky is painted in shades of orange and gold. Orange butterflies flutter among the trees, and orange flowers bloom throughout the scene.
|
20
|
+
DESCRIPTION
|
20
21
|
|
21
22
|
expect(result.output.valid?).to be false
|
22
|
-
expect(result.output.errors.full_messages).to include(
|
23
|
+
expect(result.output.errors.full_messages).to include(
|
24
|
+
"Color name must be a valid CSS level 1 color name"
|
25
|
+
)
|
23
26
|
end
|
24
27
|
|
25
|
-
it
|
26
|
-
expect {
|
27
|
-
|
28
|
-
|
29
|
-
)
|
30
|
-
}.to raise_error(Lluminary::ValidationError)
|
28
|
+
it "validates presence of image_description" do
|
29
|
+
expect { described_class.call!(image_description: "") }.to raise_error(
|
30
|
+
Lluminary::ValidationError
|
31
|
+
)
|
31
32
|
end
|
32
33
|
|
33
|
-
it
|
34
|
-
result =
|
35
|
-
image_description: "A bright red sports car"
|
36
|
-
)
|
34
|
+
it "validates that color_name is lowercase" do
|
35
|
+
result =
|
36
|
+
described_class.call(image_description: "A bright red sports car")
|
37
37
|
|
38
38
|
expect(result.output.valid?).to be true
|
39
39
|
expect(result.output.color_name).to eq(result.output.color_name.downcase)
|
40
40
|
end
|
41
41
|
end
|
42
|
-
end
|
42
|
+
end
|
@@ -1,75 +1,58 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/content_analyzer"
|
3
4
|
|
4
5
|
RSpec.describe ContentAnalyzer do
|
5
|
-
let(:technical_text)
|
6
|
-
<<~TEXT
|
6
|
+
let(:technical_text) { <<~TEXT }
|
7
7
|
The revolutionary new quantum processor leverages advanced photonic circuits to achieve unprecedented computational speeds.
|
8
8
|
By utilizing entangled photon pairs, it can perform complex calculations in parallel, significantly reducing processing time.
|
9
9
|
This breakthrough technology represents a major advancement in quantum computing.
|
10
10
|
TEXT
|
11
|
-
end
|
12
11
|
|
13
|
-
let(:emotional_text)
|
14
|
-
<<~TEXT
|
12
|
+
let(:emotional_text) { <<~TEXT }
|
15
13
|
I can't believe how amazing this experience was! My heart was racing with excitement as I watched the performance.
|
16
14
|
The raw emotion and passion in every movement brought tears to my eyes. It was truly a transformative moment that I'll never forget.
|
17
15
|
TEXT
|
18
|
-
end
|
19
16
|
|
20
|
-
describe
|
21
|
-
it
|
22
|
-
result =
|
23
|
-
text: technical_text,
|
24
|
-
content_type: "technical"
|
25
|
-
)
|
17
|
+
describe "#call" do
|
18
|
+
it "correctly identifies technical content" do
|
19
|
+
result =
|
20
|
+
described_class.call(text: technical_text, content_type: "technical")
|
26
21
|
|
27
22
|
expect(result.output.contains_type).to be true
|
28
23
|
end
|
29
24
|
|
30
|
-
it
|
31
|
-
result =
|
32
|
-
text: emotional_text,
|
33
|
-
content_type: "technical"
|
34
|
-
)
|
25
|
+
it "correctly identifies non-technical content" do
|
26
|
+
result =
|
27
|
+
described_class.call(text: emotional_text, content_type: "technical")
|
35
28
|
|
36
29
|
expect(result.output.contains_type).to be false
|
37
30
|
end
|
38
31
|
|
39
|
-
it
|
40
|
-
result =
|
41
|
-
text: emotional_text,
|
42
|
-
content_type: "emotional"
|
43
|
-
)
|
32
|
+
it "correctly identifies emotional content" do
|
33
|
+
result =
|
34
|
+
described_class.call(text: emotional_text, content_type: "emotional")
|
44
35
|
|
45
36
|
expect(result.output.contains_type).to be true
|
46
37
|
end
|
47
38
|
|
48
|
-
it
|
49
|
-
result =
|
50
|
-
text: technical_text,
|
51
|
-
content_type: "emotional"
|
52
|
-
)
|
39
|
+
it "correctly identifies non-emotional content" do
|
40
|
+
result =
|
41
|
+
described_class.call(text: technical_text, content_type: "emotional")
|
53
42
|
|
54
43
|
expect(result.output.contains_type).to be false
|
55
44
|
end
|
56
45
|
|
57
|
-
it
|
58
|
-
expect
|
59
|
-
described_class.call!(
|
60
|
-
|
61
|
-
content_type: "technical"
|
62
|
-
)
|
63
|
-
}.to raise_error(Lluminary::ValidationError)
|
46
|
+
it "validates presence of text" do
|
47
|
+
expect do
|
48
|
+
described_class.call!(text: "", content_type: "technical")
|
49
|
+
end.to raise_error(Lluminary::ValidationError)
|
64
50
|
end
|
65
51
|
|
66
|
-
it
|
67
|
-
expect
|
68
|
-
described_class.call!(
|
69
|
-
|
70
|
-
content_type: ""
|
71
|
-
)
|
72
|
-
}.to raise_error(Lluminary::ValidationError)
|
52
|
+
it "validates presence of content_type" do
|
53
|
+
expect do
|
54
|
+
described_class.call!(text: technical_text, content_type: "")
|
55
|
+
end.to raise_error(Lluminary::ValidationError)
|
73
56
|
end
|
74
57
|
end
|
75
|
-
end
|
58
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/historical_event_analyzer"
|
4
|
+
require "pry-byebug"
|
4
5
|
|
5
6
|
RSpec.describe HistoricalEventAnalyzer do
|
6
|
-
describe
|
7
|
-
context
|
8
|
-
it
|
9
|
-
result =
|
10
|
-
|
11
|
-
|
7
|
+
describe "#call" do
|
8
|
+
context "with events that have known exact times" do
|
9
|
+
it "returns the exact time of the human step on the moon" do
|
10
|
+
result =
|
11
|
+
described_class.call(
|
12
|
+
event_description: "Neil Armstrong's first step onto the Moon"
|
13
|
+
)
|
12
14
|
|
13
15
|
expect(result.output.valid?).to be true
|
14
16
|
expect(result.output.event_datetime).to be_a(DateTime)
|
@@ -19,11 +21,12 @@ RSpec.describe HistoricalEventAnalyzer do
|
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
|
-
context
|
23
|
-
it
|
24
|
-
result =
|
25
|
-
|
26
|
-
|
24
|
+
context "with events that have approximate times" do
|
25
|
+
it "returns midnight for the fall of the Roman Empire" do
|
26
|
+
result =
|
27
|
+
described_class.call(
|
28
|
+
event_description: "Assassination of Julius Caesar"
|
29
|
+
)
|
27
30
|
|
28
31
|
expect(result.output.valid?).to be true
|
29
32
|
expect(result.output.event_datetime).to be_a(DateTime)
|
@@ -34,4 +37,4 @@ RSpec.describe HistoricalEventAnalyzer do
|
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
37
|
-
end
|
40
|
+
end
|
@@ -1,46 +1,40 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/price_analyzer"
|
3
4
|
|
4
5
|
RSpec.describe PriceAnalyzer do
|
5
|
-
describe
|
6
|
-
it
|
7
|
-
result =
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
describe "#call" do
|
7
|
+
it "returns a competitiveness score between 0.0 and 1.0 for a high-priced item" do
|
8
|
+
result =
|
9
|
+
described_class.call(
|
10
|
+
product_name: "Entry LevelLuxury Watch",
|
11
|
+
price: 999.99
|
12
|
+
)
|
11
13
|
|
12
14
|
expect(result.output.competitiveness_score).to be_a(Float)
|
13
15
|
expect(result.output.competitiveness_score).to be_between(0.0, 1.0)
|
14
16
|
expect(result.output.competitiveness_score).to be >= 0.5 # Lower priced luxury watch is more competitive
|
15
17
|
end
|
16
18
|
|
17
|
-
it
|
18
|
-
result =
|
19
|
-
product_name: "Basic Watch",
|
20
|
-
price: 10049.99
|
21
|
-
)
|
19
|
+
it "returns a higher competitiveness score for a reasonably priced item" do
|
20
|
+
result =
|
21
|
+
described_class.call(product_name: "Basic Watch", price: 10_049.99)
|
22
22
|
|
23
23
|
expect(result.output.competitiveness_score).to be_a(Float)
|
24
24
|
expect(result.output.competitiveness_score).to be_between(0.0, 1.0)
|
25
25
|
expect(result.output.competitiveness_score).to be <= 0.5 # Higher priced basic watch is less competitive
|
26
26
|
end
|
27
27
|
|
28
|
-
it
|
29
|
-
expect
|
30
|
-
described_class.call!(
|
31
|
-
|
32
|
-
price: 49.99
|
33
|
-
)
|
34
|
-
}.to raise_error(Lluminary::ValidationError)
|
28
|
+
it "validates presence of product_name" do
|
29
|
+
expect do
|
30
|
+
described_class.call!(product_name: "", price: 49.99)
|
31
|
+
end.to raise_error(Lluminary::ValidationError)
|
35
32
|
end
|
36
33
|
|
37
|
-
it
|
38
|
-
expect
|
39
|
-
described_class.call!(
|
40
|
-
|
41
|
-
price: nil
|
42
|
-
)
|
43
|
-
}.to raise_error(Lluminary::ValidationError)
|
34
|
+
it "validates presence of price" do
|
35
|
+
expect do
|
36
|
+
described_class.call!(product_name: "Basic Watch", price: nil)
|
37
|
+
end.to raise_error(Lluminary::ValidationError)
|
44
38
|
end
|
45
39
|
end
|
46
|
-
end
|
40
|
+
end
|
@@ -1,27 +1,28 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/quote_task"
|
3
4
|
|
4
5
|
RSpec.describe QuoteTask do
|
5
|
-
describe
|
6
|
-
it
|
6
|
+
describe ".call" do
|
7
|
+
it "returns a quote and its author" do
|
7
8
|
result = described_class.call
|
8
9
|
|
9
10
|
expect(result.output.quote).to be_a(String)
|
10
11
|
expect(result.output.quote).not_to be_empty
|
11
|
-
|
12
|
+
|
12
13
|
expect(result.output.author).to be_a(String)
|
13
14
|
expect(result.output.author).not_to be_empty
|
14
15
|
end
|
15
16
|
|
16
|
-
it
|
17
|
+
it "can be called without any input parameters" do
|
17
18
|
expect { described_class.call }.not_to raise_error
|
18
19
|
end
|
19
20
|
|
20
|
-
it
|
21
|
+
it "returns a valid result object" do
|
21
22
|
result = described_class.call
|
22
23
|
expect(result).to be_a(Lluminary::Task)
|
23
24
|
expect(result.input).to be_a(Lluminary::SchemaModel)
|
24
25
|
expect(result.input.valid?).to be true
|
25
26
|
end
|
26
27
|
end
|
27
|
-
end
|
28
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/sentiment_analysis"
|
3
4
|
|
4
5
|
RSpec.describe SentimentAnalysis do
|
5
6
|
let(:text) { "I absolutely love this new feature!" }
|
6
7
|
|
7
|
-
describe
|
8
|
-
it
|
8
|
+
describe ".call" do
|
9
|
+
it "analyzes sentiment of given text" do
|
9
10
|
result = described_class.call(text: text)
|
10
|
-
|
11
|
+
|
11
12
|
expect(result.output.sentiment).to be_a(String)
|
12
13
|
expect(result.output.sentiment).not_to be_empty
|
13
14
|
expect(result.output.explanation).to be_a(String)
|
@@ -16,7 +17,7 @@ RSpec.describe SentimentAnalysis do
|
|
16
17
|
expect(result.output.confidence).to be_between(0, 100)
|
17
18
|
end
|
18
19
|
|
19
|
-
it
|
20
|
+
it "returns valid JSON response" do
|
20
21
|
result = described_class.call(text: text)
|
21
22
|
expect(result.output.raw_response).to be_a(String)
|
22
23
|
expect { JSON.parse(result.output.raw_response) }.not_to raise_error
|
@@ -29,17 +30,19 @@ RSpec.describe SentimentAnalysis do
|
|
29
30
|
expect(json["confidence"]).to eq(result.output.confidence)
|
30
31
|
end
|
31
32
|
|
32
|
-
it
|
33
|
+
it "requires text input" do
|
33
34
|
result = described_class.call({})
|
34
35
|
expect(result.valid?).to be false
|
35
|
-
expect(result.input.errors.full_messages).to include(
|
36
|
+
expect(result.input.errors.full_messages).to include(
|
37
|
+
"Text can't be blank"
|
38
|
+
)
|
36
39
|
end
|
37
40
|
|
38
|
-
it
|
41
|
+
it "returns a valid result object" do
|
39
42
|
result = described_class.call(text: text)
|
40
43
|
expect(result).to be_a(Lluminary::Task)
|
41
44
|
expect(result.input).to be_a(Lluminary::SchemaModel)
|
42
45
|
expect(result.input.valid?).to be true
|
43
46
|
end
|
44
47
|
end
|
45
|
-
end
|
48
|
+
end
|