lex-prospection 0.1.0
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 +7 -0
- data/README.md +58 -0
- data/lib/legion/extensions/prospection/actors/decay.rb +41 -0
- data/lib/legion/extensions/prospection/client.rb +22 -0
- data/lib/legion/extensions/prospection/helpers/constants.rb +37 -0
- data/lib/legion/extensions/prospection/helpers/prospection_engine.rb +125 -0
- data/lib/legion/extensions/prospection/helpers/scenario.rb +116 -0
- data/lib/legion/extensions/prospection/runners/prospection.rb +123 -0
- data/lib/legion/extensions/prospection/version.rb +9 -0
- data/lib/legion/extensions/prospection.rb +17 -0
- metadata +66 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: de2a689e0894969a5cfa8c7abff2cf2a95273b80886d0a77f52f1f7ef38cf408
|
|
4
|
+
data.tar.gz: 7d5dbf56573144226f438724b93a26f2c6ec030a893beb3b433554eb54578e6c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 63e196a84901637427facc533a3d468759c31d3c8e14e3c6b5304fe3263d418e1568bbffe918f4f5028a51ea10e26eebbe76fbfaa76fd714f33d9cb3aa33b67e
|
|
7
|
+
data.tar.gz: c1a256a883a33ad6af03ce4026c48a2d399ae1af57532318c02f2dd4a9afcc97017f4bb62f21eb1e56b3d1b7de3865f2939d0d411946f337405cf1feddaa48f7
|
data/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# lex-prospection
|
|
2
|
+
|
|
3
|
+
Mental time travel and future scenario simulation for the LegionIO cognitive architecture. Imagines future events with emotional predictions and tracks forecast accuracy.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
Simulates future scenarios with predicted emotional valence and arousal. Applies two cognitive bias corrections: impact bias correction (reduces overestimation of emotional intensity) and focalism discount (reduces confidence when multiple scenarios compete for attention in the same domain). When scenarios are resolved against actual outcomes, forecast accuracy is tracked per domain.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
client = Legion::Extensions::Prospection::Client.new
|
|
13
|
+
|
|
14
|
+
# Imagine a future scenario
|
|
15
|
+
scenario = client.imagine_future(
|
|
16
|
+
domain: :deployment,
|
|
17
|
+
description: 'Deploy new service to production',
|
|
18
|
+
time_horizon: 7, # days from now
|
|
19
|
+
predicted_valence: 0.6, # expecting positive outcome
|
|
20
|
+
predicted_arousal: 0.7, # anticipation is high
|
|
21
|
+
confidence: 0.7
|
|
22
|
+
)
|
|
23
|
+
# => { success: true, scenario_id: '...', label: :positive,
|
|
24
|
+
# corrected_valence: 0.36, corrected_arousal: 0.42,
|
|
25
|
+
# confidence: 0.7, confidence_label: :moderate }
|
|
26
|
+
|
|
27
|
+
# See upcoming scenarios
|
|
28
|
+
client.near_future_scenarios(days: 7)
|
|
29
|
+
client.vivid_scenarios(count: 5)
|
|
30
|
+
|
|
31
|
+
# Resolve after outcome observed
|
|
32
|
+
client.resolve_future(
|
|
33
|
+
scenario_id: scenario[:scenario_id],
|
|
34
|
+
actual_valence: 0.7,
|
|
35
|
+
actual_arousal: 0.4
|
|
36
|
+
)
|
|
37
|
+
# => { success: true, forecast_error: 0.17, actual_valence: 0.7, actual_arousal: 0.4 }
|
|
38
|
+
|
|
39
|
+
# Check forecast accuracy for domain
|
|
40
|
+
client.forecast_accuracy(domain: :deployment)
|
|
41
|
+
# => { success: true, domain: :deployment, accuracy: 0.83 }
|
|
42
|
+
|
|
43
|
+
# Periodic decay (vividness fades)
|
|
44
|
+
client.update_prospection
|
|
45
|
+
client.prospection_stats
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Development
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
bundle install
|
|
52
|
+
bundle exec rspec
|
|
53
|
+
bundle exec rubocop
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
MIT
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/actors/every'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Prospection
|
|
8
|
+
module Actor
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class
|
|
11
|
+
Legion::Extensions::Prospection::Runners::Prospection
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def runner_function
|
|
15
|
+
'update_prospection'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def time
|
|
19
|
+
300
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run_now?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def use_runner?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def check_subtask?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def generate_task?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/prospection/helpers/constants'
|
|
4
|
+
require 'legion/extensions/prospection/helpers/scenario'
|
|
5
|
+
require 'legion/extensions/prospection/helpers/prospection_engine'
|
|
6
|
+
require 'legion/extensions/prospection/runners/prospection'
|
|
7
|
+
|
|
8
|
+
module Legion
|
|
9
|
+
module Extensions
|
|
10
|
+
module Prospection
|
|
11
|
+
class Client
|
|
12
|
+
include Runners::Prospection
|
|
13
|
+
|
|
14
|
+
attr_reader :prospection_engine
|
|
15
|
+
|
|
16
|
+
def initialize(prospection_engine: nil, **)
|
|
17
|
+
@prospection_engine = prospection_engine || Helpers::ProspectionEngine.new
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Prospection
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_SCENARIOS = 100
|
|
9
|
+
MAX_FORECASTS_PER_SCENARIO = 10
|
|
10
|
+
MAX_HISTORY = 200
|
|
11
|
+
SCENARIO_DECAY = 0.01
|
|
12
|
+
DEFAULT_CONFIDENCE = 0.4
|
|
13
|
+
IMPACT_BIAS_CORRECTION = 0.6
|
|
14
|
+
FOCALISM_DISCOUNT = 0.15
|
|
15
|
+
TEMPORAL_DISCOUNT_RATE = 0.05
|
|
16
|
+
MAX_TIME_HORIZON = 365
|
|
17
|
+
VIVIDNESS_ALPHA = 0.1
|
|
18
|
+
DEFAULT_VIVIDNESS = 0.5
|
|
19
|
+
|
|
20
|
+
VALENCE_LABELS = {
|
|
21
|
+
(0.6..) => :positive,
|
|
22
|
+
(0.2...0.6) => :neutral,
|
|
23
|
+
(-0.2...0.2) => :ambivalent,
|
|
24
|
+
(..-0.2) => :negative
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
CONFIDENCE_LABELS = {
|
|
28
|
+
(0.8..) => :calibrated,
|
|
29
|
+
(0.5...0.8) => :moderate,
|
|
30
|
+
(0.2...0.5) => :rough,
|
|
31
|
+
(..0.2) => :speculative
|
|
32
|
+
}.freeze
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Prospection
|
|
6
|
+
module Helpers
|
|
7
|
+
class ProspectionEngine
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
attr_reader :scenarios, :domain_accuracy, :history
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@scenarios = {}
|
|
14
|
+
@domain_accuracy = {}
|
|
15
|
+
@history = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def imagine(domain:, description:, time_horizon:, predicted_valence:,
|
|
19
|
+
predicted_arousal:, confidence: nil, **)
|
|
20
|
+
prune_if_full
|
|
21
|
+
scenario = Scenario.new(
|
|
22
|
+
domain: domain,
|
|
23
|
+
description: description,
|
|
24
|
+
time_horizon: time_horizon,
|
|
25
|
+
predicted_valence: predicted_valence,
|
|
26
|
+
predicted_arousal: predicted_arousal,
|
|
27
|
+
confidence: confidence
|
|
28
|
+
)
|
|
29
|
+
apply_focalism(scenario, domain)
|
|
30
|
+
@scenarios[scenario.id] = scenario
|
|
31
|
+
scenario
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def scenarios_for(domain:)
|
|
35
|
+
@scenarios.values.select { |s| s.domain == domain && !s.resolved? }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def resolve_scenario(id:, actual_valence:, actual_arousal:)
|
|
39
|
+
scenario = @scenarios[id]
|
|
40
|
+
return nil unless scenario
|
|
41
|
+
return nil if scenario.resolved?
|
|
42
|
+
|
|
43
|
+
scenario.resolve(actual_valence: actual_valence, actual_arousal: actual_arousal)
|
|
44
|
+
update_domain_accuracy(scenario)
|
|
45
|
+
record_history(scenario)
|
|
46
|
+
scenario
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def accuracy_for(domain)
|
|
50
|
+
1.0 - @domain_accuracy.fetch(domain, 0.0)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def near_future(days: 7)
|
|
54
|
+
@scenarios.values.select { |s| !s.resolved? && s.time_horizon <= days }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def most_vivid(count: 5)
|
|
58
|
+
@scenarios.values
|
|
59
|
+
.reject(&:resolved?)
|
|
60
|
+
.sort_by { |s| -s.vividness }
|
|
61
|
+
.first(count)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def decay_all
|
|
65
|
+
@scenarios.each_value(&:decay)
|
|
66
|
+
@scenarios.reject! { |_, s| s.expired? }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def remove_scenario(id:)
|
|
70
|
+
@scenarios.delete(id)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def scenario_count
|
|
74
|
+
@scenarios.size
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def domain_count
|
|
78
|
+
@scenarios.values.map(&:domain).uniq.size
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def to_h
|
|
82
|
+
{
|
|
83
|
+
scenario_count: scenario_count,
|
|
84
|
+
domain_count: domain_count,
|
|
85
|
+
history_size: @history.size,
|
|
86
|
+
domain_accuracy: @domain_accuracy.transform_values { |v| (1.0 - v).round(4) }
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def apply_focalism(scenario, domain)
|
|
93
|
+
active = scenarios_for(domain: domain)
|
|
94
|
+
scenario.apply_focalism_discount if active.size >= 1
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def update_domain_accuracy(scenario)
|
|
98
|
+
error = scenario.forecast_error || 0.0
|
|
99
|
+
existing = @domain_accuracy.fetch(scenario.domain, error)
|
|
100
|
+
@domain_accuracy[scenario.domain] = existing + (VIVIDNESS_ALPHA * (error - existing))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def record_history(scenario)
|
|
104
|
+
@history << {
|
|
105
|
+
scenario_id: scenario.id,
|
|
106
|
+
domain: scenario.domain,
|
|
107
|
+
forecast_error: scenario.forecast_error,
|
|
108
|
+
resolved_at: scenario.resolved_at
|
|
109
|
+
}
|
|
110
|
+
@history.shift while @history.size > MAX_HISTORY
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def prune_if_full
|
|
114
|
+
return unless @scenarios.size >= MAX_SCENARIOS
|
|
115
|
+
|
|
116
|
+
oldest = @scenarios.values
|
|
117
|
+
.reject(&:resolved?)
|
|
118
|
+
.min_by(&:created_at)
|
|
119
|
+
@scenarios.delete(oldest.id) if oldest
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Prospection
|
|
8
|
+
module Helpers
|
|
9
|
+
class Scenario
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :domain, :description, :time_horizon, :predicted_valence,
|
|
13
|
+
:predicted_arousal, :confidence, :vividness, :created_at,
|
|
14
|
+
:actual_valence, :actual_arousal, :resolved_at
|
|
15
|
+
|
|
16
|
+
def initialize(domain:, description:, time_horizon:, predicted_valence:,
|
|
17
|
+
predicted_arousal:, confidence: nil, **)
|
|
18
|
+
@id = SecureRandom.uuid
|
|
19
|
+
@domain = domain
|
|
20
|
+
@description = description
|
|
21
|
+
@time_horizon = [time_horizon.to_f, MAX_TIME_HORIZON].min
|
|
22
|
+
@predicted_valence = predicted_valence.clamp(-1.0, 1.0)
|
|
23
|
+
@predicted_arousal = predicted_arousal.clamp(0.0, 1.0)
|
|
24
|
+
@confidence = (confidence || DEFAULT_CONFIDENCE).clamp(0.0, 1.0)
|
|
25
|
+
@vividness = DEFAULT_VIVIDNESS
|
|
26
|
+
@created_at = Time.now.utc
|
|
27
|
+
@actual_valence = nil
|
|
28
|
+
@actual_arousal = nil
|
|
29
|
+
@resolved_at = nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def temporal_discount
|
|
33
|
+
1.0 - (TEMPORAL_DISCOUNT_RATE * @time_horizon).clamp(0.0, 0.9)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def corrected_valence
|
|
37
|
+
@predicted_valence * IMPACT_BIAS_CORRECTION * temporal_discount
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def corrected_arousal
|
|
41
|
+
@predicted_arousal * IMPACT_BIAS_CORRECTION * temporal_discount
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def label
|
|
45
|
+
VALENCE_LABELS.each do |range, lbl|
|
|
46
|
+
return lbl if range.cover?(corrected_valence)
|
|
47
|
+
end
|
|
48
|
+
:ambivalent
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def confidence_label
|
|
52
|
+
CONFIDENCE_LABELS.each do |range, lbl|
|
|
53
|
+
return lbl if range.cover?(@confidence)
|
|
54
|
+
end
|
|
55
|
+
:speculative
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def decay
|
|
59
|
+
@confidence = (@confidence - SCENARIO_DECAY).clamp(0.0, 1.0)
|
|
60
|
+
@vividness = (@vividness - SCENARIO_DECAY).clamp(0.0, 1.0)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def reinforce_vividness(amount)
|
|
64
|
+
@vividness = (@vividness + (VIVIDNESS_ALPHA * amount)).clamp(0.0, 1.0)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def apply_focalism_discount
|
|
68
|
+
@predicted_valence *= (1.0 - FOCALISM_DISCOUNT)
|
|
69
|
+
@predicted_arousal *= (1.0 - FOCALISM_DISCOUNT)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def resolve(actual_valence:, actual_arousal:)
|
|
73
|
+
@actual_valence = actual_valence.clamp(-1.0, 1.0)
|
|
74
|
+
@actual_arousal = actual_arousal.clamp(0.0, 1.0)
|
|
75
|
+
@resolved_at = Time.now.utc
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def resolved?
|
|
79
|
+
!@resolved_at.nil?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def forecast_error
|
|
83
|
+
return nil unless resolved?
|
|
84
|
+
|
|
85
|
+
(corrected_valence - @actual_valence).abs
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def expired?
|
|
89
|
+
@confidence < 0.01 && @vividness < 0.01
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def to_h
|
|
93
|
+
{
|
|
94
|
+
id: @id,
|
|
95
|
+
domain: @domain,
|
|
96
|
+
description: @description,
|
|
97
|
+
time_horizon: @time_horizon,
|
|
98
|
+
predicted_valence: @predicted_valence,
|
|
99
|
+
predicted_arousal: @predicted_arousal,
|
|
100
|
+
corrected_valence: corrected_valence.round(4),
|
|
101
|
+
corrected_arousal: corrected_arousal.round(4),
|
|
102
|
+
confidence: @confidence.round(4),
|
|
103
|
+
confidence_label: confidence_label,
|
|
104
|
+
vividness: @vividness.round(4),
|
|
105
|
+
label: label,
|
|
106
|
+
resolved: resolved?,
|
|
107
|
+
forecast_error: forecast_error&.round(4),
|
|
108
|
+
created_at: @created_at,
|
|
109
|
+
resolved_at: @resolved_at
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Prospection
|
|
6
|
+
module Runners
|
|
7
|
+
module Prospection
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def imagine_future(domain:, description:, time_horizon:, predicted_valence:,
|
|
12
|
+
predicted_arousal:, confidence: nil, **)
|
|
13
|
+
scenario = prospection_engine.imagine(
|
|
14
|
+
domain: domain,
|
|
15
|
+
description: description,
|
|
16
|
+
time_horizon: time_horizon,
|
|
17
|
+
predicted_valence: predicted_valence,
|
|
18
|
+
predicted_arousal: predicted_arousal,
|
|
19
|
+
confidence: confidence
|
|
20
|
+
)
|
|
21
|
+
Legion::Logging.debug \
|
|
22
|
+
"[prospection] imagine: domain=#{domain} horizon=#{time_horizon}d " \
|
|
23
|
+
"valence=#{predicted_valence.round(3)} label=#{scenario.label}"
|
|
24
|
+
{
|
|
25
|
+
success: true,
|
|
26
|
+
scenario_id: scenario.id,
|
|
27
|
+
domain: scenario.domain,
|
|
28
|
+
label: scenario.label,
|
|
29
|
+
corrected_valence: scenario.corrected_valence.round(4),
|
|
30
|
+
corrected_arousal: scenario.corrected_arousal.round(4),
|
|
31
|
+
confidence: scenario.confidence.round(4),
|
|
32
|
+
confidence_label: scenario.confidence_label
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def resolve_future(scenario_id:, actual_valence:, actual_arousal:, **)
|
|
37
|
+
scenario = prospection_engine.resolve_scenario(
|
|
38
|
+
id: scenario_id,
|
|
39
|
+
actual_valence: actual_valence,
|
|
40
|
+
actual_arousal: actual_arousal
|
|
41
|
+
)
|
|
42
|
+
unless scenario
|
|
43
|
+
Legion::Logging.debug "[prospection] resolve: id=#{scenario_id} not_found_or_already_resolved"
|
|
44
|
+
return { success: false, reason: :not_found }
|
|
45
|
+
end
|
|
46
|
+
Legion::Logging.debug \
|
|
47
|
+
"[prospection] resolve: id=#{scenario_id} domain=#{scenario.domain} " \
|
|
48
|
+
"error=#{scenario.forecast_error&.round(3)}"
|
|
49
|
+
{
|
|
50
|
+
success: true,
|
|
51
|
+
scenario_id: scenario.id,
|
|
52
|
+
domain: scenario.domain,
|
|
53
|
+
forecast_error: scenario.forecast_error&.round(4),
|
|
54
|
+
actual_valence: actual_valence,
|
|
55
|
+
actual_arousal: actual_arousal
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def forecast_accuracy(domain: :general, **)
|
|
60
|
+
accuracy = prospection_engine.accuracy_for(domain)
|
|
61
|
+
Legion::Logging.debug "[prospection] accuracy: domain=#{domain} accuracy=#{accuracy.round(3)}"
|
|
62
|
+
{ success: true, domain: domain, accuracy: accuracy.round(4) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def near_future_scenarios(days: 7, **)
|
|
66
|
+
scenarios = prospection_engine.near_future(days: days)
|
|
67
|
+
Legion::Logging.debug "[prospection] near_future: days=#{days} count=#{scenarios.size}"
|
|
68
|
+
{
|
|
69
|
+
success: true,
|
|
70
|
+
days: days,
|
|
71
|
+
scenarios: scenarios.map(&:to_h),
|
|
72
|
+
count: scenarios.size
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def vivid_scenarios(count: 5, **)
|
|
77
|
+
scenarios = prospection_engine.most_vivid(count: count)
|
|
78
|
+
Legion::Logging.debug "[prospection] vivid: count=#{scenarios.size}"
|
|
79
|
+
{
|
|
80
|
+
success: true,
|
|
81
|
+
scenarios: scenarios.map(&:to_h),
|
|
82
|
+
count: scenarios.size
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def scenarios_in_domain(domain:, **)
|
|
87
|
+
scenarios = prospection_engine.scenarios_for(domain: domain)
|
|
88
|
+
Legion::Logging.debug "[prospection] domain_scenarios: domain=#{domain} count=#{scenarios.size}"
|
|
89
|
+
{
|
|
90
|
+
success: true,
|
|
91
|
+
domain: domain,
|
|
92
|
+
scenarios: scenarios.map(&:to_h),
|
|
93
|
+
count: scenarios.size
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def update_prospection(**)
|
|
98
|
+
prospection_engine.decay_all
|
|
99
|
+
stats = prospection_engine.to_h
|
|
100
|
+
Legion::Logging.debug \
|
|
101
|
+
"[prospection] tick: scenarios=#{stats[:scenario_count]} " \
|
|
102
|
+
"domains=#{stats[:domain_count]} history=#{stats[:history_size]}"
|
|
103
|
+
{ success: true }.merge(stats)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def prospection_stats(**)
|
|
107
|
+
stats = prospection_engine.to_h
|
|
108
|
+
Legion::Logging.debug \
|
|
109
|
+
"[prospection] stats: scenarios=#{stats[:scenario_count]} " \
|
|
110
|
+
"domains=#{stats[:domain_count]}"
|
|
111
|
+
{ success: true, stats: stats }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def prospection_engine
|
|
117
|
+
@prospection_engine ||= Helpers::ProspectionEngine.new
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'legion/extensions/prospection/version'
|
|
5
|
+
require 'legion/extensions/prospection/helpers/constants'
|
|
6
|
+
require 'legion/extensions/prospection/helpers/scenario'
|
|
7
|
+
require 'legion/extensions/prospection/helpers/prospection_engine'
|
|
8
|
+
require 'legion/extensions/prospection/runners/prospection'
|
|
9
|
+
require 'legion/extensions/prospection/client'
|
|
10
|
+
|
|
11
|
+
module Legion
|
|
12
|
+
module Extensions
|
|
13
|
+
module Prospection
|
|
14
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-prospection
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matthew Iverson
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-gaia
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: 'Future-oriented mental simulation: imagines future scenarios, predicts
|
|
27
|
+
their emotional impact, tracks impact bias and focalism corrections, and calibrates
|
|
28
|
+
forecast accuracy over time. Based on Gilbert & Wilson affective forecasting research.'
|
|
29
|
+
email:
|
|
30
|
+
- matt@iverson.io
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- README.md
|
|
36
|
+
- lib/legion/extensions/prospection.rb
|
|
37
|
+
- lib/legion/extensions/prospection/actors/decay.rb
|
|
38
|
+
- lib/legion/extensions/prospection/client.rb
|
|
39
|
+
- lib/legion/extensions/prospection/helpers/constants.rb
|
|
40
|
+
- lib/legion/extensions/prospection/helpers/prospection_engine.rb
|
|
41
|
+
- lib/legion/extensions/prospection/helpers/scenario.rb
|
|
42
|
+
- lib/legion/extensions/prospection/runners/prospection.rb
|
|
43
|
+
- lib/legion/extensions/prospection/version.rb
|
|
44
|
+
homepage: https://github.com/LegionIO/lex-prospection
|
|
45
|
+
licenses:
|
|
46
|
+
- MIT
|
|
47
|
+
metadata:
|
|
48
|
+
rubygems_mfa_required: 'true'
|
|
49
|
+
rdoc_options: []
|
|
50
|
+
require_paths:
|
|
51
|
+
- lib
|
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '3.4'
|
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
requirements: []
|
|
63
|
+
rubygems_version: 3.6.9
|
|
64
|
+
specification_version: 4
|
|
65
|
+
summary: Affective forecasting and mental time travel for LegionIO
|
|
66
|
+
test_files: []
|