lex-self-model 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5c0246b5a6afc20e2381d6517d83d9e7786c3da4de8f35126fb027bdabacd0aa
4
+ data.tar.gz: 8975d4507567a790d341bd24317f4206207aaebe24ef630dfafbcd82634b24ce
5
+ SHA512:
6
+ metadata.gz: 145cc57f3dd898d6ea653162aad93be523c0e7b8f3c5c15c604ce23bdc2b343f0936a6052d3cff8ea3824e03672439f5dcfe0c1f3176e06a241f8247c6bc4fc0
7
+ data.tar.gz: 0b6d03bc047353a34198a1459c40ab873428d073c549a7a5ac2003104dd588c0bffc91f68846c446cd2729c939b89629270bd901fdbc0c25a2d0c7277efe9a09
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module SelfModel
6
+ class Client
7
+ include Runners::SelfModel
8
+
9
+ def initialize(model: nil)
10
+ @model = model || Helpers::SelfModel.new
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module SelfModel
6
+ module Helpers
7
+ class Capability
8
+ include Constants
9
+
10
+ attr_reader :id, :name, :domain, :competence, :attempts, :successes, :state, :calibration_error
11
+
12
+ def initialize(id:, name:, domain: :general, competence: DEFAULT_COMPETENCE)
13
+ @id = id
14
+ @name = name
15
+ @domain = domain
16
+ @competence = competence.to_f.clamp(COMPETENCE_FLOOR, COMPETENCE_CEILING)
17
+ @attempts = 0
18
+ @successes = 0
19
+ @calibration_error = 0.0
20
+ @state = compute_state
21
+ end
22
+
23
+ def record_attempt(predicted_success:, actual_success:)
24
+ @attempts += 1
25
+ @successes += 1 if actual_success
26
+
27
+ prediction = predicted_success ? 1.0 : 0.0
28
+ outcome = actual_success ? 1.0 : 0.0
29
+
30
+ error = prediction - outcome
31
+ @calibration_error += CALIBRATION_ALPHA * (error - @calibration_error)
32
+
33
+ @competence += CALIBRATION_ALPHA * (outcome - @competence)
34
+ @competence = @competence.clamp(COMPETENCE_FLOOR, COMPETENCE_CEILING)
35
+ @state = compute_state
36
+ end
37
+
38
+ def competence_label
39
+ CONFIDENCE_LABELS.each { |range, lbl| return lbl if range.cover?(@competence) }
40
+ :very_low
41
+ end
42
+
43
+ def calibrated?
44
+ @calibration_error.abs < 0.15
45
+ end
46
+
47
+ def overconfident?
48
+ @calibration_error > OVERCONFIDENCE_THRESHOLD
49
+ end
50
+
51
+ def underconfident?
52
+ @calibration_error < UNDERCONFIDENCE_THRESHOLD
53
+ end
54
+
55
+ def to_h
56
+ {
57
+ id: @id,
58
+ name: @name,
59
+ domain: @domain,
60
+ competence: @competence.round(4),
61
+ competence_label: competence_label,
62
+ state: @state,
63
+ attempts: @attempts,
64
+ successes: @successes,
65
+ calibration_error: @calibration_error.round(4),
66
+ calibrated: calibrated?,
67
+ overconfident: overconfident?,
68
+ underconfident: underconfident?
69
+ }
70
+ end
71
+
72
+ private
73
+
74
+ def compute_state
75
+ if @competence < 0.2
76
+ :unknown
77
+ elsif @competence < 0.5
78
+ :developing
79
+ elsif @competence < 0.8
80
+ :competent
81
+ else
82
+ :expert
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module SelfModel
6
+ module Helpers
7
+ module Constants
8
+ MAX_CAPABILITIES = 100
9
+ MAX_KNOWLEDGE_DOMAINS = 50
10
+ MAX_HISTORY = 200
11
+
12
+ DEFAULT_COMPETENCE = 0.5
13
+ COMPETENCE_FLOOR = 0.05
14
+ COMPETENCE_CEILING = 0.99
15
+ CALIBRATION_ALPHA = 0.1
16
+
17
+ OVERCONFIDENCE_THRESHOLD = 0.3
18
+ UNDERCONFIDENCE_THRESHOLD = -0.3
19
+
20
+ CAPABILITY_STATES = %i[unknown developing competent expert].freeze
21
+ KNOWLEDGE_STATES = %i[ignorant aware familiar expert].freeze
22
+
23
+ CONFIDENCE_LABELS = {
24
+ (0.9..) => :very_high,
25
+ (0.7...0.9) => :high,
26
+ (0.5...0.7) => :moderate,
27
+ (0.3...0.5) => :low,
28
+ (..0.3) => :very_low
29
+ }.freeze
30
+
31
+ CALIBRATION_LABELS = {
32
+ excellent: 0.05,
33
+ good: 0.10,
34
+ fair: 0.20,
35
+ poor: 0.35,
36
+ uncalibrated: Float::INFINITY
37
+ }.freeze
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module SelfModel
6
+ module Helpers
7
+ class KnowledgeDomain
8
+ include Constants
9
+
10
+ attr_reader :id, :name, :depth, :breadth, :confidence, :state, :last_accessed
11
+
12
+ def initialize(id:, name:, depth: 0.0, breadth: 0.0)
13
+ @id = id
14
+ @name = name
15
+ @depth = depth.to_f.clamp(0.0, 1.0)
16
+ @breadth = breadth.to_f.clamp(0.0, 1.0)
17
+ @last_accessed = nil
18
+ @state = compute_state
19
+ @confidence = average_score
20
+ end
21
+
22
+ def deepen(amount:)
23
+ @depth = (@depth + amount.to_f).clamp(0.0, 1.0)
24
+ @state = compute_state
25
+ @confidence = average_score
26
+ end
27
+
28
+ def broaden(amount:)
29
+ @breadth = (@breadth + amount.to_f).clamp(0.0, 1.0)
30
+ @state = compute_state
31
+ @confidence = average_score
32
+ end
33
+
34
+ def access!
35
+ @last_accessed = Time.now.utc
36
+ end
37
+
38
+ def knowledge_label
39
+ CONFIDENCE_LABELS.each { |range, lbl| return lbl if range.cover?(@confidence) }
40
+ :very_low
41
+ end
42
+
43
+ def to_h
44
+ {
45
+ id: @id,
46
+ name: @name,
47
+ depth: @depth.round(4),
48
+ breadth: @breadth.round(4),
49
+ confidence: @confidence.round(4),
50
+ state: @state,
51
+ knowledge_label: knowledge_label,
52
+ last_accessed: @last_accessed
53
+ }
54
+ end
55
+
56
+ private
57
+
58
+ def average_score
59
+ (@depth + @breadth) / 2.0
60
+ end
61
+
62
+ def compute_state
63
+ score = average_score
64
+ if score < 0.2
65
+ :ignorant
66
+ elsif score < 0.5
67
+ :aware
68
+ elsif score < 0.8
69
+ :familiar
70
+ else
71
+ :expert
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module SelfModel
6
+ module Helpers
7
+ class SelfModel
8
+ include Constants
9
+
10
+ attr_reader :capabilities, :knowledge_domains, :predictions, :history
11
+
12
+ def initialize
13
+ @capabilities = {}
14
+ @knowledge_domains = {}
15
+ @predictions = []
16
+ @history = []
17
+ @cap_counter = 0
18
+ @dom_counter = 0
19
+ end
20
+
21
+ def add_capability(name:, domain: :general, competence: DEFAULT_COMPETENCE)
22
+ return nil if @capabilities.size >= MAX_CAPABILITIES
23
+
24
+ @cap_counter += 1
25
+ cap_id = :"cap_#{@cap_counter}"
26
+ cap = Capability.new(id: cap_id, name: name, domain: domain, competence: competence)
27
+ @capabilities[cap_id] = cap
28
+ cap
29
+ end
30
+
31
+ def add_knowledge_domain(name:, depth: 0.0, breadth: 0.0)
32
+ return nil if @knowledge_domains.size >= MAX_KNOWLEDGE_DOMAINS
33
+
34
+ @dom_counter += 1
35
+ dom_id = :"dom_#{@dom_counter}"
36
+ dom = KnowledgeDomain.new(id: dom_id, name: name, depth: depth, breadth: breadth)
37
+ @knowledge_domains[dom_id] = dom
38
+ dom
39
+ end
40
+
41
+ def predict_success(capability_id:)
42
+ cap = @capabilities[capability_id]
43
+ return nil unless cap
44
+
45
+ prediction = { capability_id: capability_id, predicted_probability: cap.competence.round(4),
46
+ at: Time.now.utc }
47
+ @predictions << prediction
48
+ @predictions.shift while @predictions.size > MAX_HISTORY
49
+ prediction
50
+ end
51
+
52
+ def record_outcome(capability_id:, predicted:, actual:)
53
+ cap = @capabilities[capability_id]
54
+ return nil unless cap
55
+
56
+ cap.record_attempt(predicted_success: predicted, actual_success: actual)
57
+ event = { type: :outcome, capability_id: capability_id, predicted: predicted,
58
+ actual: actual, at: Time.now.utc }
59
+ @history << event
60
+ @history.shift while @history.size > MAX_HISTORY
61
+ event
62
+ end
63
+
64
+ def introspect
65
+ {
66
+ overall_confidence: overall_confidence.round(4),
67
+ strengths: strengths.map(&:to_h),
68
+ weaknesses: weaknesses.map(&:to_h),
69
+ blind_spots: blind_spots.map(&:to_h),
70
+ knowledge_gaps: knowledge_gaps.map(&:to_h),
71
+ calibration: calibration_report
72
+ }
73
+ end
74
+
75
+ def strengths
76
+ @capabilities.values.select { |c| c.competence > 0.7 }
77
+ end
78
+
79
+ def weaknesses
80
+ @capabilities.values.select { |c| c.competence < 0.3 }
81
+ end
82
+
83
+ def blind_spots
84
+ @capabilities.values.select(&:overconfident?)
85
+ end
86
+
87
+ def calibration_report
88
+ caps = @capabilities.values
89
+ return { label: :uncalibrated, mean_error: 0.0, calibrated_count: 0, total: 0 } if caps.empty?
90
+
91
+ errors = caps.map { |c| c.calibration_error.abs }
92
+ mean_error = errors.sum / errors.size.to_f
93
+ calibrated_count = caps.count(&:calibrated?)
94
+
95
+ label = CALIBRATION_LABELS.each do |lbl, threshold|
96
+ break lbl if mean_error <= threshold
97
+ end
98
+ label = :uncalibrated unless label.is_a?(Symbol)
99
+
100
+ { label: label, mean_error: mean_error.round(4), calibrated_count: calibrated_count,
101
+ total: caps.size }
102
+ end
103
+
104
+ def knowledge_gaps
105
+ @knowledge_domains.values.select { |d| d.depth < 0.3 }
106
+ end
107
+
108
+ def can_do?(capability_name)
109
+ cap = @capabilities.values.find { |c| c.name == capability_name }
110
+ return false unless cap
111
+
112
+ cap.competence >= 0.5
113
+ end
114
+
115
+ def knows_about?(domain_name)
116
+ dom = @knowledge_domains.values.find { |d| d.name == domain_name }
117
+ return false unless dom
118
+
119
+ dom.confidence >= 0.5
120
+ end
121
+
122
+ def overall_confidence
123
+ return DEFAULT_COMPETENCE if @capabilities.empty?
124
+
125
+ total = @capabilities.values.sum(&:competence)
126
+ total / @capabilities.size.to_f
127
+ end
128
+
129
+ def to_h
130
+ {
131
+ capability_count: @capabilities.size,
132
+ knowledge_domain_count: @knowledge_domains.size,
133
+ overall_confidence: overall_confidence.round(4),
134
+ strength_count: strengths.size,
135
+ weakness_count: weaknesses.size,
136
+ blind_spot_count: blind_spots.size,
137
+ knowledge_gap_count: knowledge_gaps.size,
138
+ prediction_count: @predictions.size,
139
+ history_size: @history.size
140
+ }
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module SelfModel
6
+ module Runners
7
+ module SelfModel
8
+ include Helpers::Constants
9
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
10
+
11
+ def add_self_capability(name:, domain: :general, competence: DEFAULT_COMPETENCE, **)
12
+ cap = model.add_capability(name: name, domain: domain, competence: competence)
13
+ return { success: false, reason: :limit_reached } unless cap
14
+
15
+ { success: true, capability_id: cap.id, competence: cap.competence.round(4), state: cap.state }
16
+ end
17
+
18
+ def add_self_knowledge(name:, depth: 0.0, breadth: 0.0, **)
19
+ dom = model.add_knowledge_domain(name: name, depth: depth, breadth: breadth)
20
+ return { success: false, reason: :limit_reached } unless dom
21
+
22
+ { success: true, domain_id: dom.id, depth: dom.depth.round(4), breadth: dom.breadth.round(4),
23
+ state: dom.state }
24
+ end
25
+
26
+ def predict_own_success(capability_id:, **)
27
+ prediction = model.predict_success(capability_id: capability_id)
28
+ return { success: false, reason: :not_found } unless prediction
29
+
30
+ { success: true }.merge(prediction)
31
+ end
32
+
33
+ def record_self_outcome(capability_id:, predicted:, actual:, **)
34
+ event = model.record_outcome(capability_id: capability_id, predicted: predicted, actual: actual)
35
+ return { success: false, reason: :not_found } unless event
36
+
37
+ cap = model.capabilities[capability_id]
38
+ { success: true, capability_id: capability_id, competence: cap.competence.round(4),
39
+ calibration_error: cap.calibration_error.round(4) }
40
+ end
41
+
42
+ def self_introspection(**)
43
+ { success: true }.merge(model.introspect)
44
+ end
45
+
46
+ def self_strengths(**)
47
+ strengths = model.strengths.map(&:to_h)
48
+ { success: true, strengths: strengths, count: strengths.size }
49
+ end
50
+
51
+ def self_weaknesses(**)
52
+ weaknesses = model.weaknesses.map(&:to_h)
53
+ { success: true, weaknesses: weaknesses, count: weaknesses.size }
54
+ end
55
+
56
+ def self_blind_spots(**)
57
+ blind_spots = model.blind_spots.map(&:to_h)
58
+ { success: true, blind_spots: blind_spots, count: blind_spots.size }
59
+ end
60
+
61
+ def self_calibration_report(**)
62
+ { success: true }.merge(model.calibration_report)
63
+ end
64
+
65
+ def self_model_stats(**)
66
+ { success: true }.merge(model.to_h)
67
+ end
68
+
69
+ private
70
+
71
+ def model
72
+ @model ||= Helpers::SelfModel.new
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module SelfModel
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-self-model
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Esity
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: legion-gaia
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: Predictive self-model for LegionIO — capability tracking, knowledge domain
27
+ modeling, calibration, and metacognitive introspection
28
+ email:
29
+ - matthewdiverson@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/legion/extensions/self_model/client.rb
35
+ - lib/legion/extensions/self_model/helpers/capability.rb
36
+ - lib/legion/extensions/self_model/helpers/constants.rb
37
+ - lib/legion/extensions/self_model/helpers/knowledge_domain.rb
38
+ - lib/legion/extensions/self_model/helpers/self_model.rb
39
+ - lib/legion/extensions/self_model/runners/self_model.rb
40
+ - lib/legion/extensions/self_model/version.rb
41
+ homepage: https://github.com/LegionIO/lex-self-model
42
+ licenses:
43
+ - MIT
44
+ metadata:
45
+ homepage_uri: https://github.com/LegionIO/lex-self-model
46
+ source_code_uri: https://github.com/LegionIO/lex-self-model
47
+ documentation_uri: https://github.com/LegionIO/lex-self-model/blob/master/README.md
48
+ changelog_uri: https://github.com/LegionIO/lex-self-model/blob/master/CHANGELOG.md
49
+ bug_tracker_uri: https://github.com/LegionIO/lex-self-model/issues
50
+ rubygems_mfa_required: 'true'
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '3.4'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.6.9
66
+ specification_version: 4
67
+ summary: Metacognitive self-model for LegionIO
68
+ test_files: []