lex-mind-growth 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/Gemfile +19 -0
- data/LICENSE +21 -0
- data/lex-mind-growth.gemspec +29 -0
- data/lib/legion/extensions/mind_growth/actors/growth_cycle.rb +55 -0
- data/lib/legion/extensions/mind_growth/client.rb +36 -0
- data/lib/legion/extensions/mind_growth/helpers/build_pipeline.rb +63 -0
- data/lib/legion/extensions/mind_growth/helpers/cognitive_models.rb +60 -0
- data/lib/legion/extensions/mind_growth/helpers/concept_proposal.rb +69 -0
- data/lib/legion/extensions/mind_growth/helpers/constants.rb +55 -0
- data/lib/legion/extensions/mind_growth/helpers/fitness_evaluator.rb +58 -0
- data/lib/legion/extensions/mind_growth/helpers/proposal_store.rb +71 -0
- data/lib/legion/extensions/mind_growth/runners/analyzer.rb +52 -0
- data/lib/legion/extensions/mind_growth/runners/builder.rb +254 -0
- data/lib/legion/extensions/mind_growth/runners/orchestrator.rb +178 -0
- data/lib/legion/extensions/mind_growth/runners/proposer.rb +269 -0
- data/lib/legion/extensions/mind_growth/runners/validator.rb +57 -0
- data/lib/legion/extensions/mind_growth/version.rb +9 -0
- data/lib/legion/extensions/mind_growth.rb +25 -0
- data/spec/legion/extensions/mind_growth/actors/growth_cycle_spec.rb +81 -0
- data/spec/legion/extensions/mind_growth/client_spec.rb +111 -0
- data/spec/legion/extensions/mind_growth/helpers/build_pipeline_spec.rb +190 -0
- data/spec/legion/extensions/mind_growth/helpers/cognitive_models_spec.rb +106 -0
- data/spec/legion/extensions/mind_growth/helpers/concept_proposal_spec.rb +209 -0
- data/spec/legion/extensions/mind_growth/helpers/fitness_evaluator_spec.rb +123 -0
- data/spec/legion/extensions/mind_growth/helpers/proposal_store_spec.rb +203 -0
- data/spec/legion/extensions/mind_growth/runners/analyzer_spec.rb +108 -0
- data/spec/legion/extensions/mind_growth/runners/builder_spec.rb +347 -0
- data/spec/legion/extensions/mind_growth/runners/orchestrator_spec.rb +185 -0
- data/spec/legion/extensions/mind_growth/runners/proposer_spec.rb +493 -0
- data/spec/legion/extensions/mind_growth/runners/validator_spec.rb +120 -0
- data/spec/spec_helper.rb +22 -0
- metadata +91 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 811a3676ca7c7fd4168c30606006adeca6020be69398bb94128130ab6da89c34
|
|
4
|
+
data.tar.gz: c40ec71e46d799b65100fa378c8840ca55579147159bd39eee9a8b6858b6ab97
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 997b9b014e37450333f475b4dd2ab8c70cc4bf7cd355d5cbc73a9873aa73dae4b7394b925f61cd97b9a8102ca038e0b6902bf32bee1a86b119b93f745380d8a7
|
|
7
|
+
data.tar.gz: 4765d190fee92ce1c98e20a40955fa85521adc98dfe2b4013817fd4a01b7b4e18348323e665cafc605967196c367180f157cf28894ca70b089a955fb2cb470ef
|
data/Gemfile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
gemspec
|
|
5
|
+
|
|
6
|
+
group :test do
|
|
7
|
+
gem 'rake'
|
|
8
|
+
gem 'rspec', '~> 3.13'
|
|
9
|
+
gem 'rspec_junit_formatter'
|
|
10
|
+
gem 'rubocop', '~> 1.75'
|
|
11
|
+
gem 'rubocop-rspec'
|
|
12
|
+
gem 'simplecov'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
gem 'legion-llm', path: '../../legion-llm', require: false
|
|
16
|
+
gem 'lex-codegen', path: '../../extensions-core/lex-codegen', require: false
|
|
17
|
+
gem 'lex-exec', path: '../../extensions-core/lex-exec', require: false
|
|
18
|
+
|
|
19
|
+
gem 'legion-gaia', path: '../../legion-gaia'
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LegionIO
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/mind_growth/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-mind-growth'
|
|
7
|
+
spec.version = Legion::Extensions::MindGrowth::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Mind Growth'
|
|
12
|
+
spec.description = 'Autonomous cognitive architecture expansion for LegionIO'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-mind-growth'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
|
+
|
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-mind-growth'
|
|
19
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-mind-growth'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-mind-growth'
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-mind-growth/issues'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
23
|
+
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
Dir.glob('{lib,spec}/**/*') + %w[lex-mind-growth.gemspec Gemfile LICENSE]
|
|
26
|
+
end
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
spec.add_development_dependency 'legion-gaia'
|
|
29
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/actors/every'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module MindGrowth
|
|
8
|
+
module Actor
|
|
9
|
+
class GrowthCycle < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class
|
|
11
|
+
Legion::Extensions::MindGrowth::Runners::Orchestrator
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def runner_function
|
|
15
|
+
'run_growth_cycle'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def time
|
|
19
|
+
3600
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def enabled?
|
|
23
|
+
codegen_loaded? || exec_loaded?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run_now?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def use_runner?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def check_subtask?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def generate_task?
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def codegen_loaded?
|
|
45
|
+
defined?(Legion::Extensions::Codegen)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def exec_loaded?
|
|
49
|
+
defined?(Legion::Extensions::Exec)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
class Client
|
|
7
|
+
def initialize; end
|
|
8
|
+
|
|
9
|
+
# Proposer delegation
|
|
10
|
+
def analyze_gaps(**) = Runners::Proposer.analyze_gaps(**)
|
|
11
|
+
def propose_concept(**) = Runners::Proposer.propose_concept(**)
|
|
12
|
+
def evaluate_proposal(**) = Runners::Proposer.evaluate_proposal(**)
|
|
13
|
+
def list_proposals(**) = Runners::Proposer.list_proposals(**)
|
|
14
|
+
def proposal_stats(**) = Runners::Proposer.proposal_stats(**)
|
|
15
|
+
|
|
16
|
+
# Analyzer delegation
|
|
17
|
+
def cognitive_profile(**) = Runners::Analyzer.cognitive_profile(**)
|
|
18
|
+
def identify_weak_links(**) = Runners::Analyzer.identify_weak_links(**)
|
|
19
|
+
def recommend_priorities(**) = Runners::Analyzer.recommend_priorities(**)
|
|
20
|
+
|
|
21
|
+
# Builder delegation
|
|
22
|
+
def build_extension(**) = Runners::Builder.build_extension(**)
|
|
23
|
+
def build_status(**) = Runners::Builder.build_status(**)
|
|
24
|
+
|
|
25
|
+
# Validator delegation
|
|
26
|
+
def validate_proposal(**) = Runners::Validator.validate_proposal(**)
|
|
27
|
+
def validate_scores(**) = Runners::Validator.validate_scores(**)
|
|
28
|
+
def validate_fitness(**) = Runners::Validator.validate_fitness(**)
|
|
29
|
+
|
|
30
|
+
# Orchestrator delegation
|
|
31
|
+
def run_growth_cycle(**) = Runners::Orchestrator.run_growth_cycle(**)
|
|
32
|
+
def growth_status(**) = Runners::Orchestrator.growth_status(**)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Helpers
|
|
7
|
+
class BuildPipeline
|
|
8
|
+
STAGES = %i[scaffold implement test validate register complete failed].freeze
|
|
9
|
+
|
|
10
|
+
attr_reader :proposal, :stage, :errors, :artifacts, :started_at, :completed_at
|
|
11
|
+
|
|
12
|
+
def initialize(proposal)
|
|
13
|
+
@proposal = proposal
|
|
14
|
+
@stage = :scaffold
|
|
15
|
+
@errors = []
|
|
16
|
+
@artifacts = {}
|
|
17
|
+
@started_at = Time.now.utc
|
|
18
|
+
@completed_at = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def advance!(result)
|
|
22
|
+
return if complete? || failed?
|
|
23
|
+
|
|
24
|
+
if result[:success]
|
|
25
|
+
@artifacts[@stage] = result
|
|
26
|
+
next_idx = STAGES.index(@stage) + 1
|
|
27
|
+
@stage = STAGES[next_idx] || :complete
|
|
28
|
+
@completed_at = Time.now.utc if @stage == :complete
|
|
29
|
+
else
|
|
30
|
+
@errors << { stage: @stage, error: result[:error], at: Time.now.utc }
|
|
31
|
+
@stage = :failed if @errors.size >= Helpers::Constants::MAX_FIX_ATTEMPTS
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def complete?
|
|
36
|
+
@stage == :complete
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def failed?
|
|
40
|
+
@stage == :failed
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_h
|
|
44
|
+
{
|
|
45
|
+
proposal_id: @proposal.id,
|
|
46
|
+
stage: @stage,
|
|
47
|
+
errors: @errors,
|
|
48
|
+
artifacts: @artifacts,
|
|
49
|
+
started_at: @started_at,
|
|
50
|
+
completed_at: @completed_at,
|
|
51
|
+
duration_ms: duration_ms
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def duration_ms
|
|
56
|
+
end_time = @completed_at || Time.now.utc
|
|
57
|
+
((end_time - @started_at) * 1000).round
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Helpers
|
|
7
|
+
module CognitiveModels
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
MODELS = {
|
|
11
|
+
global_workspace: {
|
|
12
|
+
name: 'Global Workspace Theory (Baars)',
|
|
13
|
+
required: %i[attention global_workspace broadcasting working_memory consciousness],
|
|
14
|
+
description: 'Conscious access via global broadcasting to specialized processors'
|
|
15
|
+
},
|
|
16
|
+
free_energy: {
|
|
17
|
+
name: 'Free Energy Principle (Friston)',
|
|
18
|
+
required: %i[prediction free_energy predictive_coding belief_revision active_inference error_monitoring],
|
|
19
|
+
description: 'Minimize surprise through predictive models and active inference'
|
|
20
|
+
},
|
|
21
|
+
dual_process: {
|
|
22
|
+
name: 'Dual Process Theory (Kahneman)',
|
|
23
|
+
required: %i[intuition dual_process inhibition executive_function cognitive_control],
|
|
24
|
+
description: 'System 1 (fast/automatic) vs System 2 (slow/deliberate) processing'
|
|
25
|
+
},
|
|
26
|
+
somatic_marker: {
|
|
27
|
+
name: 'Somatic Marker Hypothesis (Damasio)',
|
|
28
|
+
required: %i[emotion somatic_marker interoception appraisal embodied_simulation],
|
|
29
|
+
description: 'Emotion-cognition integration for decision making'
|
|
30
|
+
},
|
|
31
|
+
working_memory: {
|
|
32
|
+
name: 'Working Memory Model (Baddeley)',
|
|
33
|
+
required: %i[working_memory episodic_buffer attention executive_function cognitive_load],
|
|
34
|
+
description: 'Multi-component model with central executive and slave systems'
|
|
35
|
+
}
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
def gap_analysis(existing_extensions)
|
|
39
|
+
existing_names = existing_extensions.map { |e| e.to_s.downcase.to_sym }
|
|
40
|
+
MODELS.map do |key, model|
|
|
41
|
+
missing = model[:required] - existing_names
|
|
42
|
+
coverage = 1.0 - (missing.size.to_f / model[:required].size)
|
|
43
|
+
{
|
|
44
|
+
model: key,
|
|
45
|
+
name: model[:name],
|
|
46
|
+
coverage: coverage.round(2),
|
|
47
|
+
missing: missing,
|
|
48
|
+
total_required: model[:required].size
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def recommend_from_gaps(gap_results)
|
|
54
|
+
gap_results.flat_map { |g| g[:missing] }.tally.sort_by { |_, count| -count }.map(&:first)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Helpers
|
|
7
|
+
class ConceptProposal
|
|
8
|
+
FIELDS = %i[
|
|
9
|
+
id name module_name category description metaphor helpers runner_methods
|
|
10
|
+
rationale scores status origin created_at evaluated_at built_at
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
13
|
+
attr_reader(*FIELDS)
|
|
14
|
+
|
|
15
|
+
def initialize(name:, module_name:, category:, description:, metaphor: nil, helpers: [],
|
|
16
|
+
runner_methods: [], rationale: nil, origin: :proposer)
|
|
17
|
+
@id = SecureRandom.uuid
|
|
18
|
+
@name = name
|
|
19
|
+
@module_name = module_name
|
|
20
|
+
@category = category.to_sym
|
|
21
|
+
@description = description
|
|
22
|
+
@metaphor = metaphor
|
|
23
|
+
@helpers = helpers # array of { name:, methods: [] }
|
|
24
|
+
@runner_methods = runner_methods # array of { name:, params: [], returns: '' }
|
|
25
|
+
@rationale = rationale
|
|
26
|
+
@scores = {} # { novelty: 0.0..1.0, fit: 0.0..1.0, ... }
|
|
27
|
+
@status = :proposed
|
|
28
|
+
@origin = origin
|
|
29
|
+
@created_at = Time.now.utc
|
|
30
|
+
@evaluated_at = nil
|
|
31
|
+
@built_at = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def evaluate!(scores)
|
|
35
|
+
@scores = scores
|
|
36
|
+
@evaluated_at = Time.now.utc
|
|
37
|
+
@status = passing_evaluation? ? :approved : :rejected
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def passing_evaluation?
|
|
41
|
+
return false if @scores.empty?
|
|
42
|
+
|
|
43
|
+
Helpers::Constants::EVALUATION_DIMENSIONS.all? { |dim| (@scores[dim] || 0) >= Helpers::Constants::MIN_DIMENSION_SCORE }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def auto_approvable?
|
|
47
|
+
return false if @scores.empty?
|
|
48
|
+
|
|
49
|
+
Helpers::Constants::EVALUATION_DIMENSIONS.all? { |dim| (@scores[dim] || 0) >= Helpers::Constants::AUTO_APPROVE_THRESHOLD }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def transition!(new_status)
|
|
53
|
+
new_status = new_status.to_sym
|
|
54
|
+
unless Helpers::Constants::PROPOSAL_STATUSES.include?(new_status)
|
|
55
|
+
raise ArgumentError, "invalid status: #{new_status} (valid: #{Helpers::Constants::PROPOSAL_STATUSES.join(', ')})"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
@status = new_status
|
|
59
|
+
@built_at = Time.now.utc if new_status == :passing
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_h
|
|
63
|
+
FIELDS.to_h { |f| [f, send(f)] }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
# Proposal status lifecycle
|
|
9
|
+
PROPOSAL_STATUSES = %i[
|
|
10
|
+
proposed evaluating approved rejected building testing passing wired active degraded pruned build_failed
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
13
|
+
# Cognitive categories and target distribution
|
|
14
|
+
CATEGORIES = %i[cognition perception introspection safety communication memory motivation coordination].freeze
|
|
15
|
+
|
|
16
|
+
TARGET_DISTRIBUTION = {
|
|
17
|
+
cognition: 0.30,
|
|
18
|
+
perception: 0.15,
|
|
19
|
+
introspection: 0.12,
|
|
20
|
+
safety: 0.12,
|
|
21
|
+
communication: 0.10,
|
|
22
|
+
memory: 0.10,
|
|
23
|
+
motivation: 0.06,
|
|
24
|
+
coordination: 0.05
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
# Evaluation dimensions
|
|
28
|
+
EVALUATION_DIMENSIONS = %i[novelty fit cognitive_value implementability composability].freeze
|
|
29
|
+
MIN_DIMENSION_SCORE = 0.6
|
|
30
|
+
AUTO_APPROVE_THRESHOLD = 0.9
|
|
31
|
+
REDUNDANCY_THRESHOLD = 0.8
|
|
32
|
+
|
|
33
|
+
# Build pipeline
|
|
34
|
+
MAX_FIX_ATTEMPTS = 3
|
|
35
|
+
BUILD_TIMEOUT_MS = 600_000 # 10 minutes
|
|
36
|
+
|
|
37
|
+
# Fitness function weights
|
|
38
|
+
FITNESS_WEIGHTS = {
|
|
39
|
+
invocation_rate: 0.25,
|
|
40
|
+
impact_score: 0.30,
|
|
41
|
+
health: 0.25,
|
|
42
|
+
error_penalty: -0.15,
|
|
43
|
+
latency_penalty: -0.05
|
|
44
|
+
}.freeze
|
|
45
|
+
|
|
46
|
+
PRUNE_THRESHOLD = 0.2
|
|
47
|
+
IMPROVEMENT_THRESHOLD = 0.4
|
|
48
|
+
|
|
49
|
+
# Reference cognitive models
|
|
50
|
+
COGNITIVE_MODELS = %i[global_workspace free_energy dual_process somatic_marker working_memory].freeze
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Helpers
|
|
7
|
+
module FitnessEvaluator
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def fitness(extension)
|
|
11
|
+
w = Helpers::Constants::FITNESS_WEIGHTS
|
|
12
|
+
invocation = normalize_invocations(extension[:invocation_count] || 0)
|
|
13
|
+
impact = extension[:impact_score] || 0.5
|
|
14
|
+
health = extension[:health_score] || 1.0
|
|
15
|
+
error_rate = extension[:error_rate] || 0.0
|
|
16
|
+
latency = normalize_latency(extension[:avg_latency_ms] || 0)
|
|
17
|
+
|
|
18
|
+
score = (w[:invocation_rate] * invocation) +
|
|
19
|
+
(w[:impact_score] * impact) +
|
|
20
|
+
(w[:health] * health) +
|
|
21
|
+
(w[:error_penalty] * error_rate) +
|
|
22
|
+
(w[:latency_penalty] * latency)
|
|
23
|
+
score.clamp(0.0, 1.0).round(3)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def rank(extensions)
|
|
27
|
+
extensions.map { |e| e.merge(fitness: fitness(e)) }.sort_by { |e| -e[:fitness] }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def prune_candidates(extensions)
|
|
31
|
+
extensions.select { |e| fitness(e) < Helpers::Constants::PRUNE_THRESHOLD }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def improvement_candidates(extensions)
|
|
35
|
+
extensions.select do |e|
|
|
36
|
+
f = fitness(e)
|
|
37
|
+
f >= Helpers::Constants::PRUNE_THRESHOLD && f < Helpers::Constants::IMPROVEMENT_THRESHOLD
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def normalize_invocations(count)
|
|
44
|
+
# Log scale: 0 invocations = 0.0, 1000+ = 1.0
|
|
45
|
+
return 0.0 if count.zero?
|
|
46
|
+
|
|
47
|
+
(Math.log10(count + 1) / 3.0).clamp(0.0, 1.0)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def normalize_latency(ms_val)
|
|
51
|
+
# Higher latency = higher penalty (0-1 scale, 5000ms = 1.0)
|
|
52
|
+
(ms_val / 5000.0).clamp(0.0, 1.0)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Helpers
|
|
7
|
+
class ProposalStore
|
|
8
|
+
MAX_PROPOSALS = 500
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@proposals = {}
|
|
12
|
+
@mutex = Mutex.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def store(proposal)
|
|
16
|
+
@mutex.synchronize do
|
|
17
|
+
evict_oldest if @proposals.size >= MAX_PROPOSALS
|
|
18
|
+
@proposals[proposal.id] = proposal
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get(id)
|
|
23
|
+
@mutex.synchronize { @proposals[id] }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def all
|
|
27
|
+
@mutex.synchronize { @proposals.values.dup }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def by_status(status)
|
|
31
|
+
@mutex.synchronize { @proposals.values.select { |p| p.status == status.to_sym } }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def by_category(category)
|
|
35
|
+
@mutex.synchronize { @proposals.values.select { |p| p.category == category.to_sym } }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def approved
|
|
39
|
+
by_status(:approved)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def build_queue
|
|
43
|
+
by_status(:approved).sort_by { |p| -(p.scores.values.sum / p.scores.size.to_f) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def recent(limit: 20)
|
|
47
|
+
@mutex.synchronize { @proposals.values.sort_by { |p| -p.created_at.to_f }.first(limit) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def stats
|
|
51
|
+
@mutex.synchronize do
|
|
52
|
+
statuses = @proposals.values.group_by(&:status).transform_values(&:count)
|
|
53
|
+
{ total: @proposals.size, by_status: statuses }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def clear
|
|
58
|
+
@mutex.synchronize { @proposals.clear }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def evict_oldest
|
|
64
|
+
oldest = @proposals.values.min_by { |p| p.created_at.to_f }
|
|
65
|
+
@proposals.delete(oldest.id) if oldest
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module MindGrowth
|
|
6
|
+
module Runners
|
|
7
|
+
module Analyzer
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def cognitive_profile(existing_extensions: nil, **)
|
|
11
|
+
extensions = existing_extensions || current_extensions
|
|
12
|
+
gap_data = Helpers::CognitiveModels.gap_analysis(extensions)
|
|
13
|
+
{
|
|
14
|
+
success: true,
|
|
15
|
+
total_extensions: extensions.size,
|
|
16
|
+
model_coverage: gap_data,
|
|
17
|
+
overall_coverage: (gap_data.sum { |g| g[:coverage] } / gap_data.size.to_f).round(2)
|
|
18
|
+
}
|
|
19
|
+
rescue ArgumentError => e
|
|
20
|
+
{ success: false, error: e.message }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def identify_weak_links(extensions: [], **)
|
|
24
|
+
weak = extensions.select { |e| Helpers::FitnessEvaluator.fitness(e) < Helpers::Constants::IMPROVEMENT_THRESHOLD }
|
|
25
|
+
{ success: true, weak_links: Helpers::FitnessEvaluator.rank(weak), count: weak.size }
|
|
26
|
+
rescue ArgumentError => e
|
|
27
|
+
{ success: false, error: e.message }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def recommend_priorities(existing_extensions: nil, **)
|
|
31
|
+
gaps = Helpers::CognitiveModels.recommend_from_gaps(
|
|
32
|
+
Helpers::CognitiveModels.gap_analysis(existing_extensions || current_extensions)
|
|
33
|
+
)
|
|
34
|
+
{ success: true, priorities: gaps.first(10), rationale: 'Based on cognitive model gap analysis' }
|
|
35
|
+
rescue ArgumentError => e
|
|
36
|
+
{ success: false, error: e.message }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def current_extensions
|
|
42
|
+
if defined?(Legion::Extensions::Metacognition::Helpers::Constants::SUBSYSTEMS)
|
|
43
|
+
Legion::Extensions::Metacognition::Helpers::Constants::SUBSYSTEMS
|
|
44
|
+
else
|
|
45
|
+
[]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|