lex-agentic-learning 0.1.0 → 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 +13 -0
- data/README.md +41 -3
- 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/fermentation/helpers/fermentation_engine.rb +4 -0
- data/lib/legion/extensions/agentic/learning/fermentation/version.rb +1 -1
- 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/legion/extensions/agentic/learning/fermentation/helpers/fermentation_engine_spec.rb +26 -0
- data/spec/spec_helper.rb +21 -21
- metadata +99 -1
|
@@ -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/legion/extensions/agentic/learning/fermentation/helpers/fermentation_engine_spec.rb
CHANGED
|
@@ -26,6 +26,18 @@ RSpec.describe Legion::Extensions::Agentic::Learning::Fermentation::Helpers::Fer
|
|
|
26
26
|
engine.create_substrate(substrate_type: :raw_idea, domain: :emotional)
|
|
27
27
|
expect(engine.fermentation_report[:total_batches]).to eq(2)
|
|
28
28
|
end
|
|
29
|
+
|
|
30
|
+
it 'rejects invalid substrate_type' do
|
|
31
|
+
expect(engine.create_substrate(substrate_type: :telekinesis, domain: :cognitive)).to be_nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'accepts all valid SUBSTRATE_TYPES' do
|
|
35
|
+
constants = Legion::Extensions::Agentic::Learning::Fermentation::Helpers::Constants
|
|
36
|
+
constants::SUBSTRATE_TYPES.each do |st|
|
|
37
|
+
result = engine.create_substrate(substrate_type: st, domain: :cognitive)
|
|
38
|
+
expect(result).not_to be_nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
29
41
|
end
|
|
30
42
|
|
|
31
43
|
describe '#ferment' do
|
|
@@ -50,6 +62,20 @@ RSpec.describe Legion::Extensions::Agentic::Learning::Fermentation::Helpers::Fer
|
|
|
50
62
|
it 'returns nil for unknown substrate' do
|
|
51
63
|
expect(engine.catalyze(substrate_id: 'x', catalyst_type: :analogy)).to be_nil
|
|
52
64
|
end
|
|
65
|
+
|
|
66
|
+
it 'rejects invalid catalyst_type' do
|
|
67
|
+
sub = engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive)
|
|
68
|
+
expect(engine.catalyze(substrate_id: sub.id, catalyst_type: :magic)).to be_nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'accepts all valid CATALYST_TYPES' do
|
|
72
|
+
constants = Legion::Extensions::Agentic::Learning::Fermentation::Helpers::Constants
|
|
73
|
+
sub = engine.create_substrate(substrate_type: :raw_idea, domain: :cognitive, potency: 0.1)
|
|
74
|
+
constants::CATALYST_TYPES.each do |ct|
|
|
75
|
+
result = engine.catalyze(substrate_id: sub.id, catalyst_type: ct)
|
|
76
|
+
expect(result).not_to be_nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
53
79
|
end
|
|
54
80
|
|
|
55
81
|
describe '#ferment_all!' do
|
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
|
|