lluminary 0.1.2 → 0.1.4

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.
@@ -11,6 +11,38 @@ RSpec.describe Lluminary::Providers::OpenAI do
11
11
  end
12
12
  end
13
13
 
14
+ describe "#models" do
15
+ let(:mock_models_response) do
16
+ {
17
+ "object" => "list",
18
+ "data" => [
19
+ {
20
+ "id" => "gpt-4",
21
+ "object" => "model",
22
+ "created" => 1_687_882_411,
23
+ "owned_by" => "openai"
24
+ },
25
+ {
26
+ "id" => "gpt-3.5-turbo",
27
+ "object" => "model",
28
+ "created" => 1_677_610_602,
29
+ "owned_by" => "openai"
30
+ }
31
+ ]
32
+ }
33
+ end
34
+
35
+ before do
36
+ allow_any_instance_of(OpenAI::Client).to receive(:models).and_return(
37
+ double("ModelsClient", list: mock_models_response)
38
+ )
39
+ end
40
+
41
+ it "returns an array of model IDs as strings" do
42
+ expect(provider.models).to eq(%w[gpt-4 gpt-3.5-turbo])
43
+ end
44
+ end
45
+
14
46
  describe "#call" do
15
47
  let(:prompt) { "Test prompt" }
16
48
  let(:task) { "Test task" }
@@ -10,6 +10,31 @@ RSpec.describe Lluminary::Providers::Test do
10
10
  end
11
11
  let(:task) { double("Task", class: task_class) }
12
12
 
13
+ describe "#initialize" do
14
+ it "accepts configuration options" do
15
+ config = { api_key: "test_key", model: "test_model" }
16
+ provider = described_class.new(**config)
17
+ expect(provider.config).to eq(config)
18
+ end
19
+
20
+ it "merges default provider configuration with instance configuration" do
21
+ # Set up default provider configuration
22
+ Lluminary.configure do |config|
23
+ config.provider(:test, api_key: "global_key", model: "global_model")
24
+ end
25
+
26
+ # Create provider with instance-specific config
27
+ provider = described_class.new(model: "instance_model", temperature: 0.7)
28
+
29
+ # Should merge default and instance config, with instance taking precedence
30
+ expect(provider.config).to eq(
31
+ api_key: "global_key",
32
+ model: "instance_model",
33
+ temperature: 0.7
34
+ )
35
+ end
36
+ end
37
+
13
38
  describe "#call" do
14
39
  it "returns a hash with raw and parsed response" do
15
40
  response = provider.call(prompt, task)
@@ -91,5 +91,82 @@ RSpec.describe Lluminary::SchemaModel do
91
91
  instance = model_class.new(name: "John", age: 30)
92
92
  expect(instance.attributes).to eq({ "name" => "John", "age" => 30 })
93
93
  end
94
+
95
+ it "validates array types" do
96
+ fields = {
97
+ items: {
98
+ type: :array,
99
+ element_type: {
100
+ type: :string,
101
+ description: nil
102
+ },
103
+ description: "A list of strings"
104
+ }
105
+ }
106
+ model_class = described_class.build(fields: fields, validations: [])
107
+
108
+ # Test that nil is allowed
109
+ instance = model_class.new(items: nil)
110
+ expect(instance.valid?).to be true
111
+
112
+ # Test valid array of strings
113
+ instance = model_class.new(items: %w[one two])
114
+ expect(instance.valid?).to be true
115
+
116
+ # Test invalid array (not an array)
117
+ instance = model_class.new(items: "not an array")
118
+ expect(instance.valid?).to be false
119
+ expect(instance.errors.full_messages).to include("Items must be an Array")
120
+
121
+ # Test invalid array elements
122
+ instance = model_class.new(items: ["one", 2, "three"])
123
+ expect(instance.valid?).to be false
124
+ expect(instance.errors.full_messages).to include(
125
+ "Items[1] must be a String"
126
+ )
127
+ end
128
+
129
+ it "validates nested array types" do
130
+ fields = {
131
+ matrix: {
132
+ type: :array,
133
+ element_type: {
134
+ type: :array,
135
+ element_type: {
136
+ type: :integer,
137
+ description: nil
138
+ },
139
+ description: nil
140
+ },
141
+ description: "A matrix of integers"
142
+ }
143
+ }
144
+ model_class = described_class.build(fields: fields, validations: [])
145
+
146
+ # Test valid nested arrays
147
+ instance = model_class.new(matrix: [[1, 2], [3, 4]])
148
+ expect(instance.valid?).to be true
149
+
150
+ # Test invalid outer array
151
+ instance = model_class.new(matrix: "not an array")
152
+ expect(instance.valid?).to be false
153
+ expect(instance.errors.full_messages).to include(
154
+ "Matrix must be an Array"
155
+ )
156
+
157
+ # Test invalid inner array
158
+ instance = model_class.new(matrix: ["not an array"])
159
+ expect(instance.valid?).to be false
160
+ expect(instance.errors.full_messages).to include(
161
+ "Matrix[0] must be an Array"
162
+ )
163
+
164
+ # Test invalid inner array elements
165
+ instance = model_class.new(matrix: [[1, "2"], [3, 4]])
166
+ expect(instance.valid?).to be false
167
+ expect(instance.errors.full_messages).to include(
168
+ "Matrix[0][1] must be an Integer"
169
+ )
170
+ end
94
171
  end
95
172
  end
@@ -96,6 +96,152 @@ RSpec.describe Lluminary::Schema do
96
96
  end
97
97
  end
98
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)
242
+ end
243
+ end
244
+
99
245
  describe "#fields" do
100
246
  it "returns the fields hash" do
101
247
  schema.string(:name)