lluminary 0.1.1 → 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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lluminary/config.rb +11 -1
  3. data/lib/lluminary/field_description.rb +34 -29
  4. data/lib/lluminary/provider_error.rb +2 -1
  5. data/lib/lluminary/providers/base.rb +5 -1
  6. data/lib/lluminary/providers/bedrock.rb +32 -30
  7. data/lib/lluminary/providers/openai.rb +28 -21
  8. data/lib/lluminary/providers/test.rb +9 -14
  9. data/lib/lluminary/result.rb +5 -2
  10. data/lib/lluminary/schema.rb +9 -16
  11. data/lib/lluminary/schema_model.rb +13 -10
  12. data/lib/lluminary/task.rb +84 -59
  13. data/lib/lluminary/validation_error.rb +2 -1
  14. data/lib/lluminary/version.rb +3 -2
  15. data/lib/lluminary.rb +23 -7
  16. data/spec/examples/analyze_text_spec.rb +7 -4
  17. data/spec/examples/color_analyzer_spec.rb +22 -22
  18. data/spec/examples/content_analyzer_spec.rb +27 -44
  19. data/spec/examples/historical_event_analyzer_spec.rb +18 -15
  20. data/spec/examples/price_analyzer_spec.rb +22 -28
  21. data/spec/examples/quote_task_spec.rb +9 -8
  22. data/spec/examples/sentiment_analysis_spec.rb +13 -10
  23. data/spec/examples/summarize_text_spec.rb +7 -4
  24. data/spec/lluminary/config_spec.rb +28 -26
  25. data/spec/lluminary/field_description_spec.rb +19 -21
  26. data/spec/lluminary/providers/base_spec.rb +11 -8
  27. data/spec/lluminary/providers/bedrock_spec.rb +47 -57
  28. data/spec/lluminary/providers/openai_spec.rb +27 -35
  29. data/spec/lluminary/providers/test_spec.rb +21 -16
  30. data/spec/lluminary/result_spec.rb +17 -10
  31. data/spec/lluminary/schema_model_spec.rb +31 -22
  32. data/spec/lluminary/schema_spec.rb +95 -107
  33. data/spec/lluminary/task_spec.rb +366 -300
  34. data/spec/spec_helper.rb +7 -2
  35. metadata +35 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '02594d2f6a20444f1e6609a1582ca06c29ecddaf12042a42fdf65621eb16fce3'
4
- data.tar.gz: 20c476662fe4ab8dcc4fe403fd580b5d44323bc970f8c9830a7b32eb06589d4e
3
+ metadata.gz: cd0178afb0e59f3bf23029641e37f9ed149a7a3fadcdf364a88ad9079c8adc7d
4
+ data.tar.gz: 4fe96f29859d3f55104d977ca81df4e41ff450c1e37a3a880f7af3f574bf8037
5
5
  SHA512:
6
- metadata.gz: 5b0f2590d708746f35958ea556d6b6bd6d7e9d594505fc5b70178af317a9b6570b1ac9f4ab8580eee0d16776358fa7be7b65f4a3abc88cf14ce364c5410442bf
7
- data.tar.gz: aad105c596b169ccb3ca0613d974b3883553d7296190c9906bda88f2b5b00b765a079364cb3b45ffaa94dbf9aa81e2b369aa3a236936eb756671785c32058d5d
6
+ metadata.gz: 33ec5d4bd8e00d43dca677aafd076f6de386859c14fb0b001b962032ca0a455fdd2c36e5755e6d898ee54a1bbc4f64714c1ea7b9d7774330a25478c4698c8759
7
+ data.tar.gz: 99c318f93c98a31b1c171436db63609b3258ed183c7e9fc73f16c7f0eb8c09a5eb3d5d1c4e548232e10c2b40956d13b31efae5a0582bb35d91b19d315d667a06
@@ -1,4 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Lluminary
4
+ # Configuration class for Lluminary framework.
5
+ # Handles global settings and provider configurations.
6
+ #
7
+ # @example Setting up configuration
8
+ # Lluminary.configure do |config|
9
+ # config.provider = :openai
10
+ # config.api_key = "your-api-key"
11
+ # end
2
12
  class Config
3
13
  def initialize
4
14
  @providers = {}
@@ -20,4 +30,4 @@ module Lluminary
20
30
  @providers = {}
21
31
  end
22
32
  end
23
- end
33
+ end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  module Lluminary
3
+ # Represents a field in a schema with its type, description, and validations.
4
+ # Used to generate human-readable descriptions and validate field values.
2
5
  class FieldDescription
3
6
  def initialize(name, field)
4
7
  @name = name
@@ -11,7 +14,9 @@ module Lluminary
11
14
  parts = []
12
15
  parts << "#{@name} (#{type_description})"
13
16
  parts << ": #{@description}" if @description
14
- parts << " (#{validation_descriptions.join(', ')})" if validation_descriptions.any?
17
+ if validation_descriptions.any?
18
+ parts << " (#{validation_descriptions.join(", ")})"
19
+ end
15
20
  parts.join
16
21
  end
17
22
 
@@ -19,7 +24,9 @@ module Lluminary
19
24
  parts = []
20
25
  parts << "#{@name} (#{type_description})"
21
26
  parts << ": #{@description}" if @description
22
- parts << "\nValidation: #{validation_descriptions.join(', ')}" if validation_descriptions.any?
27
+ if validation_descriptions.any?
28
+ parts << "\nValidation: #{validation_descriptions.join(", ")}"
29
+ end
23
30
  parts << "\nExample: #{example_value}"
24
31
  parts.join
25
32
  end
@@ -36,26 +43,28 @@ module Lluminary
36
43
  end
37
44
 
38
45
  def validation_descriptions
39
- @validations.map do |_, options|
40
- case options.keys.first
41
- when :absence
42
- "must be absent"
43
- when :comparison
44
- comparison_descriptions(options[:comparison])
45
- when :exclusion
46
- "must not be one of: #{options[:exclusion][:in].join(', ')}"
47
- when :format
48
- "must match format: #{options[:format][:with]}"
49
- when :inclusion
50
- "must be one of: #{options[:inclusion][:in].join(', ')}"
51
- when :length
52
- length_descriptions(options[:length])
53
- when :numericality
54
- numericality_descriptions(options[:numericality])
55
- when :presence
56
- "must be present"
46
+ @validations
47
+ .map do |_, options|
48
+ case options.keys.first
49
+ when :absence
50
+ "must be absent"
51
+ when :comparison
52
+ comparison_descriptions(options[:comparison])
53
+ when :exclusion
54
+ "must not be one of: #{options[:exclusion][:in].join(", ")}"
55
+ when :format
56
+ "must match format: #{options[:format][:with]}"
57
+ when :inclusion
58
+ "must be one of: #{options[:inclusion][:in].join(", ")}"
59
+ when :length
60
+ length_descriptions(options[:length])
61
+ when :numericality
62
+ numericality_descriptions(options[:numericality])
63
+ when :presence
64
+ "must be present"
65
+ end
57
66
  end
58
- end.compact
67
+ .compact
59
68
  end
60
69
 
61
70
  def comparison_descriptions(options)
@@ -119,14 +128,10 @@ module Lluminary
119
128
  descriptions << "must be other than #{options[:other_than]}"
120
129
  end
121
130
  if options[:in]
122
- descriptions << "must be in: #{options[:in].to_a.join(', ')}"
123
- end
124
- if options[:odd]
125
- descriptions << "must be odd"
126
- end
127
- if options[:even]
128
- descriptions << "must be even"
131
+ descriptions << "must be in: #{options[:in].to_a.join(", ")}"
129
132
  end
133
+ descriptions << "must be odd" if options[:odd]
134
+ descriptions << "must be even" if options[:even]
130
135
  descriptions.join(", ")
131
136
  end
132
137
 
@@ -145,4 +150,4 @@ module Lluminary
145
150
  end
146
151
  end
147
152
  end
148
- end
153
+ end
@@ -1,4 +1,5 @@
1
+ # frozen_string_literal: true
1
2
  module Lluminary
2
3
  class ProviderError < StandardError
3
4
  end
4
- end
5
+ end
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Lluminary
2
4
  module Providers
5
+ # Base class for all LLM providers.
6
+ # Defines the interface that all providers must implement.
3
7
  class Base
4
8
  attr_reader :config
5
9
 
@@ -12,4 +16,4 @@ module Lluminary
12
16
  end
13
17
  end
14
18
  end
15
- end
19
+ end
@@ -1,51 +1,53 @@
1
- require 'aws-sdk-bedrockruntime'
2
- require 'json'
3
- require_relative '../provider_error'
1
+ # frozen_string_literal: true
2
+ require "aws-sdk-bedrockruntime"
3
+ require "json"
4
+ require_relative "../provider_error"
4
5
 
5
- require 'pry-byebug'
6
+ require "pry-byebug"
6
7
 
7
8
  module Lluminary
8
9
  module Providers
10
+ # Provider for AWS Bedrock models.
11
+ # Implements the Base provider interface for AWS Bedrock's API.
9
12
  class Bedrock < Base
10
- DEFAULT_MODEL_ID = 'anthropic.claude-instant-v1'
13
+ DEFAULT_MODEL_ID = "anthropic.claude-instant-v1"
11
14
 
12
15
  attr_reader :client, :config
13
16
 
14
17
  def initialize(**config)
15
18
  super
16
- @config = config
19
+ @config = { model_id: DEFAULT_MODEL_ID }.merge(config)
17
20
 
18
- @client = Aws::BedrockRuntime::Client.new(
19
- region: config[:region],
20
- credentials: Aws::Credentials.new(
21
- config[:access_key_id],
22
- config[:secret_access_key]
21
+ @client =
22
+ Aws::BedrockRuntime::Client.new(
23
+ region: config[:region],
24
+ credentials:
25
+ Aws::Credentials.new(
26
+ config[:access_key_id],
27
+ config[:secret_access_key]
28
+ )
23
29
  )
24
- )
25
30
  end
26
31
 
27
- def call(prompt, task)
28
- response = @client.converse(
29
- model_id: config[:model_id] || DEFAULT_MODEL_ID,
30
- messages: [
31
- {
32
- role: 'user',
33
- content: [{text: prompt}]
34
- }
35
- ]
36
- )
32
+ def call(prompt, _task)
33
+ response =
34
+ @client.converse(
35
+ model_id: config[:model_id],
36
+ messages: [{ role: "user", content: [{ text: prompt }] }]
37
+ )
37
38
 
38
39
  content = response.dig(:output, :message, :content, 0, :text)
39
-
40
- {
40
+
41
+ {
41
42
  raw: content,
42
- parsed: begin
43
- JSON.parse(content) if content
44
- rescue JSON::ParserError
45
- nil
46
- end
43
+ parsed:
44
+ begin
45
+ JSON.parse(content) if content
46
+ rescue JSON::ParserError
47
+ nil
48
+ end
47
49
  }
48
50
  end
49
51
  end
50
52
  end
51
- end
53
+ end
@@ -1,9 +1,12 @@
1
- require 'openai'
2
- require 'json'
3
- require_relative '../provider_error'
1
+ # frozen_string_literal: true
2
+ require "openai"
3
+ require "json"
4
+ require_relative "../provider_error"
4
5
 
5
6
  module Lluminary
6
7
  module Providers
8
+ # Provider for OpenAI's GPT models.
9
+ # Implements the Base provider interface for OpenAI's API.
7
10
  class OpenAI < Base
8
11
  DEFAULT_MODEL = "gpt-3.5-turbo"
9
12
 
@@ -11,30 +14,34 @@ module Lluminary
11
14
 
12
15
  def initialize(**config)
13
16
  super
14
- @config = config
17
+ @config = { model: DEFAULT_MODEL }.merge(config)
15
18
  @client = ::OpenAI::Client.new(access_token: config[:api_key])
16
19
  end
17
20
 
18
- def call(prompt, task)
19
- response = @client.chat(
20
- parameters: {
21
- model: config[:model] || DEFAULT_MODEL,
22
- messages: [{ role: "user", content: prompt }],
23
- response_format: { type: "json_object" }
24
- }
25
- )
21
+ def call(prompt, _task)
22
+ response =
23
+ @client.chat(
24
+ parameters: {
25
+ model: config[:model],
26
+ messages: [{ role: "user", content: prompt }],
27
+ response_format: {
28
+ type: "json_object"
29
+ }
30
+ }
31
+ )
26
32
 
27
- content = response.dig('choices', 0, 'message', 'content')
28
-
29
- {
33
+ content = response.dig("choices", 0, "message", "content")
34
+
35
+ {
30
36
  raw: content,
31
- parsed: begin
32
- JSON.parse(content) if content
33
- rescue JSON::ParserError
34
- nil
35
- end
37
+ parsed:
38
+ begin
39
+ JSON.parse(content) if content
40
+ rescue JSON::ParserError
41
+ nil
42
+ end
36
43
  }
37
44
  end
38
45
  end
39
46
  end
40
- end
47
+ end
@@ -1,25 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Lluminary
2
4
  module Providers
5
+ # Test provider for development and testing.
6
+ # Returns predefined responses for testing purposes.
3
7
  class Test < Base
4
- def initialize(**config)
5
- super
6
- end
7
-
8
- def call(prompt, task)
8
+ def call(_prompt, task)
9
9
  response = generate_response(task.class.output_fields)
10
- raw_response = JSON.pretty_generate(response).gsub(/\n\s*/, '')
11
- {
12
- raw: raw_response,
13
- parsed: JSON.parse(raw_response)
14
- }
10
+ raw_response = JSON.pretty_generate(response).gsub(/\n\s*/, "")
11
+ { raw: raw_response, parsed: JSON.parse(raw_response) }
15
12
  end
16
13
 
17
14
  private
18
15
 
19
16
  def generate_response(fields)
20
- fields.each_with_object({}) do |(name, field), hash|
21
- hash[name] = generate_value(field[:type])
22
- end
17
+ fields.transform_values { |field| generate_value(field[:type]) }
23
18
  end
24
19
 
25
20
  def generate_value(type)
@@ -34,4 +29,4 @@ module Lluminary
34
29
  end
35
30
  end
36
31
  end
37
- end
32
+ end
@@ -1,6 +1,9 @@
1
- require 'ostruct'
1
+ # frozen_string_literal: true
2
+ require "ostruct"
2
3
 
3
4
  module Lluminary
5
+ # Represents the result of a task execution.
6
+ # Contains the output data and any metadata about the execution.
4
7
  class Result
5
8
  attr_reader :raw_response, :output, :prompt
6
9
 
@@ -10,4 +13,4 @@ module Lluminary
10
13
  @prompt = prompt
11
14
  end
12
15
  end
13
- end
16
+ end
@@ -1,13 +1,10 @@
1
- require 'active_model'
2
- require_relative 'schema_model'
1
+ # frozen_string_literal: true
2
+ require "active_model"
3
+ require_relative "schema_model"
3
4
 
4
5
  module Lluminary
5
- class JsonValidator < ActiveModel::EachValidator
6
- def validate_each(record, attribute, value)
7
- record.errors.add(:base, "Response must be valid JSON") unless value.is_a?(Hash)
8
- end
9
- end
10
-
6
+ # Represents a JSON schema for validating task inputs and outputs.
7
+ # Provides methods for defining and validating schemas.
11
8
  class Schema
12
9
  def initialize
13
10
  @fields = {}
@@ -34,9 +31,7 @@ module Lluminary
34
31
  @fields[name] = { type: :datetime, description: description }
35
32
  end
36
33
 
37
- def fields
38
- @fields
39
- end
34
+ attr_reader :fields
40
35
 
41
36
  def validates(*args, **options)
42
37
  @validations << [args, options]
@@ -47,10 +42,8 @@ module Lluminary
47
42
  end
48
43
 
49
44
  def schema_model
50
- @schema_model ||= SchemaModel.build(
51
- fields: @fields,
52
- validations: @validations
53
- )
45
+ @schema_model ||=
46
+ SchemaModel.build(fields: @fields, validations: @validations)
54
47
  end
55
48
 
56
49
  def validate(values)
@@ -58,4 +51,4 @@ module Lluminary
58
51
  instance.valid? ? [] : instance.errors.full_messages
59
52
  end
60
53
  end
61
- end
54
+ end
@@ -1,6 +1,9 @@
1
- require 'active_model'
1
+ # frozen_string_literal: true
2
+ require "active_model"
2
3
 
3
4
  module Lluminary
5
+ # Base class for models that use JSON schema validation.
6
+ # Provides ActiveModel integration and schema validation.
4
7
  class SchemaModel
5
8
  include ActiveModel::Validations
6
9
 
@@ -12,7 +15,7 @@ module Lluminary
12
15
 
13
16
  def to_s
14
17
  attrs = attributes.dup
15
- attrs.delete('raw_response')
18
+ attrs.delete("raw_response")
16
19
  "#<#{self.class.name} #{attrs.inspect}>"
17
20
  end
18
21
 
@@ -25,8 +28,10 @@ module Lluminary
25
28
  end
26
29
 
27
30
  # Add raw_response field and validation
28
- define_method(:raw_response) { @attributes['raw_response'] }
29
- define_method(:raw_response=) { |value| @attributes['raw_response'] = value }
31
+ define_method(:raw_response) { @attributes["raw_response"] }
32
+ define_method(:raw_response=) do |value|
33
+ @attributes["raw_response"] = value
34
+ end
30
35
 
31
36
  validate do |record|
32
37
  if record.raw_response
@@ -41,7 +46,7 @@ module Lluminary
41
46
  # Add type validations
42
47
  validate do |record|
43
48
  record.attributes.each do |name, value|
44
- next if name == 'raw_response'
49
+ next if name == "raw_response"
45
50
  next if value.nil?
46
51
 
47
52
  field = fields[name.to_sym]
@@ -57,7 +62,7 @@ module Lluminary
57
62
  record.errors.add(name, "must be an Integer")
58
63
  end
59
64
  when :boolean
60
- unless value == true || value == false
65
+ unless [true, false].include?(value)
61
66
  record.errors.add(name, "must be true or false")
62
67
  end
63
68
  when :float
@@ -73,9 +78,7 @@ module Lluminary
73
78
  end
74
79
 
75
80
  # Add ActiveModel validations
76
- validations.each do |args, options|
77
- validates(*args, **options)
78
- end
81
+ validations.each { |args, options| validates(*args, **options) }
79
82
 
80
83
  # Set model name for error messages
81
84
  define_singleton_method(:model_name) do
@@ -84,4 +87,4 @@ module Lluminary
84
87
  end
85
88
  end
86
89
  end
87
- end
90
+ end