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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lluminary/config.rb +6 -1
  3. data/lib/lluminary/models/base.rb +235 -0
  4. data/lib/lluminary/models/bedrock/amazon_nova_pro_v1.rb +19 -0
  5. data/lib/lluminary/models/bedrock/anthropic_claude_instant_v1.rb +17 -0
  6. data/lib/lluminary/models/bedrock/base.rb +29 -0
  7. data/lib/lluminary/models/openai/gpt35_turbo.rb +20 -0
  8. data/lib/lluminary/provider_error.rb +2 -1
  9. data/lib/lluminary/providers/base.rb +20 -3
  10. data/lib/lluminary/providers/bedrock.rb +52 -32
  11. data/lib/lluminary/providers/openai.rb +41 -24
  12. data/lib/lluminary/providers/test.rb +14 -13
  13. data/lib/lluminary/result.rb +5 -2
  14. data/lib/lluminary/schema.rb +59 -15
  15. data/lib/lluminary/schema_model.rb +67 -10
  16. data/lib/lluminary/task.rb +58 -99
  17. data/lib/lluminary/validation_error.rb +2 -1
  18. data/lib/lluminary/version.rb +3 -2
  19. data/lib/lluminary.rb +25 -7
  20. data/spec/examples/analyze_text_spec.rb +7 -4
  21. data/spec/examples/color_analyzer_spec.rb +22 -22
  22. data/spec/examples/content_analyzer_spec.rb +27 -44
  23. data/spec/examples/historical_event_analyzer_spec.rb +18 -15
  24. data/spec/examples/meal_suggester_spec.rb +64 -0
  25. data/spec/examples/price_analyzer_spec.rb +22 -28
  26. data/spec/examples/quote_task_spec.rb +9 -8
  27. data/spec/examples/sentiment_analysis_spec.rb +13 -10
  28. data/spec/examples/summarize_text_spec.rb +7 -4
  29. data/spec/lluminary/config_spec.rb +28 -26
  30. data/spec/lluminary/models/base_spec.rb +581 -0
  31. data/spec/lluminary/models/bedrock/amazon_nova_pro_v1_spec.rb +30 -0
  32. data/spec/lluminary/models/bedrock/anthropic_claude_instant_v1_spec.rb +21 -0
  33. data/spec/lluminary/models/openai/gpt35_turbo_spec.rb +22 -0
  34. data/spec/lluminary/providers/bedrock_spec.rb +86 -57
  35. data/spec/lluminary/providers/openai_spec.rb +58 -34
  36. data/spec/lluminary/providers/test_spec.rb +46 -16
  37. data/spec/lluminary/result_spec.rb +17 -10
  38. data/spec/lluminary/schema_model_spec.rb +108 -22
  39. data/spec/lluminary/schema_spec.rb +241 -107
  40. data/spec/lluminary/task_spec.rb +118 -584
  41. data/spec/spec_helper.rb +8 -2
  42. metadata +73 -22
  43. data/lib/lluminary/field_description.rb +0 -148
  44. data/spec/lluminary/field_description_spec.rb +0 -36
  45. data/spec/lluminary/providers/base_spec.rb +0 -17
@@ -1,135 +1,279 @@
1
- require 'spec_helper'
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 '#initialize' do
7
- it 'creates an empty fields hash' do
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 '#string' do
13
- it 'adds a string field to the schema' do
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 'adds a string field with description' do
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
- type: :string,
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 '#integer' do
30
- it 'adds an integer field to the schema' do
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({ count: { type: :integer, description: nil } })
30
+ expect(schema.fields).to eq(
31
+ { count: { type: :integer, description: nil } }
32
+ )
33
33
  end
34
34
 
35
- it 'adds an integer field with description' do
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
- type: :integer,
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 '#boolean' do
47
- it 'adds a boolean field to the schema' do
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({ active: { type: :boolean, description: nil } })
46
+ expect(schema.fields).to eq(
47
+ { active: { type: :boolean, description: nil } }
48
+ )
50
49
  end
51
50
 
52
- it 'adds a boolean field with description' do
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
- active: {
56
- type: :boolean,
57
- description: "Whether the item is active"
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 '#float' do
64
- it 'adds a float field to the schema' do
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 'adds a float field with description' do
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
- type: :float,
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 '#datetime' do
81
- it 'adds a datetime field to the schema' do
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({ start_time: { type: :datetime, description: nil } })
81
+ expect(schema.fields).to eq(
82
+ { start_time: { type: :datetime, description: nil } }
83
+ )
84
84
  end
85
85
 
86
- it 'adds a datetime field with description' do
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
- start_time: {
90
- type: :datetime,
91
- description: "When the event starts"
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 '#fields' do
98
- it 'returns the fields hash' do
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 'returns the same hash instance' do
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 'with datetime fields' do
111
- let(:schema) do
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 'accepts DateTime values' do
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 'accepts nil values' do
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 'returns errors for non-DateTime values' do
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 'can be required using presence validation' do
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 '#validate' do
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 'returns no errors when all values match their field types' do
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 'returns errors for type mismatches' do
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 'with boolean fields' do
162
- let(:schema) do
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 'accepts true values' do
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 'accepts false values' do
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 'accepts nil values' do
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 'returns errors for non-boolean values' do
184
- errors = schema.validate(active: 'true')
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 'can be required using presence validation' do
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 'with string fields' do
199
- let(:schema) do
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 'accepts string values' do
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 'accepts nil values' do
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 'returns errors for non-string values' do
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 'can be required using presence validation' do
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 'with integer fields' do
228
- let(:schema) do
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 'accepts integer values' do
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 'accepts nil values' do
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 'returns errors for non-integer values' do
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 'can be required using presence validation' do
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 'ActiveModel validations' do
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 'generates a class that includes ActiveModel::Validations' do
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 'adds accessors for defined fields' do
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 'validates presence' do
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 'validates numericality' do
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("Age must be greater than 0")
425
+ expect(instance.errors.full_messages).to include(
426
+ "Age must be greater than 0"
427
+ )
294
428
  end
295
429
 
296
- it 'returns true for valid instances' do
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