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.
- checksums.yaml +4 -4
- data/lib/lluminary/config.rb +11 -1
- data/lib/lluminary/field_description.rb +34 -29
- data/lib/lluminary/provider_error.rb +2 -1
- data/lib/lluminary/providers/base.rb +5 -1
- data/lib/lluminary/providers/bedrock.rb +32 -30
- data/lib/lluminary/providers/openai.rb +28 -21
- data/lib/lluminary/providers/test.rb +9 -14
- data/lib/lluminary/result.rb +5 -2
- data/lib/lluminary/schema.rb +9 -16
- data/lib/lluminary/schema_model.rb +13 -10
- data/lib/lluminary/task.rb +84 -59
- data/lib/lluminary/validation_error.rb +2 -1
- data/lib/lluminary/version.rb +3 -2
- data/lib/lluminary.rb +23 -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/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/field_description_spec.rb +19 -21
- data/spec/lluminary/providers/base_spec.rb +11 -8
- data/spec/lluminary/providers/bedrock_spec.rb +47 -57
- data/spec/lluminary/providers/openai_spec.rb +27 -35
- data/spec/lluminary/providers/test_spec.rb +21 -16
- data/spec/lluminary/result_spec.rb +17 -10
- data/spec/lluminary/schema_model_spec.rb +31 -22
- data/spec/lluminary/schema_spec.rb +95 -107
- data/spec/lluminary/task_spec.rb +366 -300
- data/spec/spec_helper.rb +7 -2
- metadata +35 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd0178afb0e59f3bf23029641e37f9ed149a7a3fadcdf364a88ad9079c8adc7d
|
4
|
+
data.tar.gz: 4fe96f29859d3f55104d977ca81df4e41ff450c1e37a3a880f7af3f574bf8037
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33ec5d4bd8e00d43dca677aafd076f6de386859c14fb0b001b962032ca0a455fdd2c36e5755e6d898ee54a1bbc4f64714c1ea7b9d7774330a25478c4698c8759
|
7
|
+
data.tar.gz: 99c318f93c98a31b1c171436db63609b3258ed183c7e9fc73f16c7f0eb8c09a5eb3d5d1c4e548232e10c2b40956d13b31efae5a0582bb35d91b19d315d667a06
|
data/lib/lluminary/config.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
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,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
|
-
|
2
|
-
require
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "aws-sdk-bedrockruntime"
|
3
|
+
require "json"
|
4
|
+
require_relative "../provider_error"
|
4
5
|
|
5
|
-
require
|
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 =
|
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 =
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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,
|
28
|
-
response =
|
29
|
-
|
30
|
-
|
31
|
-
{
|
32
|
-
|
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:
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
2
|
-
require
|
3
|
-
|
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,
|
19
|
-
response =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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(
|
28
|
-
|
29
|
-
{
|
33
|
+
content = response.dig("choices", 0, "message", "content")
|
34
|
+
|
35
|
+
{
|
30
36
|
raw: content,
|
31
|
-
parsed:
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
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.
|
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
|
data/lib/lluminary/result.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
-
|
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
|
data/lib/lluminary/schema.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "active_model"
|
3
|
+
require_relative "schema_model"
|
3
4
|
|
4
5
|
module Lluminary
|
5
|
-
|
6
|
-
|
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
|
-
|
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 ||=
|
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
|
-
|
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(
|
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[
|
29
|
-
define_method(:raw_response=)
|
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 ==
|
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
|
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
|
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
|