lex-agentic-learning 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/CHANGELOG.md +7 -0
- data/lex-agentic-learning.gemspec +8 -0
- data/lib/legion/extensions/agentic/learning/anchoring/runners/anchoring.rb +12 -12
- data/lib/legion/extensions/agentic/learning/catalyst/runners/cognitive_catalyst.rb +16 -16
- data/lib/legion/extensions/agentic/learning/chrysalis/runners/cognitive_chrysalis.rb +8 -8
- data/lib/legion/extensions/agentic/learning/curiosity/runners/curiosity.rb +10 -10
- data/lib/legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity.rb +13 -13
- data/lib/legion/extensions/agentic/learning/habit/runners/habit.rb +7 -7
- data/lib/legion/extensions/agentic/learning/hebbian/runners/hebbian_assembly.rb +10 -10
- data/lib/legion/extensions/agentic/learning/learning_rate/runners/learning_rate.rb +4 -4
- data/lib/legion/extensions/agentic/learning/meta_learning/runners/meta_learning.rb +20 -20
- data/lib/legion/extensions/agentic/learning/preference_learning/runners/preference_learning.rb +10 -10
- data/lib/legion/extensions/agentic/learning/procedural/runners/procedural_learning.rb +14 -14
- data/lib/legion/extensions/agentic/learning/scaffolding/runners/cognitive_scaffolding.rb +19 -19
- data/lib/legion/extensions/agentic/learning/version.rb +1 -1
- data/spec/spec_helper.rb +21 -21
- metadata +99 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8e389e9f54e7a9c30da3458b1cfda5723454543d12b47b76bc8a99c9a35e72f
|
|
4
|
+
data.tar.gz: 228ba88c5b55ee8a8886de351f10ce0a10abad53cbdca36e0131ad1b0e013ccb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 638c8c4442081c5d1584b70cd6bbd579e468f4e63cc6aee9643dbc177ca9f254b4d562a5886a0ca5aa3686fe83b9bab16871bdcf44b65d7cbb70cafaa38746fe
|
|
7
|
+
data.tar.gz: c1ca765261443c56a76eadd376960950183b5435c7c53cdef9af0abcaa9a3cd8a205e47aa6dee4ebad0f97b5c144ced0cdc50d16c67f8b5406d86e25be99b25e
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.1.2] - 2026-03-22
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Add legion-* sub-gems as runtime dependencies (logging, settings, json, cache, crypt, data, transport)
|
|
9
|
+
- Replace direct Legion::Logging calls with injected log helper in all runner modules
|
|
10
|
+
- Update spec_helper with real sub-gem helper stubs
|
|
11
|
+
|
|
5
12
|
## [0.1.1] - 2026-03-18
|
|
6
13
|
|
|
7
14
|
### Changed
|
|
@@ -24,6 +24,14 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
end
|
|
25
25
|
spec.require_paths = ['lib']
|
|
26
26
|
|
|
27
|
+
spec.add_dependency 'legion-cache', '>= 1.3.11'
|
|
28
|
+
spec.add_dependency 'legion-crypt', '>= 1.4.9'
|
|
29
|
+
spec.add_dependency 'legion-data', '>= 1.4.17'
|
|
30
|
+
spec.add_dependency 'legion-json', '>= 1.2.1'
|
|
31
|
+
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
32
|
+
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
33
|
+
spec.add_dependency 'legion-transport', '>= 1.3.9'
|
|
34
|
+
|
|
27
35
|
spec.add_development_dependency 'rspec', '~> 3.13'
|
|
28
36
|
spec.add_development_dependency 'rubocop', '~> 1.60'
|
|
29
37
|
spec.add_development_dependency 'rubocop-rspec', '~> 2.26'
|
|
@@ -12,28 +12,28 @@ module Legion
|
|
|
12
12
|
|
|
13
13
|
def record_anchor(value:, domain: :general, **)
|
|
14
14
|
anchor = anchor_store.add(value: value, domain: domain)
|
|
15
|
-
|
|
15
|
+
log.debug "[anchoring] record_anchor domain=#{domain} value=#{value} id=#{anchor.id}"
|
|
16
16
|
{ success: true, anchor: anchor.to_h }
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def evaluate_estimate(estimate:, domain: :general, **)
|
|
20
20
|
result = anchor_store.evaluate(estimate: estimate, domain: domain)
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
log.debug "[anchoring] evaluate_estimate domain=#{domain} estimate=#{estimate} " \
|
|
22
|
+
"anchored=#{result[:anchored_estimate].round(4)} pull=#{result[:pull_strength].round(4)}"
|
|
23
23
|
{ success: true }.merge(result)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def reference_frame(value:, domain: :general, **)
|
|
27
27
|
result = anchor_store.reference_frame(value: value, domain: domain)
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
log.debug "[anchoring] reference_frame domain=#{domain} value=#{value} " \
|
|
29
|
+
"gain_or_loss=#{result[:gain_or_loss]}"
|
|
30
30
|
{ success: true }.merge(result)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def de_anchor(estimate:, domain: :general, **)
|
|
34
34
|
anchor = anchor_store.strongest(domain: domain)
|
|
35
35
|
if anchor.nil?
|
|
36
|
-
|
|
36
|
+
log.debug "[anchoring] de_anchor domain=#{domain} no anchor found"
|
|
37
37
|
return { success: true, corrected_estimate: estimate.to_f, anchor_bias: 0.0, domain: domain }
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -41,8 +41,8 @@ module Legion
|
|
|
41
41
|
bias = biased - estimate.to_f
|
|
42
42
|
corrected = estimate.to_f - bias
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
log.debug "[anchoring] de_anchor domain=#{domain} estimate=#{estimate} " \
|
|
45
|
+
"corrected=#{corrected.round(4)} bias=#{bias.round(4)}"
|
|
46
46
|
|
|
47
47
|
{
|
|
48
48
|
success: true,
|
|
@@ -56,13 +56,13 @@ module Legion
|
|
|
56
56
|
|
|
57
57
|
def shift_reference(domain:, new_reference:, **)
|
|
58
58
|
result = anchor_store.shift_reference(domain: domain, new_reference: new_reference)
|
|
59
|
-
|
|
59
|
+
log.info "[anchoring] shift_reference domain=#{domain} new=#{new_reference} significant=#{result[:significant]}"
|
|
60
60
|
{ success: true }.merge(result)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def update_anchoring(**)
|
|
64
64
|
pruned = anchor_store.decay_all
|
|
65
|
-
|
|
65
|
+
log.debug "[anchoring] update_anchoring pruned=#{pruned}"
|
|
66
66
|
{ success: true, pruned: pruned }
|
|
67
67
|
end
|
|
68
68
|
|
|
@@ -70,7 +70,7 @@ module Legion
|
|
|
70
70
|
domain = domain.to_sym
|
|
71
71
|
anchor = anchor_store.strongest(domain: domain)
|
|
72
72
|
all_list = anchor_store.instance_variable_get(:@anchors)[domain] || []
|
|
73
|
-
|
|
73
|
+
log.debug "[anchoring] domain_anchors domain=#{domain} count=#{all_list.size}"
|
|
74
74
|
{
|
|
75
75
|
success: true,
|
|
76
76
|
domain: domain,
|
|
@@ -82,7 +82,7 @@ module Legion
|
|
|
82
82
|
|
|
83
83
|
def anchoring_stats(**)
|
|
84
84
|
stats = anchor_store.to_h
|
|
85
|
-
|
|
85
|
+
log.debug "[anchoring] stats domains=#{stats[:domain_count]} total=#{stats[:total_anchors]}"
|
|
86
86
|
{ success: true }.merge(stats)
|
|
87
87
|
end
|
|
88
88
|
|
|
@@ -22,11 +22,11 @@ module Legion
|
|
|
22
22
|
opts[:specificity] = specificity unless specificity.nil?
|
|
23
23
|
|
|
24
24
|
catalyst = e.create_catalyst(**opts)
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
log.debug "[cognitive_catalyst] create_catalyst id=#{catalyst.id[0..7]} " \
|
|
26
|
+
"type=#{catalyst_type} domain=#{domain} potency=#{catalyst.potency.round(2)}"
|
|
27
27
|
{ success: true, catalyst: catalyst.to_h }
|
|
28
28
|
rescue ArgumentError => e
|
|
29
|
-
|
|
29
|
+
log.warn "[cognitive_catalyst] create_catalyst failed: #{e.message}"
|
|
30
30
|
{ success: false, reason: e.message }
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -41,51 +41,51 @@ module Legion
|
|
|
41
41
|
opts[:activation_energy] = activation_energy unless activation_energy.nil?
|
|
42
42
|
|
|
43
43
|
reaction = e.create_reaction(**opts)
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
log.debug "[cognitive_catalyst] create_reaction id=#{reaction.id[0..7]} " \
|
|
45
|
+
"type=#{reaction_type} reactants=#{reaction.reactants.size}"
|
|
46
46
|
{ success: true, reaction: reaction.to_h }
|
|
47
47
|
rescue ArgumentError => e
|
|
48
|
-
|
|
48
|
+
log.warn "[cognitive_catalyst] create_reaction failed: #{e.message}"
|
|
49
49
|
{ success: false, reason: e.message }
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def apply_catalyst(catalyst_id:, reaction_id:, engine: nil, **)
|
|
53
53
|
e = engine || default_engine
|
|
54
54
|
result = e.apply_catalyst(catalyst_id: catalyst_id, reaction_id: reaction_id)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
log.debug '[cognitive_catalyst] apply_catalyst ' \
|
|
56
|
+
"catalyst=#{catalyst_id[0..7]} reaction=#{reaction_id[0..7]} " \
|
|
57
|
+
"success=#{result[:success]}"
|
|
58
58
|
result
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def attempt_reaction(reaction_id:, energy_input:, engine: nil, **)
|
|
62
62
|
e = engine || default_engine
|
|
63
63
|
result = e.attempt_reaction(reaction_id: reaction_id, energy_input: energy_input)
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
log.debug "[cognitive_catalyst] attempt_reaction id=#{reaction_id[0..7]} " \
|
|
65
|
+
"energy=#{energy_input} completed=#{result[:completed]}"
|
|
66
66
|
result
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def recharge(catalyst_id:, amount:, engine: nil, **)
|
|
70
70
|
e = engine || default_engine
|
|
71
71
|
result = e.recharge_catalyst(catalyst_id: catalyst_id, amount: amount)
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
log.debug "[cognitive_catalyst] recharge id=#{catalyst_id[0..7]} " \
|
|
73
|
+
"amount=#{amount} potency=#{result[:potency]&.round(2)}"
|
|
74
74
|
result
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def list_catalysts(engine: nil, **)
|
|
78
78
|
e = engine || default_engine
|
|
79
79
|
catalysts = e.all_catalysts
|
|
80
|
-
|
|
80
|
+
log.debug "[cognitive_catalyst] list_catalysts count=#{catalysts.size}"
|
|
81
81
|
{ success: true, catalysts: catalysts.map(&:to_h), count: catalysts.size }
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
def catalyst_status(engine: nil, **)
|
|
85
85
|
e = engine || default_engine
|
|
86
86
|
report = e.catalyst_report
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
log.debug "[cognitive_catalyst] catalyst_status total=#{report[:total_catalysts]} " \
|
|
88
|
+
"reactions=#{report[:total_reactions]} rate=#{report[:catalyzed_rate].round(2)}"
|
|
89
89
|
{ success: true }.merge(report)
|
|
90
90
|
end
|
|
91
91
|
|
|
@@ -22,7 +22,7 @@ module Legion
|
|
|
22
22
|
return { success: false, reason: :invalid_type, valid_types: Helpers::Constants::CHRYSALIS_TYPES }
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
log.debug "[cognitive_chrysalis] creating chrysalis type=#{type}"
|
|
26
26
|
engine.create_chrysalis(chrysalis_type: type, content: content)
|
|
27
27
|
rescue ArgumentError => e
|
|
28
28
|
{ success: false, reason: e.message }
|
|
@@ -30,7 +30,7 @@ module Legion
|
|
|
30
30
|
|
|
31
31
|
def create_cocoon(environment: 'default', temperature: 0.5, humidity: 0.5, engine: nil, **)
|
|
32
32
|
engine ||= default_engine
|
|
33
|
-
|
|
33
|
+
log.debug "[cognitive_chrysalis] creating cocoon environment=#{environment}"
|
|
34
34
|
engine.create_cocoon(environment: environment, temperature: temperature, humidity: humidity)
|
|
35
35
|
rescue ArgumentError => e
|
|
36
36
|
{ success: false, reason: e.message }
|
|
@@ -40,7 +40,7 @@ module Legion
|
|
|
40
40
|
return { success: false, reason: :missing_chrysalis_id } if chrysalis_id.nil?
|
|
41
41
|
|
|
42
42
|
engine ||= default_engine
|
|
43
|
-
|
|
43
|
+
log.debug "[cognitive_chrysalis] spinning chrysalis=#{chrysalis_id}"
|
|
44
44
|
engine.spin(chrysalis_id: chrysalis_id)
|
|
45
45
|
rescue ArgumentError => e
|
|
46
46
|
{ success: false, reason: e.message }
|
|
@@ -51,7 +51,7 @@ module Legion
|
|
|
51
51
|
return { success: false, reason: :missing_cocoon_id } if cocoon_id.nil?
|
|
52
52
|
|
|
53
53
|
engine ||= default_engine
|
|
54
|
-
|
|
54
|
+
log.debug "[cognitive_chrysalis] enclosing chrysalis=#{chrysalis_id} in cocoon=#{cocoon_id}"
|
|
55
55
|
engine.enclose(chrysalis_id: chrysalis_id, cocoon_id: cocoon_id)
|
|
56
56
|
rescue ArgumentError => e
|
|
57
57
|
{ success: false, reason: e.message }
|
|
@@ -61,7 +61,7 @@ module Legion
|
|
|
61
61
|
return { success: false, reason: :missing_chrysalis_id } if chrysalis_id.nil?
|
|
62
62
|
|
|
63
63
|
engine ||= default_engine
|
|
64
|
-
|
|
64
|
+
log.debug "[cognitive_chrysalis] incubating chrysalis=#{chrysalis_id}"
|
|
65
65
|
engine.incubate(chrysalis_id: chrysalis_id)
|
|
66
66
|
rescue ArgumentError => e
|
|
67
67
|
{ success: false, reason: e.message }
|
|
@@ -69,7 +69,7 @@ module Legion
|
|
|
69
69
|
|
|
70
70
|
def incubate_all(engine: nil, **)
|
|
71
71
|
engine ||= default_engine
|
|
72
|
-
|
|
72
|
+
log.debug '[cognitive_chrysalis] incubating all eligible chrysalises'
|
|
73
73
|
engine.incubate_all!
|
|
74
74
|
rescue ArgumentError => e
|
|
75
75
|
{ success: false, reason: e.message }
|
|
@@ -79,7 +79,7 @@ module Legion
|
|
|
79
79
|
return { success: false, reason: :missing_chrysalis_id } if chrysalis_id.nil?
|
|
80
80
|
|
|
81
81
|
engine ||= default_engine
|
|
82
|
-
|
|
82
|
+
log.debug "[cognitive_chrysalis] emerging chrysalis=#{chrysalis_id} force=#{force}"
|
|
83
83
|
if force
|
|
84
84
|
engine.force_emerge(chrysalis_id: chrysalis_id)
|
|
85
85
|
else
|
|
@@ -93,7 +93,7 @@ module Legion
|
|
|
93
93
|
return { success: false, reason: :missing_cocoon_id } if cocoon_id.nil?
|
|
94
94
|
|
|
95
95
|
engine ||= default_engine
|
|
96
|
-
|
|
96
|
+
log.debug "[cognitive_chrysalis] disturbing cocoon=#{cocoon_id} force=#{force}"
|
|
97
97
|
engine.disturb_cocoon(cocoon_id: cocoon_id, force: force)
|
|
98
98
|
rescue ArgumentError => e
|
|
99
99
|
{ success: false, reason: e.message }
|
|
@@ -13,7 +13,7 @@ module Legion
|
|
|
13
13
|
|
|
14
14
|
def detect_gaps(prior_results: {}, **)
|
|
15
15
|
gaps = Helpers::GapDetector.detect(prior_results)
|
|
16
|
-
|
|
16
|
+
log.debug "[curiosity] detected #{gaps.size} knowledge gaps"
|
|
17
17
|
|
|
18
18
|
created = create_wonders_from_gaps(gaps)
|
|
19
19
|
build_detect_result(gaps, created)
|
|
@@ -28,7 +28,7 @@ module Legion
|
|
|
28
28
|
source_trace_ids: source_trace_ids
|
|
29
29
|
)
|
|
30
30
|
wonder_store.store(wonder)
|
|
31
|
-
|
|
31
|
+
log.info "[curiosity] manually generated wonder: #{question}"
|
|
32
32
|
wonder
|
|
33
33
|
end
|
|
34
34
|
|
|
@@ -39,7 +39,7 @@ module Legion
|
|
|
39
39
|
return { error: :not_explorable, reason: :max_attempts } unless Helpers::Wonder.explorable?(wonder)
|
|
40
40
|
|
|
41
41
|
wonder_store.update(wonder_id, attempts: wonder[:attempts] + 1, last_explored_at: Time.now.utc)
|
|
42
|
-
|
|
42
|
+
log.info "[curiosity] exploring: #{wonder[:question]} (attempt ##{wonder[:attempts] + 1})"
|
|
43
43
|
{ exploring: true, wonder_id: wonder_id, attempt: wonder[:attempts] + 1 }
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -54,20 +54,20 @@ module Legion
|
|
|
54
54
|
|
|
55
55
|
def curiosity_intensity(**)
|
|
56
56
|
intensity = compute_intensity
|
|
57
|
-
|
|
57
|
+
log.debug "[curiosity] intensity=#{intensity.round(3)}"
|
|
58
58
|
{ intensity: intensity, active_wonders: wonder_store.active_count,
|
|
59
59
|
resolution_rate: wonder_store.resolution_rate.round(3), top_domain: top_curiosity_domain }
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def top_wonders(limit: 5, **)
|
|
63
63
|
wonders = wonder_store.top_balanced(limit: limit)
|
|
64
|
-
|
|
64
|
+
log.debug "[curiosity] top #{wonders.size} wonders requested"
|
|
65
65
|
{ wonders: wonders.map { |w| format_wonder(w) } }
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def form_agenda(**)
|
|
69
69
|
wonders = wonder_store.top_balanced(limit: 5)
|
|
70
|
-
|
|
70
|
+
log.debug "[curiosity] forming agenda from #{wonders.size} wonders"
|
|
71
71
|
{ agenda_items: wonders.map { |w| format_agenda_item(w) }, source: :curiosity }
|
|
72
72
|
end
|
|
73
73
|
|
|
@@ -79,7 +79,7 @@ module Legion
|
|
|
79
79
|
|
|
80
80
|
def decay_wonders(hours_elapsed: 1.0, **)
|
|
81
81
|
pruned = wonder_store.decay_all(hours_elapsed: hours_elapsed)
|
|
82
|
-
|
|
82
|
+
log.debug "[curiosity] decay: pruned=#{pruned} remaining=#{wonder_store.active_count}"
|
|
83
83
|
{ pruned: pruned, remaining: wonder_store.active_count }
|
|
84
84
|
end
|
|
85
85
|
|
|
@@ -97,14 +97,14 @@ module Legion
|
|
|
97
97
|
:salience, :information_gain, :source_trace_ids))
|
|
98
98
|
wonder_store.store(wonder)
|
|
99
99
|
created << wonder
|
|
100
|
-
|
|
100
|
+
log.info "[curiosity] new wonder: #{wonder[:question]} (#{wonder[:gap_type]}/#{wonder[:domain]})"
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def build_detect_result(gaps, created)
|
|
105
105
|
intensity = compute_intensity
|
|
106
106
|
top = wonder_store.top_balanced(limit: 3)
|
|
107
|
-
|
|
107
|
+
log.debug "[curiosity] intensity=#{intensity.round(3)} active=#{wonder_store.active_count}"
|
|
108
108
|
{ gaps_detected: gaps.size, wonders_created: created.size, curiosity_intensity: intensity,
|
|
109
109
|
top_wonders: top.map { |w| { wonder_id: w[:wonder_id], question: w[:question], score: Helpers::Wonder.score(w).round(3) } },
|
|
110
110
|
active_count: wonder_store.active_count }
|
|
@@ -112,7 +112,7 @@ module Legion
|
|
|
112
112
|
|
|
113
113
|
def build_resolve_result(wonder, resolved, actual_gain)
|
|
114
114
|
reward = actual_gain * Helpers::Constants::CURIOSITY_REWARD_MULTIPLIER
|
|
115
|
-
|
|
115
|
+
log.info "[curiosity] resolved: #{wonder[:question]} gain=#{actual_gain.round(2)}"
|
|
116
116
|
{ resolved: true, wonder_id: wonder[:wonder_id], actual_gain: actual_gain,
|
|
117
117
|
expected_gain: wonder[:information_gain], reward: reward,
|
|
118
118
|
domain: resolved[:domain], resolution_rate: wonder_store.resolution_rate.round(3) }
|
data/lib/legion/extensions/agentic/learning/epistemic_curiosity/runners/epistemic_curiosity.rb
CHANGED
|
@@ -13,9 +13,9 @@ module Legion
|
|
|
13
13
|
def create_gap(question:, domain:, gap_type: :factual, urgency: Helpers::Constants::DEFAULT_URGENCY, **)
|
|
14
14
|
result = engine.create_gap(question: question, domain: domain, gap_type: gap_type, urgency: urgency)
|
|
15
15
|
if result[:created]
|
|
16
|
-
|
|
16
|
+
log.info "[epistemic_curiosity] gap created: id=#{result[:gap][:id]} domain=#{domain} type=#{gap_type}"
|
|
17
17
|
else
|
|
18
|
-
|
|
18
|
+
log.debug "[epistemic_curiosity] gap not created: reason=#{result[:reason]}"
|
|
19
19
|
end
|
|
20
20
|
result
|
|
21
21
|
end
|
|
@@ -24,9 +24,9 @@ module Legion
|
|
|
24
24
|
result = engine.explore_gap(gap_id: gap_id)
|
|
25
25
|
if result[:found]
|
|
26
26
|
gap = result[:gap]
|
|
27
|
-
|
|
27
|
+
log.debug "[epistemic_curiosity] explore: id=#{gap_id} explorations=#{gap[:explorations]} urgency=#{gap[:urgency]}"
|
|
28
28
|
else
|
|
29
|
-
|
|
29
|
+
log.debug "[epistemic_curiosity] explore: id=#{gap_id} not found"
|
|
30
30
|
end
|
|
31
31
|
result
|
|
32
32
|
end
|
|
@@ -35,9 +35,9 @@ module Legion
|
|
|
35
35
|
result = engine.satisfy_gap(gap_id: gap_id, amount: amount)
|
|
36
36
|
if result[:found]
|
|
37
37
|
gap = result[:gap]
|
|
38
|
-
|
|
38
|
+
log.debug "[epistemic_curiosity] satisfy: id=#{gap_id} satisfaction=#{gap[:satisfaction]} resolved=#{gap[:resolved]}"
|
|
39
39
|
else
|
|
40
|
-
|
|
40
|
+
log.debug "[epistemic_curiosity] satisfy: id=#{gap_id} not found"
|
|
41
41
|
end
|
|
42
42
|
result
|
|
43
43
|
end
|
|
@@ -45,40 +45,40 @@ module Legion
|
|
|
45
45
|
def resolve_gap(gap_id:, **)
|
|
46
46
|
result = engine.resolve_gap(gap_id: gap_id)
|
|
47
47
|
if result[:found]
|
|
48
|
-
|
|
48
|
+
log.info "[epistemic_curiosity] resolved: id=#{gap_id}"
|
|
49
49
|
else
|
|
50
|
-
|
|
50
|
+
log.debug "[epistemic_curiosity] resolve: id=#{gap_id} not found"
|
|
51
51
|
end
|
|
52
52
|
result
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def most_urgent_gaps(limit: 5, **)
|
|
56
56
|
gaps = engine.most_urgent(limit: limit)
|
|
57
|
-
|
|
57
|
+
log.debug "[epistemic_curiosity] most_urgent: limit=#{limit} returned=#{gaps.size}"
|
|
58
58
|
{ gaps: gaps.map(&:to_h), count: gaps.size }
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def gaps_by_domain(domain:, **)
|
|
62
62
|
gaps = engine.by_domain(domain)
|
|
63
|
-
|
|
63
|
+
log.debug "[epistemic_curiosity] by_domain: domain=#{domain} count=#{gaps.size}"
|
|
64
64
|
{ gaps: gaps.map(&:to_h), count: gaps.size, domain: domain }
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def gaps_by_type(gap_type:, **)
|
|
68
68
|
gaps = engine.by_type(gap_type)
|
|
69
|
-
|
|
69
|
+
log.debug "[epistemic_curiosity] by_type: gap_type=#{gap_type} count=#{gaps.size}"
|
|
70
70
|
{ gaps: gaps.map(&:to_h), count: gaps.size, gap_type: gap_type }
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def decay_gaps(**)
|
|
74
74
|
count = engine.decay_all
|
|
75
|
-
|
|
75
|
+
log.debug "[epistemic_curiosity] decay cycle: gaps_updated=#{count}"
|
|
76
76
|
{ decayed: count }
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def curiosity_report(**)
|
|
80
80
|
report = engine.curiosity_report
|
|
81
|
-
|
|
81
|
+
log.debug "[epistemic_curiosity] report: open=#{report[:open_gaps]} debt=#{report[:information_debt]}"
|
|
82
82
|
report
|
|
83
83
|
end
|
|
84
84
|
|
|
@@ -11,10 +11,10 @@ module Legion
|
|
|
11
11
|
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
12
12
|
|
|
13
13
|
def observe_action(action:, context: {}, **)
|
|
14
|
-
|
|
14
|
+
log.debug "[habit] observe_action: action=#{action} context=#{context}"
|
|
15
15
|
habit_store.record_action(action, context: context)
|
|
16
16
|
detected = habit_store.detect_patterns
|
|
17
|
-
|
|
17
|
+
log.info "[habit] patterns detected: #{detected.size}" unless detected.empty?
|
|
18
18
|
{
|
|
19
19
|
recorded: true,
|
|
20
20
|
action: action,
|
|
@@ -25,7 +25,7 @@ module Legion
|
|
|
25
25
|
|
|
26
26
|
def suggest_habit(context: {}, **)
|
|
27
27
|
matches = habit_store.find_matching(context: context)
|
|
28
|
-
|
|
28
|
+
log.debug "[habit] suggest_habit: context=#{context} matches=#{matches.size}"
|
|
29
29
|
if matches.empty?
|
|
30
30
|
{ suggestion: nil, reason: :no_matching_habits }
|
|
31
31
|
else
|
|
@@ -43,19 +43,19 @@ module Legion
|
|
|
43
43
|
return { error: :not_found } unless habit
|
|
44
44
|
|
|
45
45
|
habit.record_execution(success: success)
|
|
46
|
-
|
|
46
|
+
log.debug "[habit] execute_habit: id=#{id} success=#{success} maturity=#{habit.maturity}"
|
|
47
47
|
{ executed: true, habit: habit.to_h, cognitive_cost: habit.cognitive_cost }
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def decay_habits(**)
|
|
51
51
|
removed = habit_store.decay_all
|
|
52
|
-
|
|
52
|
+
log.debug "[habit] decay_habits: removed=#{removed}"
|
|
53
53
|
{ decayed: true, removed_count: removed }
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def merge_habits(**)
|
|
57
57
|
merged = habit_store.merge_similar
|
|
58
|
-
|
|
58
|
+
log.debug "[habit] merge_habits: merged=#{merged}"
|
|
59
59
|
{ merged_count: merged }
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -65,7 +65,7 @@ module Legion
|
|
|
65
65
|
|
|
66
66
|
def habit_repertoire(maturity: nil, limit: 20, **)
|
|
67
67
|
habits = maturity ? habit_store.by_maturity(maturity.to_sym) : habit_store.habits.values
|
|
68
|
-
|
|
68
|
+
log.debug "[habit] habit_repertoire: maturity=#{maturity} total=#{habits.size}"
|
|
69
69
|
{
|
|
70
70
|
habits: habits.sort_by { |h| -h.strength }.first(limit).map(&:to_h),
|
|
71
71
|
total: habits.size
|
|
@@ -11,14 +11,14 @@ module Legion
|
|
|
11
11
|
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
12
12
|
|
|
13
13
|
def activate_unit(id:, level: 1.0, domain: :general, **)
|
|
14
|
-
|
|
14
|
+
log.debug "[hebbian] activate: id=#{id} level=#{level}"
|
|
15
15
|
network.add_unit(id: id, domain: domain)
|
|
16
16
|
unit = network.activate_unit(id: id, level: level)
|
|
17
17
|
{ success: true, unit: unit.to_h, assemblies: network.assembly_count }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def co_activate_units(ids:, level: 1.0, **)
|
|
21
|
-
|
|
21
|
+
log.debug "[hebbian] co_activate: ids=#{ids}"
|
|
22
22
|
results = network.co_activate(ids: ids, level: level)
|
|
23
23
|
{ success: true, units: results, assemblies: network.assembly_count }
|
|
24
24
|
end
|
|
@@ -27,19 +27,19 @@ module Legion
|
|
|
27
27
|
w = network.query_weight(from: from, to: to)
|
|
28
28
|
label = Helpers::Constants::WEIGHT_LABELS.each { |range, l| break l if range.cover?(w) }
|
|
29
29
|
label = :nascent unless label.is_a?(Symbol)
|
|
30
|
-
|
|
30
|
+
log.debug "[hebbian] weight: #{from}->#{to} = #{w}"
|
|
31
31
|
{ success: true, from: from, to: to, weight: w.round(4), label: label }
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def list_assemblies(**)
|
|
35
35
|
assemblies = network.assemblies.values.map(&:to_h)
|
|
36
|
-
|
|
36
|
+
log.debug "[hebbian] list_assemblies: #{assemblies.size}"
|
|
37
37
|
{ success: true, assemblies: assemblies, count: assemblies.size }
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def query_assembly(id:, **)
|
|
41
41
|
asm = network.query_assembly(id: id.to_sym)
|
|
42
|
-
|
|
42
|
+
log.debug "[hebbian] query_assembly: id=#{id} found=#{!asm.nil?}"
|
|
43
43
|
if asm
|
|
44
44
|
{ success: true, assembly: asm.to_h }
|
|
45
45
|
else
|
|
@@ -48,7 +48,7 @@ module Legion
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def pattern_complete(partial_ids:, **)
|
|
51
|
-
|
|
51
|
+
log.debug "[hebbian] pattern_complete: partial=#{partial_ids}"
|
|
52
52
|
result = network.pattern_complete(partial_ids: partial_ids)
|
|
53
53
|
if result
|
|
54
54
|
{ success: true, completion: result }
|
|
@@ -59,24 +59,24 @@ module Legion
|
|
|
59
59
|
|
|
60
60
|
def strongest_units(limit: 10, **)
|
|
61
61
|
units = network.strongest_units(limit.to_i)
|
|
62
|
-
|
|
62
|
+
log.debug "[hebbian] strongest_units: #{units.size}"
|
|
63
63
|
{ success: true, units: units }
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def assemblies_for(unit_id:, **)
|
|
67
67
|
asms = network.assemblies_containing(unit_id: unit_id).map(&:to_h)
|
|
68
|
-
|
|
68
|
+
log.debug "[hebbian] assemblies_for: unit=#{unit_id} count=#{asms.size}"
|
|
69
69
|
{ success: true, assemblies: asms, count: asms.size }
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def update_hebbian(**)
|
|
73
|
-
|
|
73
|
+
log.debug '[hebbian] decay tick'
|
|
74
74
|
network.decay_all
|
|
75
75
|
{ success: true, units: network.unit_count, assemblies: network.assembly_count }
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def hebbian_stats(**)
|
|
79
|
-
|
|
79
|
+
log.debug '[hebbian] stats'
|
|
80
80
|
{ success: true, stats: network.to_h }
|
|
81
81
|
end
|
|
82
82
|
|
|
@@ -14,7 +14,7 @@ module Legion
|
|
|
14
14
|
rate_model.record_prediction(domain: domain, correct: correct)
|
|
15
15
|
rate = rate_model.rate_for(domain)
|
|
16
16
|
accuracy = rate_model.accuracy_for(domain)
|
|
17
|
-
|
|
17
|
+
log.debug "[learning_rate] prediction: domain=#{domain} correct=#{correct} rate=#{rate.round(3)} accuracy=#{accuracy.round(3)}"
|
|
18
18
|
{
|
|
19
19
|
success: true,
|
|
20
20
|
domain: domain,
|
|
@@ -27,14 +27,14 @@ module Legion
|
|
|
27
27
|
def record_surprise(magnitude:, domain: :general, **)
|
|
28
28
|
rate_model.record_surprise(domain: domain, magnitude: magnitude)
|
|
29
29
|
rate = rate_model.rate_for(domain)
|
|
30
|
-
|
|
30
|
+
log.debug "[learning_rate] surprise: domain=#{domain} magnitude=#{magnitude.round(3)} rate=#{rate.round(3)}"
|
|
31
31
|
{ success: true, domain: domain, rate: rate, label: rate_model.label_for(domain) }
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def record_error(magnitude:, domain: :general, **)
|
|
35
35
|
rate_model.record_error(domain: domain, magnitude: magnitude)
|
|
36
36
|
rate = rate_model.rate_for(domain)
|
|
37
|
-
|
|
37
|
+
log.debug "[learning_rate] error: domain=#{domain} magnitude=#{magnitude.round(3)} rate=#{rate.round(3)}"
|
|
38
38
|
{ success: true, domain: domain, rate: rate, label: rate_model.label_for(domain) }
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -63,7 +63,7 @@ module Legion
|
|
|
63
63
|
def update_learning_rate(**)
|
|
64
64
|
rate_model.decay
|
|
65
65
|
overall = rate_model.overall_rate
|
|
66
|
-
|
|
66
|
+
log.debug "[learning_rate] tick: domains=#{rate_model.domain_count} overall=#{overall.round(3)}"
|
|
67
67
|
{ success: true, domain_count: rate_model.domain_count, overall_rate: overall }
|
|
68
68
|
end
|
|
69
69
|
|
|
@@ -14,77 +14,77 @@ module Legion
|
|
|
14
14
|
related_domains: [], **)
|
|
15
15
|
result = engine.create_domain(name: name, learning_rate: learning_rate, related_domains: related_domains)
|
|
16
16
|
if result.is_a?(Hash) && result[:error]
|
|
17
|
-
|
|
17
|
+
log.warn "[meta_learning] create_domain failed: #{result[:error]}"
|
|
18
18
|
return result
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
log.debug "[meta_learning] domain created: #{result.name} id=#{result.id[0..7]}"
|
|
22
22
|
result.to_h
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def register_learning_strategy(name:, strategy_type:, **)
|
|
26
26
|
result = engine.create_strategy(name: name, strategy_type: strategy_type)
|
|
27
27
|
if result.is_a?(Hash) && result[:error]
|
|
28
|
-
|
|
28
|
+
log.warn "[meta_learning] create_strategy failed: #{result[:error]}"
|
|
29
29
|
return result
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
log.debug "[meta_learning] strategy registered: #{result.name} type=#{result.strategy_type}"
|
|
33
33
|
result.to_h
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def record_learning_episode(domain_id:, success:, strategy_id: nil, **)
|
|
37
37
|
result = engine.record_episode(domain_id: domain_id, strategy_id: strategy_id, success: success)
|
|
38
38
|
if result.is_a?(Hash) && result[:error]
|
|
39
|
-
|
|
39
|
+
log.warn "[meta_learning] record_episode failed: #{result[:error]}"
|
|
40
40
|
return result
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
log.debug "[meta_learning] episode recorded domain=#{result[:domain_name]} " \
|
|
44
|
+
"success=#{success} proficiency=#{result[:proficiency].round(4)}"
|
|
45
45
|
result
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def recommend_learning_strategy(domain_id:, **)
|
|
49
49
|
result = engine.recommend_strategy(domain_id: domain_id)
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
log.debug "[meta_learning] strategy recommendation domain=#{domain_id[0..7]} " \
|
|
51
|
+
"recommendation=#{result[:recommendation]}"
|
|
52
52
|
result
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def check_transfer_learning(source_domain_id:, target_domain_id:, **)
|
|
56
56
|
result = engine.transfer_check(source_domain_id: source_domain_id, target_domain_id: target_domain_id)
|
|
57
|
-
|
|
57
|
+
log.debug "[meta_learning] transfer check eligible=#{result[:eligible]}"
|
|
58
58
|
result
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def apply_transfer_bonus(source_domain_id:, target_domain_id:, **)
|
|
62
62
|
result = engine.apply_transfer(source_domain_id: source_domain_id, target_domain_id: target_domain_id)
|
|
63
|
-
|
|
63
|
+
log.info "[meta_learning] transfer applied=#{result[:applied]}"
|
|
64
64
|
result
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def learning_domain_ranking(limit: 10, **)
|
|
68
68
|
ranking = engine.domain_ranking(limit: limit)
|
|
69
|
-
|
|
69
|
+
log.debug "[meta_learning] domain ranking returned #{ranking.size} domains"
|
|
70
70
|
{ ranking: ranking, count: ranking.size }
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def learning_strategy_ranking(limit: 10, **)
|
|
74
74
|
ranking = engine.strategy_ranking(limit: limit)
|
|
75
|
-
|
|
75
|
+
log.debug "[meta_learning] strategy ranking returned #{ranking.size} strategies"
|
|
76
76
|
{ ranking: ranking, count: ranking.size }
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def learning_curve_report(domain_id:, **)
|
|
80
80
|
result = engine.learning_curve(domain_id: domain_id)
|
|
81
81
|
if result.is_a?(Hash) && result[:error]
|
|
82
|
-
|
|
82
|
+
log.warn "[meta_learning] learning_curve failed: #{result[:error]}"
|
|
83
83
|
return result
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
log.debug "[meta_learning] learning curve domain=#{result[:domain]} " \
|
|
87
|
+
"episodes=#{result[:curve].size}"
|
|
88
88
|
result
|
|
89
89
|
end
|
|
90
90
|
|
|
@@ -92,15 +92,15 @@ module Legion
|
|
|
92
92
|
adapt_result = engine.adapt_rates
|
|
93
93
|
prune_result = engine.prune_stale_domains
|
|
94
94
|
stats = engine.to_h
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
log.info "[meta_learning] update: adapted=#{adapt_result[:count]} " \
|
|
96
|
+
"pruned=#{prune_result[:pruned]} domains=#{stats[:domain_count]}"
|
|
97
97
|
{ adapt: adapt_result, prune: prune_result, stats: stats }
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def meta_learning_stats(**)
|
|
101
101
|
stats = engine.to_h
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
log.debug "[meta_learning] stats domains=#{stats[:domain_count]} " \
|
|
103
|
+
"strategies=#{stats[:strategy_count]} efficiency=#{stats[:overall_efficiency]}"
|
|
104
104
|
stats
|
|
105
105
|
end
|
|
106
106
|
|
data/lib/legion/extensions/agentic/learning/preference_learning/runners/preference_learning.rb
CHANGED
|
@@ -13,9 +13,9 @@ module Legion
|
|
|
13
13
|
def register_preference_option(label:, domain: :general, **)
|
|
14
14
|
result = preference_engine.register_option(label: label, domain: domain)
|
|
15
15
|
if result[:error]
|
|
16
|
-
|
|
16
|
+
log.warn "[preference_learning] register failed: #{result[:error]}"
|
|
17
17
|
else
|
|
18
|
-
|
|
18
|
+
log.debug "[preference_learning] registered option id=#{result[:id]} label=#{label} domain=#{domain}"
|
|
19
19
|
end
|
|
20
20
|
result
|
|
21
21
|
end
|
|
@@ -23,9 +23,9 @@ module Legion
|
|
|
23
23
|
def record_preference_comparison(winner_id:, loser_id:, **)
|
|
24
24
|
result = preference_engine.record_comparison(winner_id: winner_id, loser_id: loser_id)
|
|
25
25
|
if result[:error]
|
|
26
|
-
|
|
26
|
+
log.warn "[preference_learning] comparison failed: #{result[:error]}"
|
|
27
27
|
else
|
|
28
|
-
|
|
28
|
+
log.info "[preference_learning] comparison: winner=#{winner_id} loser=#{loser_id} total=#{result[:comparisons]}"
|
|
29
29
|
end
|
|
30
30
|
result
|
|
31
31
|
end
|
|
@@ -33,35 +33,35 @@ module Legion
|
|
|
33
33
|
def predict_preference_outcome(option_a_id:, option_b_id:, **)
|
|
34
34
|
result = preference_engine.predict_preference(option_a_id: option_a_id, option_b_id: option_b_id)
|
|
35
35
|
if result[:error]
|
|
36
|
-
|
|
36
|
+
log.warn "[preference_learning] predict failed: #{result[:error]}"
|
|
37
37
|
else
|
|
38
|
-
|
|
38
|
+
log.debug "[preference_learning] predict: preferred=#{result[:preferred_label]} confidence=#{result[:confidence].round(2)}"
|
|
39
39
|
end
|
|
40
40
|
result
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def top_preferences_report(domain: nil, limit: 5, **)
|
|
44
44
|
options = preference_engine.top_preferences(domain: domain, limit: limit)
|
|
45
|
-
|
|
45
|
+
log.debug "[preference_learning] top #{limit} preferences domain=#{domain.inspect} count=#{options.size}"
|
|
46
46
|
{ domain: domain, limit: limit, options: options }
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def preference_stability_report(**)
|
|
50
50
|
stability = preference_engine.preference_stability
|
|
51
51
|
label = stability < 0.1 ? :stable : :variable
|
|
52
|
-
|
|
52
|
+
log.debug "[preference_learning] stability=#{stability.round(4)} label=#{label}"
|
|
53
53
|
{ stability: stability, label: label }
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def update_preference_learning(**)
|
|
57
57
|
count = preference_engine.decay_all
|
|
58
|
-
|
|
58
|
+
log.debug "[preference_learning] decay cycle: options_updated=#{count}"
|
|
59
59
|
{ decayed: count }
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def preference_learning_stats(**)
|
|
63
63
|
engine_hash = preference_engine.to_h
|
|
64
|
-
|
|
64
|
+
log.debug "[preference_learning] stats: total_options=#{engine_hash[:total_options]} comparisons=#{engine_hash[:comparisons]}"
|
|
65
65
|
engine_hash.merge(stability_label: preference_engine_stability_label)
|
|
66
66
|
end
|
|
67
67
|
|
|
@@ -12,8 +12,8 @@ module Legion
|
|
|
12
12
|
|
|
13
13
|
def create_skill(name:, domain:, **)
|
|
14
14
|
skill = engine.create_skill(name: name, domain: domain)
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
log.debug "[procedural_learning] created skill=#{name} " \
|
|
16
|
+
"domain=#{domain} id=#{skill.id[0..7]}"
|
|
17
17
|
{ success: true, skill_id: skill.id, name: name, domain: domain,
|
|
18
18
|
proficiency: skill.proficiency, stage: skill.stage }
|
|
19
19
|
end
|
|
@@ -26,59 +26,59 @@ module Legion
|
|
|
26
26
|
|
|
27
27
|
return result unless result.is_a?(Helpers::Production)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
log.debug '[procedural_learning] production added ' \
|
|
30
|
+
"skill=#{skill_id[0..7]} condition=#{condition}"
|
|
31
31
|
{ success: true, production_id: result.id, skill_id: skill_id }
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def practice_skill(skill_id:, success:, **)
|
|
35
35
|
result = engine.practice_skill(skill_id: skill_id, success: success)
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
log.debug "[procedural_learning] practice skill=#{skill_id[0..7]} " \
|
|
37
|
+
"success=#{success} proficiency=#{result[:proficiency]&.round(3)}"
|
|
38
38
|
result
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def execute_production(production_id:, success:, **)
|
|
42
42
|
result = engine.execute_production(production_id: production_id, success: success)
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
log.debug "[procedural_learning] execute production=#{production_id[0..7]} " \
|
|
44
|
+
"success=#{success}"
|
|
45
45
|
result
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def skill_assessment(skill_id:, **)
|
|
49
49
|
result = engine.skill_assessment(skill_id: skill_id)
|
|
50
|
-
|
|
50
|
+
log.debug "[procedural_learning] assessment skill=#{skill_id[0..7]}"
|
|
51
51
|
result
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def compiled_skills(**)
|
|
55
55
|
skills = engine.compiled_skills
|
|
56
|
-
|
|
56
|
+
log.debug "[procedural_learning] compiled count=#{skills.size}"
|
|
57
57
|
{ success: true, skills: skills.map(&:to_h), count: skills.size }
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def autonomous_skills(**)
|
|
61
61
|
skills = engine.autonomous_skills
|
|
62
|
-
|
|
62
|
+
log.debug "[procedural_learning] autonomous count=#{skills.size}"
|
|
63
63
|
{ success: true, skills: skills.map(&:to_h), count: skills.size }
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def most_practiced_skills(limit: 5, **)
|
|
67
67
|
skills = engine.most_practiced(limit: limit)
|
|
68
|
-
|
|
68
|
+
log.debug "[procedural_learning] most_practiced limit=#{limit}"
|
|
69
69
|
{ success: true, skills: skills.map(&:to_h), count: skills.size }
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def update_procedural_learning(**)
|
|
73
73
|
engine.decay_all
|
|
74
74
|
pruned = engine.prune_stale
|
|
75
|
-
|
|
75
|
+
log.debug "[procedural_learning] decay+prune pruned=#{pruned}"
|
|
76
76
|
{ success: true, pruned: pruned }
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def procedural_learning_stats(**)
|
|
80
80
|
stats = engine.to_h
|
|
81
|
-
|
|
81
|
+
log.debug "[procedural_learning] stats total=#{stats[:total_skills]}"
|
|
82
82
|
{ success: true }.merge(stats)
|
|
83
83
|
end
|
|
84
84
|
|
|
@@ -13,83 +13,83 @@ module Legion
|
|
|
13
13
|
def create_scaffold(skill_name:, domain:, competence: nil, **)
|
|
14
14
|
comp = competence || Helpers::Constants::DEFAULT_COMPETENCE
|
|
15
15
|
scaffold = engine.create_scaffold(skill_name: skill_name, domain: domain, competence: comp)
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
log.debug "[cognitive_scaffolding] created: skill=#{skill_name} domain=#{domain} " \
|
|
17
|
+
"competence=#{scaffold.competence.round(2)} zone=#{scaffold.current_zone}"
|
|
18
18
|
{ success: true, scaffold: scaffold.to_h }
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def attempt_scaffolded_task(scaffold_id:, difficulty:, success:, **)
|
|
22
22
|
scaffold = engine.attempt_scaffolded_task(scaffold_id: scaffold_id, difficulty: difficulty, success: success)
|
|
23
23
|
unless scaffold
|
|
24
|
-
|
|
24
|
+
log.debug "[cognitive_scaffolding] attempt: scaffold_id=#{scaffold_id} not found"
|
|
25
25
|
return { success: false, reason: :not_found, scaffold_id: scaffold_id }
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
log.debug "[cognitive_scaffolding] attempt: skill=#{scaffold.skill_name} " \
|
|
29
|
+
"difficulty=#{difficulty.round(2)} success=#{success} " \
|
|
30
|
+
"competence=#{scaffold.competence.round(2)} zone=#{scaffold.current_zone}"
|
|
31
31
|
{ success: true, scaffold: scaffold.to_h }
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def recommend_scaffolded_task(scaffold_id:, **)
|
|
35
35
|
recommendation = engine.recommend_task(scaffold_id: scaffold_id)
|
|
36
36
|
unless recommendation
|
|
37
|
-
|
|
37
|
+
log.debug "[cognitive_scaffolding] recommend: scaffold_id=#{scaffold_id} not found"
|
|
38
38
|
return { success: false, reason: :not_found, scaffold_id: scaffold_id }
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
log.debug "[cognitive_scaffolding] recommend: difficulty=#{recommendation[:difficulty].round(2)} " \
|
|
42
|
+
"support=#{recommendation[:support_level]} zone=#{recommendation[:zone]}"
|
|
43
43
|
{ success: true, recommendation: recommendation }
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def mastered_scaffolded_skills(**)
|
|
47
47
|
skills = engine.mastered_skills
|
|
48
|
-
|
|
48
|
+
log.debug "[cognitive_scaffolding] mastered: count=#{skills.size}"
|
|
49
49
|
{ success: true, skills: skills.map(&:to_h), count: skills.size }
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def zpd_skills(**)
|
|
53
53
|
skills = engine.zpd_skills
|
|
54
|
-
|
|
54
|
+
log.debug "[cognitive_scaffolding] zpd: count=#{skills.size}"
|
|
55
55
|
{ success: true, skills: skills.map(&:to_h), count: skills.size }
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def domain_scaffolds(domain:, **)
|
|
59
59
|
scaffolds = engine.by_domain(domain: domain)
|
|
60
|
-
|
|
60
|
+
log.debug "[cognitive_scaffolding] domain: domain=#{domain} count=#{scaffolds.size}"
|
|
61
61
|
{ success: true, domain: domain, scaffolds: scaffolds.map(&:to_h), count: scaffolds.size }
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def adjust_scaffold_support(scaffold_id:, direction:, **)
|
|
65
65
|
scaffold = engine.adjust_support(scaffold_id: scaffold_id, direction: direction)
|
|
66
66
|
unless scaffold
|
|
67
|
-
|
|
67
|
+
log.debug "[cognitive_scaffolding] adjust_support: scaffold_id=#{scaffold_id} not found"
|
|
68
68
|
return { success: false, reason: :not_found, scaffold_id: scaffold_id }
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
log.debug "[cognitive_scaffolding] adjust_support: skill=#{scaffold.skill_name} " \
|
|
72
|
+
"direction=#{direction} support=#{scaffold.support_level}"
|
|
73
73
|
{ success: true, scaffold: scaffold.to_h }
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def overall_scaffolded_competence(**)
|
|
77
77
|
overall = engine.overall_competence
|
|
78
|
-
|
|
78
|
+
log.debug "[cognitive_scaffolding] overall_competence: #{overall.round(3)}"
|
|
79
79
|
{ success: true, overall_competence: overall.round(4) }
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
def update_cognitive_scaffolding(**)
|
|
83
83
|
count = engine.decay_all
|
|
84
84
|
overall = engine.overall_competence
|
|
85
|
-
|
|
85
|
+
log.debug "[cognitive_scaffolding] decay: scaffolds=#{count} overall=#{overall.round(3)}"
|
|
86
86
|
{ success: true, action: :decay, scaffold_count: count, overall_competence: overall.round(4) }
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def cognitive_scaffolding_stats(**)
|
|
90
90
|
stats = engine.to_h
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
log.debug "[cognitive_scaffolding] stats: total=#{stats[:total_scaffolds]} " \
|
|
92
|
+
"mastered=#{stats[:mastered_count]} zpd=#{stats[:zpd_count]}"
|
|
93
93
|
{ success: true, stats: stats }
|
|
94
94
|
end
|
|
95
95
|
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'bundler/setup'
|
|
4
|
+
require 'legion/logging'
|
|
5
|
+
require 'legion/settings'
|
|
6
|
+
require 'legion/cache/helper'
|
|
7
|
+
require 'legion/crypt/helper'
|
|
8
|
+
require 'legion/data/helper'
|
|
9
|
+
require 'legion/json/helper'
|
|
10
|
+
require 'legion/transport'
|
|
4
11
|
|
|
5
12
|
module Legion
|
|
6
|
-
module Logging
|
|
7
|
-
def self.debug(_msg); end
|
|
8
|
-
def self.info(_msg); end
|
|
9
|
-
def self.warn(_msg); end
|
|
10
|
-
def self.error(_msg); end
|
|
11
|
-
def self.fatal(_msg); end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
13
|
module Extensions
|
|
15
|
-
module Core
|
|
16
|
-
def self.extended(_base); end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
14
|
module Helpers
|
|
20
15
|
module Lex
|
|
21
|
-
|
|
16
|
+
include Legion::Logging::Helper
|
|
17
|
+
include Legion::Settings::Helper
|
|
18
|
+
include Legion::Cache::Helper
|
|
19
|
+
include Legion::Crypt::Helper
|
|
20
|
+
include Legion::Data::Helper
|
|
21
|
+
include Legion::JSON::Helper
|
|
22
|
+
include Legion::Transport::Helper
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
25
|
|
|
27
|
-
# rubocop:disable Lint/EmptyClass, Style/OneClassPerFile
|
|
28
|
-
module Legion
|
|
29
|
-
module Extensions
|
|
30
26
|
module Actors
|
|
31
|
-
class Every
|
|
32
|
-
|
|
27
|
+
class Every
|
|
28
|
+
include Helpers::Lex
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Once
|
|
32
|
+
include Helpers::Lex
|
|
33
|
+
end
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
$LOADED_FEATURES << 'legion/extensions/actors/every'
|
|
37
38
|
$LOADED_FEATURES << 'legion/extensions/actors/once'
|
|
38
|
-
# rubocop:enable Lint/EmptyClass, Style/OneClassPerFile
|
|
39
39
|
|
|
40
40
|
require 'legion/extensions/agentic/learning'
|
|
41
41
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-agentic-learning
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -9,6 +9,104 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-cache
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 1.3.11
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 1.3.11
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: legion-crypt
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.4.9
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 1.4.9
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: legion-data
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 1.4.17
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 1.4.17
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: legion-json
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 1.2.1
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 1.2.1
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: legion-logging
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 1.3.2
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 1.3.2
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: legion-settings
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 1.3.14
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 1.3.14
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: legion-transport
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 1.3.9
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 1.3.9
|
|
12
110
|
- !ruby/object:Gem::Dependency
|
|
13
111
|
name: rspec
|
|
14
112
|
requirement: !ruby/object:Gem::Requirement
|