lluminary 0.1.1 → 0.1.3
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 +6 -1
- data/lib/lluminary/models/base.rb +235 -0
- data/lib/lluminary/models/bedrock/amazon_nova_pro_v1.rb +19 -0
- data/lib/lluminary/models/bedrock/anthropic_claude_instant_v1.rb +17 -0
- data/lib/lluminary/models/bedrock/base.rb +29 -0
- data/lib/lluminary/models/openai/gpt35_turbo.rb +20 -0
- data/lib/lluminary/provider_error.rb +2 -1
- data/lib/lluminary/providers/base.rb +20 -3
- data/lib/lluminary/providers/bedrock.rb +52 -32
- data/lib/lluminary/providers/openai.rb +41 -24
- data/lib/lluminary/providers/test.rb +14 -13
- data/lib/lluminary/result.rb +5 -2
- data/lib/lluminary/schema.rb +59 -15
- data/lib/lluminary/schema_model.rb +67 -10
- data/lib/lluminary/task.rb +58 -99
- data/lib/lluminary/validation_error.rb +2 -1
- data/lib/lluminary/version.rb +3 -2
- data/lib/lluminary.rb +25 -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/meal_suggester_spec.rb +64 -0
- 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/models/base_spec.rb +581 -0
- data/spec/lluminary/models/bedrock/amazon_nova_pro_v1_spec.rb +30 -0
- data/spec/lluminary/models/bedrock/anthropic_claude_instant_v1_spec.rb +21 -0
- data/spec/lluminary/models/openai/gpt35_turbo_spec.rb +22 -0
- data/spec/lluminary/providers/bedrock_spec.rb +86 -57
- data/spec/lluminary/providers/openai_spec.rb +58 -34
- data/spec/lluminary/providers/test_spec.rb +46 -16
- data/spec/lluminary/result_spec.rb +17 -10
- data/spec/lluminary/schema_model_spec.rb +108 -22
- data/spec/lluminary/schema_spec.rb +241 -107
- data/spec/lluminary/task_spec.rb +118 -584
- data/spec/spec_helper.rb +8 -2
- metadata +73 -22
- data/lib/lluminary/field_description.rb +0 -148
- data/spec/lluminary/field_description_spec.rb +0 -36
- data/spec/lluminary/providers/base_spec.rb +0 -17
@@ -1,135 +1,279 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
2
3
|
|
3
4
|
RSpec.describe Lluminary::Schema do
|
4
5
|
let(:schema) { described_class.new }
|
5
6
|
|
6
|
-
describe
|
7
|
-
it
|
7
|
+
describe "#initialize" do
|
8
|
+
it "creates an empty fields hash" do
|
8
9
|
expect(schema.fields).to eq({})
|
9
10
|
end
|
10
11
|
end
|
11
12
|
|
12
|
-
describe
|
13
|
-
it
|
13
|
+
describe "#string" do
|
14
|
+
it "adds a string field to the schema" do
|
14
15
|
schema.string(:name)
|
15
16
|
expect(schema.fields).to eq({ name: { type: :string, description: nil } })
|
16
17
|
end
|
17
18
|
|
18
|
-
it
|
19
|
+
it "adds a string field with description" do
|
19
20
|
schema.string(:name, description: "The user's full name")
|
20
|
-
expect(schema.fields).to eq(
|
21
|
-
name: {
|
22
|
-
|
23
|
-
description: "The user's full name"
|
24
|
-
}
|
25
|
-
})
|
21
|
+
expect(schema.fields).to eq(
|
22
|
+
{ name: { type: :string, description: "The user's full name" } }
|
23
|
+
)
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
29
|
-
describe
|
30
|
-
it
|
27
|
+
describe "#integer" do
|
28
|
+
it "adds an integer field to the schema" do
|
31
29
|
schema.integer(:count)
|
32
|
-
expect(schema.fields).to eq(
|
30
|
+
expect(schema.fields).to eq(
|
31
|
+
{ count: { type: :integer, description: nil } }
|
32
|
+
)
|
33
33
|
end
|
34
34
|
|
35
|
-
it
|
35
|
+
it "adds an integer field with description" do
|
36
36
|
schema.integer(:count, description: "The total number of items")
|
37
|
-
expect(schema.fields).to eq(
|
38
|
-
count: {
|
39
|
-
|
40
|
-
description: "The total number of items"
|
41
|
-
}
|
42
|
-
})
|
37
|
+
expect(schema.fields).to eq(
|
38
|
+
{ count: { type: :integer, description: "The total number of items" } }
|
39
|
+
)
|
43
40
|
end
|
44
41
|
end
|
45
42
|
|
46
|
-
describe
|
47
|
-
it
|
43
|
+
describe "#boolean" do
|
44
|
+
it "adds a boolean field to the schema" do
|
48
45
|
schema.boolean(:active)
|
49
|
-
expect(schema.fields).to eq(
|
46
|
+
expect(schema.fields).to eq(
|
47
|
+
{ active: { type: :boolean, description: nil } }
|
48
|
+
)
|
50
49
|
end
|
51
50
|
|
52
|
-
it
|
51
|
+
it "adds a boolean field with description" do
|
53
52
|
schema.boolean(:active, description: "Whether the item is active")
|
54
|
-
expect(schema.fields).to eq(
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
53
|
+
expect(schema.fields).to eq(
|
54
|
+
{
|
55
|
+
active: {
|
56
|
+
type: :boolean,
|
57
|
+
description: "Whether the item is active"
|
58
|
+
}
|
59
|
+
}
|
60
|
+
)
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
63
|
-
describe
|
64
|
-
it
|
64
|
+
describe "#float" do
|
65
|
+
it "adds a float field to the schema" do
|
65
66
|
schema.float(:price)
|
66
67
|
expect(schema.fields).to eq({ price: { type: :float, description: nil } })
|
67
68
|
end
|
68
69
|
|
69
|
-
it
|
70
|
+
it "adds a float field with description" do
|
70
71
|
schema.float(:price, description: "The price of the item")
|
71
|
-
expect(schema.fields).to eq(
|
72
|
-
price: {
|
73
|
-
|
74
|
-
description: "The price of the item"
|
75
|
-
}
|
76
|
-
})
|
72
|
+
expect(schema.fields).to eq(
|
73
|
+
{ price: { type: :float, description: "The price of the item" } }
|
74
|
+
)
|
77
75
|
end
|
78
76
|
end
|
79
77
|
|
80
|
-
describe
|
81
|
-
it
|
78
|
+
describe "#datetime" do
|
79
|
+
it "adds a datetime field to the schema" do
|
82
80
|
schema.datetime(:start_time)
|
83
|
-
expect(schema.fields).to eq(
|
81
|
+
expect(schema.fields).to eq(
|
82
|
+
{ start_time: { type: :datetime, description: nil } }
|
83
|
+
)
|
84
84
|
end
|
85
85
|
|
86
|
-
it
|
86
|
+
it "adds a datetime field with description" do
|
87
87
|
schema.datetime(:start_time, description: "When the event starts")
|
88
|
-
expect(schema.fields).to eq(
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
88
|
+
expect(schema.fields).to eq(
|
89
|
+
{
|
90
|
+
start_time: {
|
91
|
+
type: :datetime,
|
92
|
+
description: "When the event starts"
|
93
|
+
}
|
94
|
+
}
|
95
|
+
)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#array" do
|
100
|
+
it "adds an untyped array field to the schema" do
|
101
|
+
schema.array(:items)
|
102
|
+
expect(schema.fields).to eq({ items: { type: :array, description: nil } })
|
103
|
+
end
|
104
|
+
|
105
|
+
it "adds an untyped array field with description" do
|
106
|
+
schema.array(:items, description: "A list of items")
|
107
|
+
expect(schema.fields).to eq(
|
108
|
+
{ items: { type: :array, description: "A list of items" } }
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "requires a name for the array field" do
|
113
|
+
expect { schema.array }.to raise_error(ArgumentError)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "accepts array type without a name" do
|
117
|
+
schema.array(:items) { array { string } }
|
118
|
+
expect(schema.fields).to eq(
|
119
|
+
{
|
120
|
+
items: {
|
121
|
+
type: :array,
|
122
|
+
element_type: {
|
123
|
+
type: :array,
|
124
|
+
element_type: {
|
125
|
+
type: :string,
|
126
|
+
description: nil
|
127
|
+
},
|
128
|
+
description: nil
|
129
|
+
},
|
130
|
+
description: nil
|
131
|
+
}
|
132
|
+
}
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "accepts string type without a name" do
|
137
|
+
schema.array(:items) { string }
|
138
|
+
expect(schema.fields).to eq(
|
139
|
+
{
|
140
|
+
items: {
|
141
|
+
type: :array,
|
142
|
+
element_type: {
|
143
|
+
type: :string,
|
144
|
+
description: nil
|
145
|
+
},
|
146
|
+
description: nil
|
147
|
+
}
|
148
|
+
}
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "accepts integer type without a name" do
|
153
|
+
schema.array(:items) { integer }
|
154
|
+
expect(schema.fields).to eq(
|
155
|
+
{
|
156
|
+
items: {
|
157
|
+
type: :array,
|
158
|
+
element_type: {
|
159
|
+
type: :integer,
|
160
|
+
description: nil
|
161
|
+
},
|
162
|
+
description: nil
|
163
|
+
}
|
164
|
+
}
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "accepts boolean type without a name" do
|
169
|
+
schema.array(:items) { boolean }
|
170
|
+
expect(schema.fields).to eq(
|
171
|
+
{
|
172
|
+
items: {
|
173
|
+
type: :array,
|
174
|
+
element_type: {
|
175
|
+
type: :boolean,
|
176
|
+
description: nil
|
177
|
+
},
|
178
|
+
description: nil
|
179
|
+
}
|
180
|
+
}
|
181
|
+
)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "accepts float type without a name" do
|
185
|
+
schema.array(:items) { float }
|
186
|
+
expect(schema.fields).to eq(
|
187
|
+
{
|
188
|
+
items: {
|
189
|
+
type: :array,
|
190
|
+
element_type: {
|
191
|
+
type: :float,
|
192
|
+
description: nil
|
193
|
+
},
|
194
|
+
description: nil
|
195
|
+
}
|
196
|
+
}
|
197
|
+
)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "accepts datetime type without a name" do
|
201
|
+
schema.array(:items) { datetime }
|
202
|
+
expect(schema.fields).to eq(
|
203
|
+
{
|
204
|
+
items: {
|
205
|
+
type: :array,
|
206
|
+
element_type: {
|
207
|
+
type: :datetime,
|
208
|
+
description: nil
|
209
|
+
},
|
210
|
+
description: nil
|
211
|
+
}
|
212
|
+
}
|
213
|
+
)
|
214
|
+
end
|
215
|
+
|
216
|
+
it "validates array elements" do
|
217
|
+
schema.array(:numbers) { integer }
|
218
|
+
errors = schema.validate(numbers: [1, "2", 3])
|
219
|
+
expect(errors).to contain_exactly("Numbers[1] must be an Integer")
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe "primitive types" do
|
224
|
+
it "requires a name for string fields" do
|
225
|
+
expect { schema.string }.to raise_error(ArgumentError)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "requires a name for integer fields" do
|
229
|
+
expect { schema.integer }.to raise_error(ArgumentError)
|
230
|
+
end
|
231
|
+
|
232
|
+
it "requires a name for boolean fields" do
|
233
|
+
expect { schema.boolean }.to raise_error(ArgumentError)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "requires a name for float fields" do
|
237
|
+
expect { schema.float }.to raise_error(ArgumentError)
|
238
|
+
end
|
239
|
+
|
240
|
+
it "requires a name for datetime fields" do
|
241
|
+
expect { schema.datetime }.to raise_error(ArgumentError)
|
94
242
|
end
|
95
243
|
end
|
96
244
|
|
97
|
-
describe
|
98
|
-
it
|
245
|
+
describe "#fields" do
|
246
|
+
it "returns the fields hash" do
|
99
247
|
schema.string(:name)
|
100
248
|
expect(schema.fields).to eq({ name: { type: :string, description: nil } })
|
101
249
|
end
|
102
250
|
|
103
|
-
it
|
251
|
+
it "returns the same hash instance" do
|
104
252
|
schema.string(:name)
|
105
253
|
first_call = schema.fields
|
106
254
|
second_call = schema.fields
|
107
255
|
expect(first_call).to be(second_call)
|
108
256
|
end
|
109
257
|
|
110
|
-
context
|
111
|
-
let(:schema)
|
112
|
-
described_class.new.tap do |s|
|
113
|
-
s.datetime(:start_time)
|
114
|
-
end
|
115
|
-
end
|
258
|
+
context "with datetime fields" do
|
259
|
+
let(:schema) { described_class.new.tap { |s| s.datetime(:start_time) } }
|
116
260
|
|
117
|
-
it
|
261
|
+
it "accepts DateTime values" do
|
118
262
|
errors = schema.validate(start_time: DateTime.now)
|
119
263
|
expect(errors).to be_empty
|
120
264
|
end
|
121
265
|
|
122
|
-
it
|
266
|
+
it "accepts nil values" do
|
123
267
|
errors = schema.validate(start_time: nil)
|
124
268
|
expect(errors).to be_empty
|
125
269
|
end
|
126
270
|
|
127
|
-
it
|
271
|
+
it "returns errors for non-DateTime values" do
|
128
272
|
errors = schema.validate(start_time: "2024-01-01")
|
129
273
|
expect(errors).to contain_exactly("Start time must be a DateTime")
|
130
274
|
end
|
131
275
|
|
132
|
-
it
|
276
|
+
it "can be required using presence validation" do
|
133
277
|
schema.validates :start_time, presence: true
|
134
278
|
errors = schema.validate(start_time: nil)
|
135
279
|
expect(errors).to contain_exactly("Start time can't be blank")
|
@@ -137,7 +281,7 @@ RSpec.describe Lluminary::Schema do
|
|
137
281
|
end
|
138
282
|
end
|
139
283
|
|
140
|
-
describe
|
284
|
+
describe "#validate" do
|
141
285
|
let(:schema) do
|
142
286
|
described_class.new.tap do |s|
|
143
287
|
s.string(:name)
|
@@ -145,12 +289,12 @@ RSpec.describe Lluminary::Schema do
|
|
145
289
|
end
|
146
290
|
end
|
147
291
|
|
148
|
-
it
|
292
|
+
it "returns no errors when all values match their field types" do
|
149
293
|
errors = schema.validate(name: "John", age: 30)
|
150
294
|
expect(errors).to be_empty
|
151
295
|
end
|
152
296
|
|
153
|
-
it
|
297
|
+
it "returns errors for type mismatches" do
|
154
298
|
errors = schema.validate(name: 123, age: "30")
|
155
299
|
expect(errors).to contain_exactly(
|
156
300
|
"Name must be a String",
|
@@ -158,95 +302,83 @@ RSpec.describe Lluminary::Schema do
|
|
158
302
|
)
|
159
303
|
end
|
160
304
|
|
161
|
-
context
|
162
|
-
let(:schema)
|
163
|
-
described_class.new.tap do |s|
|
164
|
-
s.boolean(:active)
|
165
|
-
end
|
166
|
-
end
|
305
|
+
context "with boolean fields" do
|
306
|
+
let(:schema) { described_class.new.tap { |s| s.boolean(:active) } }
|
167
307
|
|
168
|
-
it
|
308
|
+
it "accepts true values" do
|
169
309
|
errors = schema.validate(active: true)
|
170
310
|
expect(errors).to be_empty
|
171
311
|
end
|
172
312
|
|
173
|
-
it
|
313
|
+
it "accepts false values" do
|
174
314
|
errors = schema.validate(active: false)
|
175
315
|
expect(errors).to be_empty
|
176
316
|
end
|
177
317
|
|
178
|
-
it
|
318
|
+
it "accepts nil values" do
|
179
319
|
errors = schema.validate(active: nil)
|
180
320
|
expect(errors).to be_empty
|
181
321
|
end
|
182
322
|
|
183
|
-
it
|
184
|
-
errors = schema.validate(active:
|
323
|
+
it "returns errors for non-boolean values" do
|
324
|
+
errors = schema.validate(active: "true")
|
185
325
|
expect(errors).to contain_exactly("Active must be true or false")
|
186
326
|
|
187
327
|
errors = schema.validate(active: 1)
|
188
328
|
expect(errors).to contain_exactly("Active must be true or false")
|
189
329
|
end
|
190
330
|
|
191
|
-
it
|
331
|
+
it "can be required using presence validation" do
|
192
332
|
schema.validates :active, presence: true
|
193
333
|
errors = schema.validate(active: nil)
|
194
334
|
expect(errors).to contain_exactly("Active can't be blank")
|
195
335
|
end
|
196
336
|
end
|
197
337
|
|
198
|
-
context
|
199
|
-
let(:schema)
|
200
|
-
described_class.new.tap do |s|
|
201
|
-
s.string(:name)
|
202
|
-
end
|
203
|
-
end
|
338
|
+
context "with string fields" do
|
339
|
+
let(:schema) { described_class.new.tap { |s| s.string(:name) } }
|
204
340
|
|
205
|
-
it
|
341
|
+
it "accepts string values" do
|
206
342
|
errors = schema.validate(name: "John")
|
207
343
|
expect(errors).to be_empty
|
208
344
|
end
|
209
345
|
|
210
|
-
it
|
346
|
+
it "accepts nil values" do
|
211
347
|
errors = schema.validate(name: nil)
|
212
348
|
expect(errors).to be_empty
|
213
349
|
end
|
214
350
|
|
215
|
-
it
|
351
|
+
it "returns errors for non-string values" do
|
216
352
|
errors = schema.validate(name: 123)
|
217
353
|
expect(errors).to contain_exactly("Name must be a String")
|
218
354
|
end
|
219
355
|
|
220
|
-
it
|
356
|
+
it "can be required using presence validation" do
|
221
357
|
schema.validates :name, presence: true
|
222
358
|
errors = schema.validate(name: nil)
|
223
359
|
expect(errors).to contain_exactly("Name can't be blank")
|
224
360
|
end
|
225
361
|
end
|
226
362
|
|
227
|
-
context
|
228
|
-
let(:schema)
|
229
|
-
described_class.new.tap do |s|
|
230
|
-
s.integer(:age)
|
231
|
-
end
|
232
|
-
end
|
363
|
+
context "with integer fields" do
|
364
|
+
let(:schema) { described_class.new.tap { |s| s.integer(:age) } }
|
233
365
|
|
234
|
-
it
|
366
|
+
it "accepts integer values" do
|
235
367
|
errors = schema.validate(age: 30)
|
236
368
|
expect(errors).to be_empty
|
237
369
|
end
|
238
370
|
|
239
|
-
it
|
371
|
+
it "accepts nil values" do
|
240
372
|
errors = schema.validate(age: nil)
|
241
373
|
expect(errors).to be_empty
|
242
374
|
end
|
243
375
|
|
244
|
-
it
|
376
|
+
it "returns errors for non-integer values" do
|
245
377
|
errors = schema.validate(age: "30")
|
246
378
|
expect(errors).to contain_exactly("Age must be an Integer")
|
247
379
|
end
|
248
380
|
|
249
|
-
it
|
381
|
+
it "can be required using presence validation" do
|
250
382
|
schema.validates :age, presence: true
|
251
383
|
errors = schema.validate(age: nil)
|
252
384
|
expect(errors).to contain_exactly("Age can't be blank")
|
@@ -254,7 +386,7 @@ RSpec.describe Lluminary::Schema do
|
|
254
386
|
end
|
255
387
|
end
|
256
388
|
|
257
|
-
describe
|
389
|
+
describe "ActiveModel validations" do
|
258
390
|
let(:schema) do
|
259
391
|
described_class.new.tap do |s|
|
260
392
|
s.string(:name)
|
@@ -265,12 +397,12 @@ RSpec.describe Lluminary::Schema do
|
|
265
397
|
end
|
266
398
|
end
|
267
399
|
|
268
|
-
it
|
400
|
+
it "generates a class that includes ActiveModel::Validations" do
|
269
401
|
schema_model = schema.schema_model
|
270
402
|
expect(schema_model.ancestors).to include(ActiveModel::Validations)
|
271
403
|
end
|
272
404
|
|
273
|
-
it
|
405
|
+
it "adds accessors for defined fields" do
|
274
406
|
schema_model = schema.schema_model
|
275
407
|
instance = schema_model.new
|
276
408
|
instance.name = "John"
|
@@ -279,24 +411,26 @@ RSpec.describe Lluminary::Schema do
|
|
279
411
|
expect(instance.age).to eq(30)
|
280
412
|
end
|
281
413
|
|
282
|
-
it
|
414
|
+
it "validates presence" do
|
283
415
|
schema_model = schema.schema_model
|
284
416
|
instance = schema_model.new
|
285
417
|
expect(instance.valid?).to be false
|
286
418
|
expect(instance.errors.full_messages).to include("Name can't be blank")
|
287
419
|
end
|
288
420
|
|
289
|
-
it
|
421
|
+
it "validates numericality" do
|
290
422
|
schema_model = schema.schema_model
|
291
423
|
instance = schema_model.new(name: "John", age: 0)
|
292
424
|
expect(instance.valid?).to be false
|
293
|
-
expect(instance.errors.full_messages).to include(
|
425
|
+
expect(instance.errors.full_messages).to include(
|
426
|
+
"Age must be greater than 0"
|
427
|
+
)
|
294
428
|
end
|
295
429
|
|
296
|
-
it
|
430
|
+
it "returns true for valid instances" do
|
297
431
|
schema_model = schema.schema_model
|
298
432
|
instance = schema_model.new(name: "John", age: 30)
|
299
433
|
expect(instance.valid?).to be true
|
300
434
|
end
|
301
435
|
end
|
302
|
-
end
|
436
|
+
end
|