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.
- checksums.yaml +4 -4
- data/lib/lluminary/config.rb +6 -1
- data/lib/lluminary/models/base.rb +235 -0
- data/lib/lluminary/models/bedrock/amazon_nova_pro_v1.rb +19 -0
- data/lib/lluminary/models/bedrock/anthropic_claude_instant_v1.rb +17 -0
- data/lib/lluminary/models/bedrock/base.rb +29 -0
- data/lib/lluminary/models/openai/gpt35_turbo.rb +20 -0
- data/lib/lluminary/provider_error.rb +2 -1
- data/lib/lluminary/providers/base.rb +20 -3
- data/lib/lluminary/providers/bedrock.rb +52 -32
- data/lib/lluminary/providers/openai.rb +41 -24
- data/lib/lluminary/providers/test.rb +14 -13
- data/lib/lluminary/result.rb +5 -2
- data/lib/lluminary/schema.rb +59 -15
- data/lib/lluminary/schema_model.rb +67 -10
- data/lib/lluminary/task.rb +58 -99
- data/lib/lluminary/validation_error.rb +2 -1
- data/lib/lluminary/version.rb +3 -2
- data/lib/lluminary.rb +25 -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/meal_suggester_spec.rb +64 -0
- 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/models/base_spec.rb +581 -0
- data/spec/lluminary/models/bedrock/amazon_nova_pro_v1_spec.rb +30 -0
- data/spec/lluminary/models/bedrock/anthropic_claude_instant_v1_spec.rb +21 -0
- data/spec/lluminary/models/openai/gpt35_turbo_spec.rb +22 -0
- data/spec/lluminary/providers/bedrock_spec.rb +86 -57
- data/spec/lluminary/providers/openai_spec.rb +58 -34
- data/spec/lluminary/providers/test_spec.rb +46 -16
- data/spec/lluminary/result_spec.rb +17 -10
- data/spec/lluminary/schema_model_spec.rb +108 -22
- data/spec/lluminary/schema_spec.rb +241 -107
- data/spec/lluminary/task_spec.rb +118 -584
- data/spec/spec_helper.rb +8 -2
- metadata +73 -22
- data/lib/lluminary/field_description.rb +0 -148
- data/spec/lluminary/field_description_spec.rb +0 -36
- data/spec/lluminary/providers/base_spec.rb +0 -17
@@ -1,75 +1,58 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/content_analyzer"
|
3
4
|
|
4
5
|
RSpec.describe ContentAnalyzer do
|
5
|
-
let(:technical_text)
|
6
|
-
<<~TEXT
|
6
|
+
let(:technical_text) { <<~TEXT }
|
7
7
|
The revolutionary new quantum processor leverages advanced photonic circuits to achieve unprecedented computational speeds.
|
8
8
|
By utilizing entangled photon pairs, it can perform complex calculations in parallel, significantly reducing processing time.
|
9
9
|
This breakthrough technology represents a major advancement in quantum computing.
|
10
10
|
TEXT
|
11
|
-
end
|
12
11
|
|
13
|
-
let(:emotional_text)
|
14
|
-
<<~TEXT
|
12
|
+
let(:emotional_text) { <<~TEXT }
|
15
13
|
I can't believe how amazing this experience was! My heart was racing with excitement as I watched the performance.
|
16
14
|
The raw emotion and passion in every movement brought tears to my eyes. It was truly a transformative moment that I'll never forget.
|
17
15
|
TEXT
|
18
|
-
end
|
19
16
|
|
20
|
-
describe
|
21
|
-
it
|
22
|
-
result =
|
23
|
-
text: technical_text,
|
24
|
-
content_type: "technical"
|
25
|
-
)
|
17
|
+
describe "#call" do
|
18
|
+
it "correctly identifies technical content" do
|
19
|
+
result =
|
20
|
+
described_class.call(text: technical_text, content_type: "technical")
|
26
21
|
|
27
22
|
expect(result.output.contains_type).to be true
|
28
23
|
end
|
29
24
|
|
30
|
-
it
|
31
|
-
result =
|
32
|
-
text: emotional_text,
|
33
|
-
content_type: "technical"
|
34
|
-
)
|
25
|
+
it "correctly identifies non-technical content" do
|
26
|
+
result =
|
27
|
+
described_class.call(text: emotional_text, content_type: "technical")
|
35
28
|
|
36
29
|
expect(result.output.contains_type).to be false
|
37
30
|
end
|
38
31
|
|
39
|
-
it
|
40
|
-
result =
|
41
|
-
text: emotional_text,
|
42
|
-
content_type: "emotional"
|
43
|
-
)
|
32
|
+
it "correctly identifies emotional content" do
|
33
|
+
result =
|
34
|
+
described_class.call(text: emotional_text, content_type: "emotional")
|
44
35
|
|
45
36
|
expect(result.output.contains_type).to be true
|
46
37
|
end
|
47
38
|
|
48
|
-
it
|
49
|
-
result =
|
50
|
-
text: technical_text,
|
51
|
-
content_type: "emotional"
|
52
|
-
)
|
39
|
+
it "correctly identifies non-emotional content" do
|
40
|
+
result =
|
41
|
+
described_class.call(text: technical_text, content_type: "emotional")
|
53
42
|
|
54
43
|
expect(result.output.contains_type).to be false
|
55
44
|
end
|
56
45
|
|
57
|
-
it
|
58
|
-
expect
|
59
|
-
described_class.call!(
|
60
|
-
|
61
|
-
content_type: "technical"
|
62
|
-
)
|
63
|
-
}.to raise_error(Lluminary::ValidationError)
|
46
|
+
it "validates presence of text" do
|
47
|
+
expect do
|
48
|
+
described_class.call!(text: "", content_type: "technical")
|
49
|
+
end.to raise_error(Lluminary::ValidationError)
|
64
50
|
end
|
65
51
|
|
66
|
-
it
|
67
|
-
expect
|
68
|
-
described_class.call!(
|
69
|
-
|
70
|
-
content_type: ""
|
71
|
-
)
|
72
|
-
}.to raise_error(Lluminary::ValidationError)
|
52
|
+
it "validates presence of content_type" do
|
53
|
+
expect do
|
54
|
+
described_class.call!(text: technical_text, content_type: "")
|
55
|
+
end.to raise_error(Lluminary::ValidationError)
|
73
56
|
end
|
74
57
|
end
|
75
|
-
end
|
58
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/historical_event_analyzer"
|
4
|
+
require "pry-byebug"
|
4
5
|
|
5
6
|
RSpec.describe HistoricalEventAnalyzer do
|
6
|
-
describe
|
7
|
-
context
|
8
|
-
it
|
9
|
-
result =
|
10
|
-
|
11
|
-
|
7
|
+
describe "#call" do
|
8
|
+
context "with events that have known exact times" do
|
9
|
+
it "returns the exact time of the human step on the moon" do
|
10
|
+
result =
|
11
|
+
described_class.call(
|
12
|
+
event_description: "Neil Armstrong's first step onto the Moon"
|
13
|
+
)
|
12
14
|
|
13
15
|
expect(result.output.valid?).to be true
|
14
16
|
expect(result.output.event_datetime).to be_a(DateTime)
|
@@ -19,11 +21,12 @@ RSpec.describe HistoricalEventAnalyzer do
|
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
|
-
context
|
23
|
-
it
|
24
|
-
result =
|
25
|
-
|
26
|
-
|
24
|
+
context "with events that have approximate times" do
|
25
|
+
it "returns midnight for the fall of the Roman Empire" do
|
26
|
+
result =
|
27
|
+
described_class.call(
|
28
|
+
event_description: "Assassination of Julius Caesar"
|
29
|
+
)
|
27
30
|
|
28
31
|
expect(result.output.valid?).to be true
|
29
32
|
expect(result.output.event_datetime).to be_a(DateTime)
|
@@ -34,4 +37,4 @@ RSpec.describe HistoricalEventAnalyzer do
|
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
37
|
-
end
|
40
|
+
end
|
@@ -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
|
@@ -1,46 +1,40 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/price_analyzer"
|
3
4
|
|
4
5
|
RSpec.describe PriceAnalyzer do
|
5
|
-
describe
|
6
|
-
it
|
7
|
-
result =
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
describe "#call" do
|
7
|
+
it "returns a competitiveness score between 0.0 and 1.0 for a high-priced item" do
|
8
|
+
result =
|
9
|
+
described_class.call(
|
10
|
+
product_name: "Entry LevelLuxury Watch",
|
11
|
+
price: 999.99
|
12
|
+
)
|
11
13
|
|
12
14
|
expect(result.output.competitiveness_score).to be_a(Float)
|
13
15
|
expect(result.output.competitiveness_score).to be_between(0.0, 1.0)
|
14
16
|
expect(result.output.competitiveness_score).to be >= 0.5 # Lower priced luxury watch is more competitive
|
15
17
|
end
|
16
18
|
|
17
|
-
it
|
18
|
-
result =
|
19
|
-
product_name: "Basic Watch",
|
20
|
-
price: 10049.99
|
21
|
-
)
|
19
|
+
it "returns a higher competitiveness score for a reasonably priced item" do
|
20
|
+
result =
|
21
|
+
described_class.call(product_name: "Basic Watch", price: 10_049.99)
|
22
22
|
|
23
23
|
expect(result.output.competitiveness_score).to be_a(Float)
|
24
24
|
expect(result.output.competitiveness_score).to be_between(0.0, 1.0)
|
25
25
|
expect(result.output.competitiveness_score).to be <= 0.5 # Higher priced basic watch is less competitive
|
26
26
|
end
|
27
27
|
|
28
|
-
it
|
29
|
-
expect
|
30
|
-
described_class.call!(
|
31
|
-
|
32
|
-
price: 49.99
|
33
|
-
)
|
34
|
-
}.to raise_error(Lluminary::ValidationError)
|
28
|
+
it "validates presence of product_name" do
|
29
|
+
expect do
|
30
|
+
described_class.call!(product_name: "", price: 49.99)
|
31
|
+
end.to raise_error(Lluminary::ValidationError)
|
35
32
|
end
|
36
33
|
|
37
|
-
it
|
38
|
-
expect
|
39
|
-
described_class.call!(
|
40
|
-
|
41
|
-
price: nil
|
42
|
-
)
|
43
|
-
}.to raise_error(Lluminary::ValidationError)
|
34
|
+
it "validates presence of price" do
|
35
|
+
expect do
|
36
|
+
described_class.call!(product_name: "Basic Watch", price: nil)
|
37
|
+
end.to raise_error(Lluminary::ValidationError)
|
44
38
|
end
|
45
39
|
end
|
46
|
-
end
|
40
|
+
end
|
@@ -1,27 +1,28 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/quote_task"
|
3
4
|
|
4
5
|
RSpec.describe QuoteTask do
|
5
|
-
describe
|
6
|
-
it
|
6
|
+
describe ".call" do
|
7
|
+
it "returns a quote and its author" do
|
7
8
|
result = described_class.call
|
8
9
|
|
9
10
|
expect(result.output.quote).to be_a(String)
|
10
11
|
expect(result.output.quote).not_to be_empty
|
11
|
-
|
12
|
+
|
12
13
|
expect(result.output.author).to be_a(String)
|
13
14
|
expect(result.output.author).not_to be_empty
|
14
15
|
end
|
15
16
|
|
16
|
-
it
|
17
|
+
it "can be called without any input parameters" do
|
17
18
|
expect { described_class.call }.not_to raise_error
|
18
19
|
end
|
19
20
|
|
20
|
-
it
|
21
|
+
it "returns a valid result object" do
|
21
22
|
result = described_class.call
|
22
23
|
expect(result).to be_a(Lluminary::Task)
|
23
24
|
expect(result.input).to be_a(Lluminary::SchemaModel)
|
24
25
|
expect(result.input.valid?).to be true
|
25
26
|
end
|
26
27
|
end
|
27
|
-
end
|
28
|
+
end
|
@@ -1,13 +1,14 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/sentiment_analysis"
|
3
4
|
|
4
5
|
RSpec.describe SentimentAnalysis do
|
5
6
|
let(:text) { "I absolutely love this new feature!" }
|
6
7
|
|
7
|
-
describe
|
8
|
-
it
|
8
|
+
describe ".call" do
|
9
|
+
it "analyzes sentiment of given text" do
|
9
10
|
result = described_class.call(text: text)
|
10
|
-
|
11
|
+
|
11
12
|
expect(result.output.sentiment).to be_a(String)
|
12
13
|
expect(result.output.sentiment).not_to be_empty
|
13
14
|
expect(result.output.explanation).to be_a(String)
|
@@ -16,7 +17,7 @@ RSpec.describe SentimentAnalysis do
|
|
16
17
|
expect(result.output.confidence).to be_between(0, 100)
|
17
18
|
end
|
18
19
|
|
19
|
-
it
|
20
|
+
it "returns valid JSON response" do
|
20
21
|
result = described_class.call(text: text)
|
21
22
|
expect(result.output.raw_response).to be_a(String)
|
22
23
|
expect { JSON.parse(result.output.raw_response) }.not_to raise_error
|
@@ -29,17 +30,19 @@ RSpec.describe SentimentAnalysis do
|
|
29
30
|
expect(json["confidence"]).to eq(result.output.confidence)
|
30
31
|
end
|
31
32
|
|
32
|
-
it
|
33
|
+
it "requires text input" do
|
33
34
|
result = described_class.call({})
|
34
35
|
expect(result.valid?).to be false
|
35
|
-
expect(result.input.errors.full_messages).to include(
|
36
|
+
expect(result.input.errors.full_messages).to include(
|
37
|
+
"Text can't be blank"
|
38
|
+
)
|
36
39
|
end
|
37
40
|
|
38
|
-
it
|
41
|
+
it "returns a valid result object" do
|
39
42
|
result = described_class.call(text: text)
|
40
43
|
expect(result).to be_a(Lluminary::Task)
|
41
44
|
expect(result.input).to be_a(Lluminary::SchemaModel)
|
42
45
|
expect(result.input.valid?).to be true
|
43
46
|
end
|
44
47
|
end
|
45
|
-
end
|
48
|
+
end
|
@@ -1,8 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
require_relative "../../examples/summarize_text"
|
3
4
|
|
4
5
|
RSpec.describe SummarizeText do
|
5
|
-
let(:text) {
|
6
|
+
let(:text) { <<~TEXT }
|
7
|
+
Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write.
|
8
|
+
TEXT
|
6
9
|
|
7
10
|
it "summarizes text" do
|
8
11
|
result = described_class.call(text: text)
|
@@ -18,4 +21,4 @@ RSpec.describe SummarizeText do
|
|
18
21
|
expect(json).to have_key("summary")
|
19
22
|
expect(json["summary"]).to eq(result.output.summary)
|
20
23
|
end
|
21
|
-
end
|
24
|
+
end
|
@@ -1,47 +1,49 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
2
3
|
|
3
4
|
RSpec.describe Lluminary::Config do
|
4
5
|
let(:config) { described_class.new }
|
5
6
|
|
6
|
-
describe
|
7
|
-
it
|
7
|
+
describe "#configure" do
|
8
|
+
it "allows setting provider configurations" do
|
8
9
|
config.configure do |c|
|
9
|
-
c.provider(:openai, api_key:
|
10
|
-
c.provider(
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
c.provider(:openai, api_key: "global-key")
|
11
|
+
c.provider(
|
12
|
+
:bedrock,
|
13
|
+
access_key_id: "global-access-key",
|
14
|
+
secret_access_key: "global-secret-key",
|
15
|
+
region: "us-east-1"
|
14
16
|
)
|
15
17
|
end
|
16
18
|
|
17
|
-
expect(config.provider_config(:openai)).to eq({ api_key:
|
18
|
-
expect(config.provider_config(:bedrock)).to eq(
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
expect(config.provider_config(:openai)).to eq({ api_key: "global-key" })
|
20
|
+
expect(config.provider_config(:bedrock)).to eq(
|
21
|
+
{
|
22
|
+
access_key_id: "global-access-key",
|
23
|
+
secret_access_key: "global-secret-key",
|
24
|
+
region: "us-east-1"
|
25
|
+
}
|
26
|
+
)
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
|
-
describe
|
27
|
-
it
|
30
|
+
describe "#provider_config" do
|
31
|
+
it "returns empty hash for unconfigured providers" do
|
28
32
|
expect(config.provider_config(:unknown)).to eq({})
|
29
33
|
end
|
30
34
|
|
31
|
-
it
|
32
|
-
config.configure
|
33
|
-
c.provider(:openai, api_key: 'test-key')
|
34
|
-
end
|
35
|
+
it "returns the configuration for a configured provider" do
|
36
|
+
config.configure { |c| c.provider(:openai, api_key: "test-key") }
|
35
37
|
|
36
|
-
expect(config.provider_config(:openai)).to eq({ api_key:
|
38
|
+
expect(config.provider_config(:openai)).to eq({ api_key: "test-key" })
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
|
-
describe
|
41
|
-
it
|
42
|
+
describe "#reset!" do
|
43
|
+
it "clears all provider configurations" do
|
42
44
|
config.configure do |c|
|
43
|
-
c.provider(:openai, api_key:
|
44
|
-
c.provider(:bedrock, access_key_id:
|
45
|
+
c.provider(:openai, api_key: "test-key")
|
46
|
+
c.provider(:bedrock, access_key_id: "test-access-key")
|
45
47
|
end
|
46
48
|
|
47
49
|
config.reset!
|
@@ -50,4 +52,4 @@ RSpec.describe Lluminary::Config do
|
|
50
52
|
expect(config.provider_config(:bedrock)).to eq({})
|
51
53
|
end
|
52
54
|
end
|
53
|
-
end
|
55
|
+
end
|