lex-intuition 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: 54cf588e11b9e4fa679d51da57b237384f363af32e94ecd858ebab56a8d45c64
4
+ data.tar.gz: b82d54cbcefe2252f333a2afb9373387cd3b031e33bef9fa110a286483ea7325
5
+ SHA512:
6
+ metadata.gz: 61e9043b280f39a6df62f982938007d829936bff7d420b1689d60ad49ae6cd8ad1a2835cb8981e11959226f9187d9790709e98e5f7bd99108ef3cb3dd3590221
7
+ data.tar.gz: d4af55546abe2e93d6d1c4dbd114496ea1f2848516509c62e9133906e39c2b07f3771e742c9d2214e4daf3bae16edde9dadd9e83a3aabeb755ecc21ce3bddc56
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Intuition
6
+ class Client
7
+ include Runners::Intuition
8
+
9
+ def initialize(engine: nil)
10
+ @engine = engine || Helpers::IntuitionEngine.new
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Intuition
6
+ module Helpers
7
+ module Constants
8
+ MAX_PATTERNS = 200
9
+ MAX_HEURISTICS = 50
10
+ MAX_HISTORY = 300
11
+
12
+ # Recognition threshold: pattern match score needed to trigger recognition
13
+ RECOGNITION_THRESHOLD = 0.6
14
+
15
+ # Confidence: how much the agent trusts its intuitions
16
+ DEFAULT_CONFIDENCE = 0.5
17
+ CONFIDENCE_FLOOR = 0.05
18
+ CONFIDENCE_CEILING = 0.95
19
+
20
+ # Reinforcement: how fast patterns strengthen/weaken
21
+ REINFORCEMENT_RATE = 0.1
22
+ DECAY_RATE = 0.01
23
+
24
+ # Speed advantage: intuition is N times faster than deliberation
25
+ SPEED_MULTIPLIER = 5
26
+
27
+ PATTERN_STATES = %i[nascent developing reliable expert].freeze
28
+
29
+ HEURISTIC_TYPES = %i[
30
+ recognition take_the_best satisficing
31
+ fast_and_frugal anchored gaze
32
+ ].freeze
33
+
34
+ INTUITION_MODES = %i[
35
+ gut_feeling pattern_match heuristic_shortcut
36
+ recognition_primed compiled_expertise
37
+ ].freeze
38
+
39
+ CONFIDENCE_LABELS = {
40
+ (0.8..) => :strong_hunch,
41
+ (0.6...0.8) => :leaning,
42
+ (0.4...0.6) => :uncertain,
43
+ (0.2...0.4) => :weak_signal,
44
+ (..0.2) => :noise
45
+ }.freeze
46
+
47
+ STATE_THRESHOLDS = {
48
+ expert: 0.85,
49
+ reliable: 0.65,
50
+ developing: 0.35
51
+ }.freeze
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Intuition
6
+ module Helpers
7
+ class Heuristic
8
+ include Constants
9
+
10
+ attr_reader :id, :name, :heuristic_type, :domain, :uses, :successes
11
+
12
+ def initialize(id:, name:, heuristic_type:, domain: :general)
13
+ @id = id
14
+ @name = name
15
+ @heuristic_type = resolve_type(heuristic_type)
16
+ @domain = domain
17
+ @uses = 0
18
+ @successes = 0
19
+ end
20
+
21
+ def apply
22
+ @uses += 1
23
+ end
24
+
25
+ def record_outcome(success:)
26
+ @successes += 1 if success
27
+ end
28
+
29
+ def success_rate
30
+ return 0.0 if @uses.zero?
31
+
32
+ @successes.to_f / @uses
33
+ end
34
+
35
+ def effective?
36
+ @uses >= 3 && success_rate >= 0.6
37
+ end
38
+
39
+ def to_h
40
+ {
41
+ id: @id,
42
+ name: @name,
43
+ heuristic_type: @heuristic_type,
44
+ domain: @domain,
45
+ uses: @uses,
46
+ successes: @successes,
47
+ success_rate: success_rate.round(4),
48
+ effective: effective?
49
+ }
50
+ end
51
+
52
+ private
53
+
54
+ def resolve_type(type)
55
+ sym = type.to_sym
56
+ HEURISTIC_TYPES.include?(sym) ? sym : :fast_and_frugal
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Intuition
6
+ module Helpers
7
+ class IntuitionEngine
8
+ include Constants
9
+
10
+ attr_reader :patterns, :heuristics, :history
11
+
12
+ def initialize
13
+ @patterns = {}
14
+ @heuristics = {}
15
+ @pattern_count = 0
16
+ @heuristic_count = 0
17
+ @history = []
18
+ end
19
+
20
+ def learn_pattern(cue:, response:, domain: :general, strength: DEFAULT_CONFIDENCE)
21
+ return nil if @patterns.size >= MAX_PATTERNS
22
+
23
+ @pattern_count += 1
24
+ pattern = Pattern.new(
25
+ id: :"pat_#{@pattern_count}",
26
+ cue: cue,
27
+ response: response,
28
+ domain: domain,
29
+ strength: strength
30
+ )
31
+ @patterns[pattern.id] = pattern
32
+ record_event(:learn, pattern_id: pattern.id)
33
+ pattern
34
+ end
35
+
36
+ def recognize(input_cue:, domain: nil)
37
+ candidates = domain ? domain_patterns(domain) : @patterns.values
38
+ matches = candidates.filter_map do |p|
39
+ score = p.match_score(input_cue)
40
+ { pattern: p, score: score } if score >= RECOGNITION_THRESHOLD
41
+ end
42
+ matches.sort_by { |m| -m[:score] }
43
+ end
44
+
45
+ def intuit(input_cue:, domain: nil)
46
+ matches = recognize(input_cue: input_cue, domain: domain)
47
+ return nil if matches.empty?
48
+
49
+ best = matches.first
50
+ record_event(:intuit, pattern_id: best[:pattern].id, score: best[:score])
51
+ {
52
+ response: best[:pattern].response,
53
+ pattern_id: best[:pattern].id,
54
+ confidence: best[:pattern].strength,
55
+ match: best[:score],
56
+ mode: intuition_mode(best)
57
+ }
58
+ end
59
+
60
+ def reinforce_pattern(pattern_id:, success:)
61
+ pattern = @patterns[pattern_id]
62
+ return nil unless pattern
63
+
64
+ pattern.reinforce(success: success)
65
+ record_event(:reinforce, pattern_id: pattern_id, success: success)
66
+ pattern.strength
67
+ end
68
+
69
+ def add_heuristic(name:, heuristic_type:, domain: :general)
70
+ return nil if @heuristics.size >= MAX_HEURISTICS
71
+
72
+ @heuristic_count += 1
73
+ heuristic = Heuristic.new(
74
+ id: :"heur_#{@heuristic_count}",
75
+ name: name,
76
+ heuristic_type: heuristic_type,
77
+ domain: domain
78
+ )
79
+ @heuristics[heuristic.id] = heuristic
80
+ heuristic
81
+ end
82
+
83
+ def apply_heuristic(heuristic_id:)
84
+ heuristic = @heuristics[heuristic_id]
85
+ return nil unless heuristic
86
+
87
+ heuristic.apply
88
+ record_event(:apply_heuristic, heuristic_id: heuristic_id)
89
+ heuristic.to_h
90
+ end
91
+
92
+ def record_heuristic_outcome(heuristic_id:, success:)
93
+ heuristic = @heuristics[heuristic_id]
94
+ return nil unless heuristic
95
+
96
+ heuristic.record_outcome(success: success)
97
+ end
98
+
99
+ def reliable_patterns
100
+ @patterns.values.select(&:reliable?).map(&:to_h)
101
+ end
102
+
103
+ def expert_patterns
104
+ @patterns.values.select(&:expert?).map(&:to_h)
105
+ end
106
+
107
+ def effective_heuristics
108
+ @heuristics.values.select(&:effective?).map(&:to_h)
109
+ end
110
+
111
+ def patterns_in(domain:)
112
+ domain_patterns(domain).map(&:to_h)
113
+ end
114
+
115
+ def decay_all
116
+ @patterns.each_value(&:decay)
117
+ end
118
+
119
+ def to_h
120
+ {
121
+ pattern_count: @patterns.size,
122
+ heuristic_count: @heuristics.size,
123
+ reliable_pattern_count: @patterns.values.count(&:reliable?),
124
+ expert_pattern_count: @patterns.values.count(&:expert?),
125
+ effective_heuristic_count: @heuristics.values.count(&:effective?),
126
+ history_size: @history.size
127
+ }
128
+ end
129
+
130
+ private
131
+
132
+ def domain_patterns(domain)
133
+ @patterns.values.select { |p| p.domain == domain }
134
+ end
135
+
136
+ def intuition_mode(match)
137
+ pattern = match[:pattern]
138
+ return :compiled_expertise if pattern.expert?
139
+ return :pattern_match if pattern.reliable?
140
+ return :recognition_primed if match[:score] >= 0.8
141
+
142
+ :gut_feeling
143
+ end
144
+
145
+ def record_event(type, **details)
146
+ @history << { type: type, at: Time.now.utc }.merge(details)
147
+ @history.shift while @history.size > MAX_HISTORY
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Intuition
6
+ module Helpers
7
+ class Pattern
8
+ include Constants
9
+
10
+ attr_reader :id, :cue, :domain, :response, :strength,
11
+ :encounters, :successes, :state
12
+
13
+ def initialize(id:, cue:, response:, domain: :general, strength: DEFAULT_CONFIDENCE)
14
+ @id = id
15
+ @cue = cue
16
+ @response = response
17
+ @domain = domain
18
+ @strength = strength.to_f.clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING)
19
+ @encounters = 0
20
+ @successes = 0
21
+ @state = compute_state
22
+ end
23
+
24
+ def match_score(input_cue)
25
+ return 0.0 unless input_cue.is_a?(Hash) && @cue.is_a?(Hash)
26
+
27
+ shared = @cue.keys & input_cue.keys
28
+ return 0.0 if shared.empty?
29
+
30
+ matches = shared.count { |k| @cue[k] == input_cue[k] }
31
+ matches.to_f / [@cue.size, input_cue.size].max
32
+ end
33
+
34
+ def recognized?(input_cue)
35
+ match_score(input_cue) >= RECOGNITION_THRESHOLD
36
+ end
37
+
38
+ def reinforce(success:)
39
+ @encounters += 1
40
+ @successes += 1 if success
41
+ shift = success ? REINFORCEMENT_RATE : -REINFORCEMENT_RATE
42
+ @strength = (@strength + shift).clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING)
43
+ @state = compute_state
44
+ @strength
45
+ end
46
+
47
+ def decay
48
+ @strength = (@strength - DECAY_RATE).clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING)
49
+ @state = compute_state
50
+ end
51
+
52
+ def success_rate
53
+ return 0.0 if @encounters.zero?
54
+
55
+ @successes.to_f / @encounters
56
+ end
57
+
58
+ def reliable?
59
+ @strength >= STATE_THRESHOLDS[:reliable] && @encounters >= 3
60
+ end
61
+
62
+ def expert?
63
+ @strength >= STATE_THRESHOLDS[:expert] && @encounters >= 10
64
+ end
65
+
66
+ def confidence_label
67
+ CONFIDENCE_LABELS.each { |range, lbl| return lbl if range.cover?(@strength) }
68
+ :noise
69
+ end
70
+
71
+ def to_h
72
+ {
73
+ id: @id,
74
+ cue: @cue,
75
+ response: @response,
76
+ domain: @domain,
77
+ strength: @strength.round(4),
78
+ encounters: @encounters,
79
+ successes: @successes,
80
+ success_rate: success_rate.round(4),
81
+ state: @state,
82
+ confidence_label: confidence_label,
83
+ reliable: reliable?,
84
+ expert: expert?
85
+ }
86
+ end
87
+
88
+ private
89
+
90
+ def compute_state
91
+ return :expert if @strength >= STATE_THRESHOLDS[:expert]
92
+ return :reliable if @strength >= STATE_THRESHOLDS[:reliable]
93
+ return :developing if @strength >= STATE_THRESHOLDS[:developing]
94
+
95
+ :nascent
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Intuition
6
+ module Runners
7
+ module Intuition
8
+ include Helpers::Constants
9
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
10
+
11
+ def learn_intuitive_pattern(cue:, response:, domain: :general, strength: DEFAULT_CONFIDENCE, **)
12
+ pattern = engine.learn_pattern(cue: cue, response: response, domain: domain, strength: strength)
13
+ return { success: false, reason: :limit_reached } unless pattern
14
+
15
+ { success: true, pattern_id: pattern.id, strength: pattern.strength }
16
+ end
17
+
18
+ def intuitive_recognize(input_cue:, domain: nil, **)
19
+ matches = engine.recognize(input_cue: input_cue, domain: domain)
20
+ {
21
+ success: true,
22
+ matches: matches.map { |m| { pattern_id: m[:pattern].id, score: m[:score].round(4) } },
23
+ count: matches.size
24
+ }
25
+ end
26
+
27
+ def intuit_response(input_cue:, domain: nil, **)
28
+ result = engine.intuit(input_cue: input_cue, domain: domain)
29
+ return { success: false, reason: :no_match } unless result
30
+
31
+ { success: true }.merge(result)
32
+ end
33
+
34
+ def reinforce_intuition(pattern_id:, success:, **)
35
+ result = engine.reinforce_pattern(pattern_id: pattern_id, success: success)
36
+ return { success: false, reason: :not_found } unless result
37
+
38
+ { success: true, pattern_id: pattern_id, strength: result.round(4) }
39
+ end
40
+
41
+ def add_intuitive_heuristic(name:, heuristic_type:, domain: :general, **)
42
+ heuristic = engine.add_heuristic(name: name, heuristic_type: heuristic_type, domain: domain)
43
+ return { success: false, reason: :limit_reached } unless heuristic
44
+
45
+ { success: true, heuristic_id: heuristic.id }
46
+ end
47
+
48
+ def apply_intuitive_heuristic(heuristic_id:, **)
49
+ result = engine.apply_heuristic(heuristic_id: heuristic_id)
50
+ return { success: false, reason: :not_found } unless result
51
+
52
+ { success: true }.merge(result)
53
+ end
54
+
55
+ def reliable_intuitions(**)
56
+ patterns = engine.reliable_patterns
57
+ { success: true, patterns: patterns, count: patterns.size }
58
+ end
59
+
60
+ def expert_intuitions(**)
61
+ patterns = engine.expert_patterns
62
+ { success: true, patterns: patterns, count: patterns.size }
63
+ end
64
+
65
+ def update_intuition(**)
66
+ engine.decay_all
67
+ { success: true }.merge(engine.to_h)
68
+ end
69
+
70
+ def intuition_stats(**)
71
+ { success: true }.merge(engine.to_h)
72
+ end
73
+
74
+ private
75
+
76
+ def engine
77
+ @engine ||= Helpers::IntuitionEngine.new
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Intuition
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-intuition
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: Recognition-primed decision making for LegionIO — fast-and-frugal heuristics,
27
+ pattern recognition, and compiled expertise
28
+ email:
29
+ - matthewdiverson@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/legion/extensions/intuition/client.rb
35
+ - lib/legion/extensions/intuition/helpers/constants.rb
36
+ - lib/legion/extensions/intuition/helpers/heuristic.rb
37
+ - lib/legion/extensions/intuition/helpers/intuition_engine.rb
38
+ - lib/legion/extensions/intuition/helpers/pattern.rb
39
+ - lib/legion/extensions/intuition/runners/intuition.rb
40
+ - lib/legion/extensions/intuition/version.rb
41
+ homepage: https://github.com/LegionIO/lex-intuition
42
+ licenses:
43
+ - MIT
44
+ metadata:
45
+ homepage_uri: https://github.com/LegionIO/lex-intuition
46
+ source_code_uri: https://github.com/LegionIO/lex-intuition
47
+ documentation_uri: https://github.com/LegionIO/lex-intuition/blob/master/README.md
48
+ changelog_uri: https://github.com/LegionIO/lex-intuition/blob/master/CHANGELOG.md
49
+ bug_tracker_uri: https://github.com/LegionIO/lex-intuition/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: Intuition and pattern recognition for LegionIO
68
+ test_files: []