lex-mind-growth 0.1.7 → 0.1.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2182f87f0f59f727c61f209083c225b1b31d9cbb5589af437618350433e8b692
4
- data.tar.gz: 1ee0428c83686efdb168054b16d68af66443dfa60bd5f0bd8e2725f4d7d87ab4
3
+ metadata.gz: 87d35372e2ef409b603807723d816df60439d40bc8f4289d48799d31b731d232
4
+ data.tar.gz: fd49c0c8bb06f49374cbbb3afb497f45ee58193e71ff66b1ef1c622b94bbeaa6
5
5
  SHA512:
6
- metadata.gz: 1e7320c15ecd31d531bd961b03f20baa8c30a77946f98e58c25557ebcd1027b42b7c9af7c9ce8ae781089e25966a76d183209922ce8b2e7a780c141853cf6d96
7
- data.tar.gz: b7abcbc0ea3109d64a4cc1cda9508ea0ee8ab777271e205abc1ae4903d1f2bf349b78053aa071fe5835790a790f4974be95800cf57ad2bfa8ab96182b358d312
6
+ metadata.gz: d3c07088c0173a6e5669af58bf3fbd315f2ef9098fa9e3f3f65228815159e6a061860c05aba14b75a0f0dcd9f01aea50f5a89ec9455a5796484037c6e1571328
7
+ data.tar.gz: 98b9b07557efbb5a3546a4018300ab984a0bfecc24ae93ab2534c35c90b991877d5564863460a75137525b262c996b7536ca14c6bbd20f77671225a8501ba402
@@ -47,6 +47,27 @@ module Legion
47
47
  # RiskAssessor delegation
48
48
  def assess_risk(**) = Runners::RiskAssessor.assess_risk(**)
49
49
  def risk_summary(**) = Runners::RiskAssessor.risk_summary(**)
50
+
51
+ # Monitor delegation
52
+ def health_check(**) = Runners::Monitor.health_check(**)
53
+ def usage_stats(**) = Runners::Monitor.usage_stats(**)
54
+ def impact_score(**) = Runners::Monitor.impact_score(**)
55
+ def decay_check(**) = Runners::Monitor.decay_check(**)
56
+ def auto_prune(**) = Runners::Monitor.auto_prune(**)
57
+ def health_summary(**) = Runners::Monitor.health_summary(**)
58
+
59
+ # Composer delegation
60
+ def add_composition(**) = Runners::Composer.add_composition(**)
61
+ def remove_composition(**) = Runners::Composer.remove_composition(**)
62
+ def evaluate_output(**) = Runners::Composer.evaluate_output(**)
63
+ def composition_stats(**) = Runners::Composer.composition_stats(**)
64
+ def suggest_compositions(**) = Runners::Composer.suggest_compositions(**)
65
+ def list_compositions(**) = Runners::Composer.list_compositions(**)
66
+
67
+ # DreamIdeation delegation
68
+ def generate_dream_proposals(**) = Runners::DreamIdeation.generate_dream_proposals(**)
69
+ def dream_agenda_items(**) = Runners::DreamIdeation.dream_agenda_items(**)
70
+ def enrich_from_dream_context(**) = Runners::DreamIdeation.enrich_from_dream_context(**)
50
71
  end
51
72
  end
52
73
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Helpers
7
+ module CompositionMap
8
+ module_function
9
+
10
+ @rules = {}
11
+ @mutex = Mutex.new
12
+
13
+ def add_rule(source_extension:, output_key:, target_extension:, target_method:, transform: nil, **)
14
+ rule_id = SecureRandom.uuid
15
+ rule = {
16
+ id: rule_id,
17
+ source_extension: source_extension.to_s,
18
+ output_key: output_key.to_sym,
19
+ target_extension: target_extension.to_s,
20
+ target_method: target_method.to_sym,
21
+ transform: transform
22
+ }
23
+ @mutex.synchronize { @rules[rule_id] = rule }
24
+ { success: true, rule_id: rule_id }
25
+ end
26
+
27
+ def remove_rule(rule_id:, **)
28
+ removed = @mutex.synchronize { @rules.delete(rule_id) }
29
+ { success: !removed.nil?, rule_id: rule_id }
30
+ end
31
+
32
+ def rules_for(source_extension:, **)
33
+ src = source_extension.to_s
34
+ @mutex.synchronize { @rules.values.select { |r| r[:source_extension] == src } }
35
+ end
36
+
37
+ def all_rules
38
+ @mutex.synchronize { @rules.values.dup }
39
+ end
40
+
41
+ def match_output(source_extension:, output:, **)
42
+ src = source_extension.to_s
43
+ out_h = output.is_a?(Hash) ? output : {}
44
+ rules = @mutex.synchronize { @rules.values.select { |r| r[:source_extension] == src } }
45
+
46
+ rules.filter_map do |rule|
47
+ key = rule[:output_key]
48
+ next unless out_h.key?(key)
49
+
50
+ { rule: rule, matched_value: out_h[key] }
51
+ end
52
+ end
53
+
54
+ def clear!
55
+ @mutex.synchronize { @rules.clear }
56
+ end
57
+
58
+ def stats
59
+ all = @mutex.synchronize { @rules.values.dup }
60
+
61
+ by_source = Hash.new(0)
62
+ by_target = Hash.new(0)
63
+ all.each do |r|
64
+ by_source[r[:source_extension]] += 1
65
+ by_target[r[:target_extension]] += 1
66
+ end
67
+
68
+ { total_rules: all.size,
69
+ by_source: by_source.transform_values { |v| v },
70
+ by_target: by_target.transform_values { |v| v } }
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -54,6 +54,10 @@ module Legion
54
54
  REJECTION_COOLDOWN_HOURS = 24
55
55
  GOVERNANCE_STATUSES = %i[pending approved rejected expired].freeze
56
56
 
57
+ # Health monitoring
58
+ HEALTH_LEVELS = { excellent: 0.8, good: 0.6, fair: 0.4, degraded: 0.2, critical: 0.0 }.freeze
59
+ DECAY_INVOCATION_THRESHOLD = 5
60
+
57
61
  # Risk assessment
58
62
  RISK_TIERS = %i[low medium high critical].freeze
59
63
  RISK_RECOMMENDATIONS = {
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Runners
7
+ module Composer
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ extend self
12
+
13
+ # Category adjacency used for heuristic suggestions
14
+ CATEGORY_FLOW = [
15
+ %i[perception cognition],
16
+ %i[cognition memory],
17
+ %i[cognition introspection],
18
+ %i[memory cognition],
19
+ %i[introspection safety],
20
+ %i[motivation cognition],
21
+ %i[cognition communication],
22
+ %i[communication coordination]
23
+ ].freeze
24
+
25
+ def add_composition(source_extension:, output_key:, target_extension:, target_method:,
26
+ transform: nil, **)
27
+ Helpers::CompositionMap.add_rule(
28
+ source_extension: source_extension,
29
+ output_key: output_key,
30
+ target_extension: target_extension,
31
+ target_method: target_method,
32
+ transform: transform
33
+ )
34
+ end
35
+
36
+ def remove_composition(rule_id:, **)
37
+ result = Helpers::CompositionMap.remove_rule(rule_id: rule_id)
38
+ { success: result[:success] }
39
+ end
40
+
41
+ def evaluate_output(source_extension:, output:, **)
42
+ matches = Helpers::CompositionMap.match_output(
43
+ source_extension: source_extension,
44
+ output: output
45
+ )
46
+
47
+ dispatches = matches.map do |match|
48
+ rule = match[:rule]
49
+ value = match[:matched_value]
50
+ input = rule[:transform] ? rule[:transform].call(value) : value
51
+
52
+ { target_extension: rule[:target_extension],
53
+ target_method: rule[:target_method],
54
+ input: input }
55
+ end
56
+
57
+ { success: true, dispatches: dispatches, count: dispatches.size }
58
+ end
59
+
60
+ def composition_stats(**)
61
+ { success: true, **Helpers::CompositionMap.stats }
62
+ end
63
+
64
+ def suggest_compositions(extensions:, **)
65
+ exts = Array(extensions)
66
+
67
+ return suggest_with_llm(exts) if defined?(Legion::LLM) && Legion::LLM.respond_to?(:started?) && Legion::LLM.started?
68
+
69
+ suggestions = heuristic_suggestions(exts)
70
+ { success: true, suggestions: suggestions, count: suggestions.size }
71
+ end
72
+
73
+ def list_compositions(**)
74
+ rules = Helpers::CompositionMap.all_rules
75
+ { success: true, rules: rules, count: rules.size }
76
+ end
77
+
78
+ private
79
+
80
+ def heuristic_suggestions(extensions)
81
+ ext_by_category = {}
82
+ extensions.each do |ext|
83
+ cat = (ext[:category] || :cognition).to_sym
84
+ (ext_by_category[cat] ||= []) << ext
85
+ end
86
+
87
+ suggestions = []
88
+ CATEGORY_FLOW.each do |src_cat, tgt_cat|
89
+ src_exts = ext_by_category[src_cat] || []
90
+ tgt_exts = ext_by_category[tgt_cat] || []
91
+
92
+ src_exts.each do |src|
93
+ tgt_exts.each do |tgt|
94
+ suggestions << {
95
+ source_extension: src[:name] || src[:extension_name],
96
+ output_key: :result,
97
+ target_extension: tgt[:name] || tgt[:extension_name],
98
+ target_method: :process,
99
+ rationale: "#{src_cat} -> #{tgt_cat} flow"
100
+ }
101
+ end
102
+ end
103
+ end
104
+
105
+ suggestions
106
+ end
107
+
108
+ def suggest_with_llm(extensions)
109
+ suggestions = heuristic_suggestions(extensions)
110
+ { success: true, suggestions: suggestions, count: suggestions.size }
111
+ rescue StandardError
112
+ { success: true, suggestions: [], count: 0 }
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Runners
7
+ module DreamIdeation
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ extend self
12
+
13
+ DREAM_NOVELTY_BONUS = 0.15
14
+
15
+ # Agenda item weight by how underrepresented the category is
16
+ MAX_AGENDA_WEIGHT = 1.0
17
+ MIN_AGENDA_WEIGHT = 0.1
18
+
19
+ def generate_dream_proposals(existing_extensions: nil, max_proposals: 2, **)
20
+ gap_result = Runners::Proposer.analyze_gaps(existing_extensions: existing_extensions)
21
+ return { success: false, error: :gap_analysis_failed } unless gap_result[:success]
22
+
23
+ recommendations = gap_result[:recommendations] || []
24
+ proposals = []
25
+
26
+ recommendations.first(max_proposals).each do |rec|
27
+ name = rec.is_a?(Hash) ? rec[:name] : rec.to_s
28
+ result = Runners::Proposer.propose_concept(
29
+ name: "lex-dream-#{name.to_s.downcase.gsub(/[^a-z0-9]/, '-')}",
30
+ description: "Dream-originated proposal for #{name} cognitive capability",
31
+ enrich: false
32
+ )
33
+ next unless result[:success]
34
+
35
+ proposal_id = result[:proposal][:id]
36
+ proposal = Runners::Proposer.get_proposal_object(proposal_id)
37
+ proposal&.instance_variable_set(:@origin, :dream)
38
+
39
+ proposals << result[:proposal]
40
+ end
41
+
42
+ { success: true, proposals: proposals, count: proposals.size,
43
+ gaps_analyzed: recommendations.size }
44
+ end
45
+
46
+ def dream_agenda_items(existing_extensions: nil, **)
47
+ gap_result = Runners::Proposer.analyze_gaps(existing_extensions: existing_extensions)
48
+ return { success: false, error: :gap_analysis_failed } unless gap_result[:success]
49
+
50
+ target = Helpers::Constants::TARGET_DISTRIBUTION
51
+ models = gap_result[:models] || []
52
+
53
+ coverage_by_cat = build_coverage_by_category(models)
54
+
55
+ items = target.filter_map do |category, target_pct|
56
+ actual_pct = coverage_by_cat[category] || 0.0
57
+ gap = (target_pct - actual_pct).clamp(0.0, 1.0)
58
+ next if gap <= 0.0
59
+
60
+ weight = ((gap / target_pct) * MAX_AGENDA_WEIGHT).clamp(MIN_AGENDA_WEIGHT, MAX_AGENDA_WEIGHT).round(3)
61
+
62
+ { type: :architectural_gap,
63
+ content: { gap_name: category, model: :target_distribution, coverage: actual_pct },
64
+ weight: weight }
65
+ end
66
+
67
+ { success: true, items: items, count: items.size }
68
+ end
69
+
70
+ def enrich_from_dream_context(proposal_id:, dream_context: {}, **)
71
+ proposal = Runners::Proposer.get_proposal_object(proposal_id)
72
+ return { success: false, error: :not_found } unless proposal
73
+
74
+ if dream_context && !dream_context.empty?
75
+ existing = proposal.rationale.to_s
76
+ additions = dream_context.map { |k, v| "#{k}: #{v}" }.join('; ')
77
+ new_rationale = existing.empty? ? additions : "#{existing}. Dream context: #{additions}"
78
+ proposal.instance_variable_set(:@rationale, new_rationale)
79
+ { success: true, proposal_id: proposal_id, enriched: true }
80
+ else
81
+ { success: true, proposal_id: proposal_id, enriched: false }
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def build_coverage_by_category(models)
88
+ coverage = {}
89
+ models.each do |model|
90
+ cat = infer_category_from_model(model[:model])
91
+ next unless cat
92
+
93
+ existing = coverage[cat] || 1.0
94
+ coverage[cat] = [existing, model_coverage_fraction(model)].min
95
+ end
96
+ coverage
97
+ end
98
+
99
+ def model_coverage_fraction(model)
100
+ total = model[:total_required] || 1
101
+ missing = (model[:missing] || []).size
102
+ covered = total - missing
103
+ total.positive? ? (covered.to_f / total).round(3) : 0.0
104
+ end
105
+
106
+ def infer_category_from_model(model_name)
107
+ mapping = {
108
+ global_workspace: :cognition,
109
+ free_energy: :introspection,
110
+ dual_process: :cognition,
111
+ somatic_marker: :motivation,
112
+ working_memory: :memory
113
+ }
114
+ mapping[model_name&.to_sym]
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module MindGrowth
6
+ module Runners
7
+ module Monitor
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ extend self
12
+
13
+ HEALTH_LEVELS = {
14
+ excellent: 0.8,
15
+ good: 0.6,
16
+ fair: 0.4,
17
+ degraded: 0.2,
18
+ critical: 0.0
19
+ }.freeze
20
+
21
+ def health_check(extension:, **)
22
+ name = extension[:name] || extension[:extension_name]
23
+ fitness = Helpers::FitnessEvaluator.fitness(extension)
24
+ level = classify_health(fitness)
25
+ alert = %i[degraded critical].include?(level)
26
+
27
+ { success: true, extension_name: name, fitness: fitness,
28
+ health_level: level, alert: alert }
29
+ end
30
+
31
+ def usage_stats(extensions:, **)
32
+ stats = Array(extensions).map do |ext|
33
+ { extension_name: ext[:name] || ext[:extension_name],
34
+ invocation_count: ext[:invocation_count] || 0,
35
+ error_rate: ext[:error_rate] || 0.0,
36
+ avg_latency_ms: ext[:avg_latency_ms] || 0 }
37
+ end
38
+
39
+ { success: true, stats: stats, count: stats.size }
40
+ end
41
+
42
+ def impact_score(extension:, extensions: nil, **)
43
+ name = extension[:name] || extension[:extension_name]
44
+ impact = extension[:impact_score] || 0.5
45
+
46
+ percentile = if extensions && !Array(extensions).empty?
47
+ all_impacts = Array(extensions).map { |e| e[:impact_score] || 0.5 }.sort
48
+ rank = all_impacts.count { |i| i <= impact }
49
+ (rank.to_f / all_impacts.size * 100).round(1)
50
+ else
51
+ 50.0
52
+ end
53
+
54
+ { success: true, extension_name: name, impact: impact, rank_percentile: percentile }
55
+ end
56
+
57
+ def decay_check(extensions:, **)
58
+ threshold = Helpers::Constants::DECAY_INVOCATION_THRESHOLD
59
+ decayed = Array(extensions).select do |ext|
60
+ count = ext[:invocation_count] || 0
61
+ fitness = Helpers::FitnessEvaluator.fitness(ext)
62
+ count < threshold || fitness < Helpers::Constants::PRUNE_THRESHOLD
63
+ end
64
+
65
+ { success: true, decayed: decayed, count: decayed.size }
66
+ end
67
+
68
+ def auto_prune(extensions:, **)
69
+ pruned = Helpers::FitnessEvaluator.prune_candidates(Array(extensions))
70
+ { success: true, pruned: pruned, count: pruned.size }
71
+ end
72
+
73
+ def health_summary(extensions:, **)
74
+ exts = Array(extensions)
75
+
76
+ by_health_level = HEALTH_LEVELS.keys.to_h { |level| [level, 0] }
77
+ alerts = []
78
+ prune_candidates = Helpers::FitnessEvaluator.prune_candidates(exts)
79
+
80
+ exts.each do |ext|
81
+ fitness = Helpers::FitnessEvaluator.fitness(ext)
82
+ level = classify_health(fitness)
83
+ by_health_level[level] += 1
84
+ alerts << ext if %i[degraded critical].include?(level)
85
+ end
86
+
87
+ { success: true, total: exts.size, by_health_level: by_health_level,
88
+ alerts: alerts, prune_candidates: prune_candidates }
89
+ end
90
+
91
+ private
92
+
93
+ def classify_health(fitness)
94
+ HEALTH_LEVELS.each do |level, threshold|
95
+ return level if fitness >= threshold
96
+ end
97
+ :critical
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module MindGrowth
6
- VERSION = '0.1.7'
6
+ VERSION = '0.1.8'
7
7
  end
8
8
  end
9
9
  end
@@ -20,6 +20,10 @@ require 'legion/extensions/mind_growth/runners/integration_tester'
20
20
  require 'legion/extensions/mind_growth/runners/retrospective'
21
21
  require 'legion/extensions/mind_growth/runners/governance'
22
22
  require 'legion/extensions/mind_growth/runners/risk_assessor'
23
+ require 'legion/extensions/mind_growth/helpers/composition_map'
24
+ require 'legion/extensions/mind_growth/runners/monitor'
25
+ require 'legion/extensions/mind_growth/runners/composer'
26
+ require 'legion/extensions/mind_growth/runners/dream_ideation'
23
27
  require 'legion/extensions/mind_growth/client'
24
28
 
25
29
  module Legion