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.
@@ -1,20 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
  require "ostruct"
3
+ require "json"
3
4
  require_relative "schema"
4
5
  require_relative "validation_error"
5
- require_relative "field_description"
6
- require "json"
6
+ require_relative "models/base"
7
+ require_relative "models/openai/gpt35_turbo"
8
+ require_relative "models/bedrock/anthropic_claude_instant_v1"
7
9
 
8
10
  module Lluminary
9
11
  # Base class for all Lluminary tasks.
10
12
  # 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
18
13
  class Task
19
14
  class << self
20
15
  def input_schema(&block)
@@ -43,11 +38,7 @@ module Lluminary
43
38
  raise ArgumentError, "Unknown provider: #{provider_name}"
44
39
  end
45
40
 
46
- # Merge global config with task-specific config
47
- global_config = Lluminary.config.provider_config(provider_name)
48
- merged_config = global_config.merge(config)
49
-
50
- @provider = provider_class.new(**merged_config)
41
+ @provider = provider_class.new(**config)
51
42
  end
52
43
 
53
44
  def call(input = {})
@@ -122,11 +113,11 @@ module Lluminary
122
113
  end
123
114
 
124
115
  def prompt
125
- <<~PROMPT
126
- #{task_prompt}
116
+ @prompt ||= self.class.provider.model.format_prompt(self)
117
+ end
127
118
 
128
- #{json_schema_example}
129
- PROMPT
119
+ def task_prompt
120
+ raise NotImplementedError, "Subclasses must implement task_prompt"
130
121
  end
131
122
 
132
123
  private
@@ -172,7 +163,7 @@ module Lluminary
172
163
  # Validate after merging
173
164
  @output.valid?
174
165
 
175
- @prompt = prompt
166
+ prompt
176
167
  end
177
168
 
178
169
  def define_input_methods
@@ -181,68 +172,11 @@ module Lluminary
181
172
  end
182
173
  end
183
174
 
184
- def task_prompt
185
- raise NotImplementedError, "Subclasses must implement task_prompt"
186
- end
187
-
188
- def json_schema_example
189
- return "{}" if fields.empty?
190
-
191
- <<~SCHEMA.chomp
192
- You must respond with ONLY a valid JSON object. Do not include any other text, explanations, or formatting.
193
- The JSON object must contain the following fields:
194
-
195
- #{generate_field_descriptions}
196
-
197
- Your response must be ONLY this JSON object:
198
- #{example_json}
199
- SCHEMA
200
- end
201
-
202
- def fields
203
- @fields ||= self.class.output_fields
204
- end
205
-
206
- def generate_field_descriptions
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")
219
- end
220
-
221
- def example_json
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
237
-
238
- JSON.pretty_generate(json)
239
- end
240
-
241
175
  def to_result
242
176
  Result.new(
243
177
  raw_response: @output&.raw_response,
244
178
  output: @parsed_response,
245
- prompt: @prompt
179
+ prompt: prompt
246
180
  )
247
181
  end
248
182
  end
data/lib/lluminary.rb CHANGED
@@ -3,8 +3,10 @@
3
3
  require_relative "lluminary/version"
4
4
  require_relative "lluminary/result"
5
5
  require_relative "lluminary/task"
6
- require_relative "lluminary/providers/base"
7
- require_relative "lluminary/providers/openai"
6
+ # automatically require all providers
7
+ Dir[File.join(__dir__, "lluminary/providers/*.rb")].each { |file| require file }
8
+ # automatically require all models
9
+ Dir[File.join(__dir__, "lluminary/models/**/*.rb")].each { |file| require file }
8
10
  require_relative "lluminary/config"
9
11
 
10
12
  # Lluminary is a framework for building and running LLM-powered tasks.
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+ require_relative "../../examples/meal_suggester"
4
+
5
+ RSpec.describe MealSuggester do
6
+ let(:valid_ingredients) { %w[eggs bread butter] }
7
+ let(:valid_count) { 3 }
8
+ let(:valid_params) do
9
+ { ingredients: valid_ingredients, suggestions_count: valid_count }
10
+ end
11
+
12
+ describe "input validation" do
13
+ it "accepts valid parameters" do
14
+ expect { described_class.call!(**valid_params) }.not_to raise_error
15
+ end
16
+
17
+ it "requires ingredients to be present" do
18
+ expect do
19
+ described_class.call!(ingredients: [], suggestions_count: valid_count)
20
+ end.to raise_error(Lluminary::ValidationError)
21
+ end
22
+
23
+ it "requires suggestions_count to be present" do
24
+ expect do
25
+ described_class.call!(
26
+ ingredients: valid_ingredients,
27
+ suggestions_count: nil
28
+ )
29
+ end.to raise_error(Lluminary::ValidationError)
30
+ end
31
+ end
32
+
33
+ describe "output validation" do
34
+ let(:result) { described_class.call(**valid_params) }
35
+
36
+ it "returns an array of meal suggestions" do
37
+ expect(result.output.meal_suggestions).to be_an(Array)
38
+ end
39
+
40
+ it "returns the requested number of suggestions" do
41
+ expect(result.output.meal_suggestions.length).to eq(valid_count)
42
+ end
43
+
44
+ it "returns string suggestions" do
45
+ expect(result.output.meal_suggestions).to all(be_a(String))
46
+ end
47
+
48
+ it "returns non-empty suggestions" do
49
+ expect(result.output.meal_suggestions).to all(be_present)
50
+ end
51
+ end
52
+
53
+ describe "prompt generation" do
54
+ let(:result) { described_class.call(**valid_params) }
55
+
56
+ it "includes the ingredients in the prompt" do
57
+ expect(result.prompt).to include(valid_ingredients.inspect)
58
+ end
59
+
60
+ it "includes the suggestions count in the prompt" do
61
+ expect(result.prompt).to include(valid_count.to_s)
62
+ end
63
+ end
64
+ end