lex-cognitive-flexibility 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: 75026cb77d550113020b61f55ce67d26557e1a5a450e0f9a8f5d2c7392af968a
4
+ data.tar.gz: 956529d1659ba97303971cc057a9531777cdf4fc4acd1edbb171d37ee5f9b01f
5
+ SHA512:
6
+ metadata.gz: 2211b8a3486ce48132f53e00bcd3ebd77ac7794babd2a93a66e6183f3cae3635a7e0d8aa04cb5e259d6edf05f7b085c72c69b0b7d088855cdf0bd07c8ecae5aa
7
+ data.tar.gz: be6cbdd2da1aec72be4b8a2c5451c4940e484dc70ca487dc386ab254622b41dfe74b2a0555c2f21c2ce2ca2a2ed0452ef1a04420641444fcf8c11866bb379a33
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # lex-cognitive-flexibility
2
+
3
+ Cognitive task-set switching and flexibility tracking for brain-modeled agentic AI in the LegionIO ecosystem.
4
+
5
+ ## What It Does
6
+
7
+ Tracks an agent's ability to shift between different rule frameworks (task sets). Switching between task sets incurs a transient cost that decays over time. The agent's flexibility score updates via EMA with each successful switch. When flexibility is low and the agent stays locked in one dominant task set, the engine flags perseveration. A background actor runs every 10 seconds to decay accumulated switch cost.
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ require 'legion/extensions/cognitive_flexibility'
13
+
14
+ client = Legion::Extensions::CognitiveFlexibility::Client.new
15
+
16
+ # Define two task sets
17
+ client.create_task_set(name: 'analysis_mode', domain: :analytical)
18
+ result = client.create_task_set(name: 'creative_mode', domain: :creative)
19
+ creative_id = result[:task_set][:id]
20
+
21
+ # Switch to creative mode
22
+ client.switch_set(set_id: creative_id)
23
+ # => { success: true, task_set: { ... }, switch_cost: 0.15 }
24
+
25
+ # Check current flexibility state
26
+ client.flexibility_level
27
+ # => { flexibility: 0.64, label: :flexible, perseverating: false, switch_cost: 0.15 }
28
+
29
+ # Tick to decay switch cost (normally handled by actor)
30
+ client.update_cognitive_flexibility
31
+ ```
32
+
33
+ ## Development
34
+
35
+ ```bash
36
+ bundle install
37
+ bundle exec rspec
38
+ bundle exec rubocop
39
+ ```
40
+
41
+ ## License
42
+
43
+ MIT
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveFlexibility
6
+ module Actors
7
+ class Tick < Legion::Extensions::Actors::Every
8
+ INTERVAL = 10
9
+
10
+ def run
11
+ Runners::CognitiveFlexibility.instance_method(:update_cognitive_flexibility).bind_call(runner_instance)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/cognitive_flexibility/helpers/constants'
4
+ require 'legion/extensions/cognitive_flexibility/helpers/task_set'
5
+ require 'legion/extensions/cognitive_flexibility/helpers/flexibility_engine'
6
+ require 'legion/extensions/cognitive_flexibility/runners/cognitive_flexibility'
7
+
8
+ module Legion
9
+ module Extensions
10
+ module CognitiveFlexibility
11
+ class Client
12
+ include Runners::CognitiveFlexibility
13
+
14
+ def initialize(engine: nil, **)
15
+ @engine = engine || Helpers::FlexibilityEngine.new
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :engine
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveFlexibility
6
+ module Helpers
7
+ module Constants
8
+ MAX_TASK_SETS = 30
9
+ MAX_SWITCH_HISTORY = 200
10
+ MAX_RULES_PER_SET = 20
11
+
12
+ SWITCH_COST_BASE = 0.15
13
+ SWITCH_COST_DECAY = 0.02
14
+ PERSEVERATION_THRESHOLD = 0.7
15
+ FLEXIBILITY_FLOOR = 0.1
16
+ DEFAULT_FLEXIBILITY = 0.6
17
+ ADAPTATION_ALPHA = 0.1
18
+
19
+ RULE_TYPES = %i[
20
+ if_then mapping category sorting
21
+ priority sequence conditional
22
+ ].freeze
23
+
24
+ FLEXIBILITY_LABELS = {
25
+ (0.8..) => :fluid,
26
+ (0.6...0.8) => :flexible,
27
+ (0.4...0.6) => :moderate,
28
+ (0.2...0.4) => :rigid,
29
+ (..0.2) => :perseverative
30
+ }.freeze
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveFlexibility
6
+ module Helpers
7
+ class FlexibilityEngine
8
+ include Constants
9
+
10
+ attr_reader :task_sets, :current_set_id, :switch_history, :switch_cost, :flexibility
11
+
12
+ def initialize
13
+ @task_sets = {}
14
+ @current_set_id = nil
15
+ @switch_cost = 0.0
16
+ @flexibility = DEFAULT_FLEXIBILITY
17
+ @switch_history = []
18
+ @counter = 0
19
+ end
20
+
21
+ def create_task_set(name:, domain: :general)
22
+ return nil if @task_sets.size >= MAX_TASK_SETS
23
+
24
+ @counter += 1
25
+ set_id = :"set_#{@counter}"
26
+ ts = TaskSet.new(id: set_id, name: name, domain: domain)
27
+ @task_sets[set_id] = ts
28
+ @current_set_id ||= set_id
29
+ ts
30
+ end
31
+
32
+ def add_rule(set_id:, type:, condition:, action:)
33
+ ts = @task_sets[set_id]
34
+ return nil unless ts
35
+
36
+ ts.add_rule(type: type, condition: condition, action: action)
37
+ end
38
+
39
+ def switch_to(set_id:)
40
+ return nil unless @task_sets.key?(set_id)
41
+ return @task_sets[set_id] if set_id == @current_set_id
42
+
43
+ old_id = @current_set_id
44
+ @task_sets[old_id]&.deactivate
45
+ @task_sets[set_id].activate
46
+ @current_set_id = set_id
47
+ @switch_cost = SWITCH_COST_BASE
48
+ update_flexibility(:success)
49
+ record_switch(from: old_id, to: set_id)
50
+ @task_sets[set_id]
51
+ end
52
+
53
+ def current_set
54
+ @current_set_id ? @task_sets[@current_set_id] : nil
55
+ end
56
+
57
+ def flexibility_label
58
+ FLEXIBILITY_LABELS.each { |range, lbl| return lbl if range.cover?(@flexibility) }
59
+ :perseverative
60
+ end
61
+
62
+ def perseverating?
63
+ cs = current_set
64
+ return false unless cs
65
+
66
+ cs.dominant? && @flexibility < 0.4
67
+ end
68
+
69
+ def available_sets
70
+ @task_sets.values.reject { |ts| ts.id == @current_set_id }.map(&:to_h)
71
+ end
72
+
73
+ def tick
74
+ @switch_cost = [@switch_cost - SWITCH_COST_DECAY, 0.0].max
75
+ end
76
+
77
+ def to_h
78
+ {
79
+ current_set: @current_set_id,
80
+ set_count: @task_sets.size,
81
+ switch_cost: @switch_cost.round(4),
82
+ flexibility: @flexibility.round(4),
83
+ label: flexibility_label,
84
+ perseverating: perseverating?,
85
+ switch_count: @switch_history.size
86
+ }
87
+ end
88
+
89
+ private
90
+
91
+ def update_flexibility(outcome)
92
+ target = outcome == :success ? 1.0 : 0.0
93
+ @flexibility += ADAPTATION_ALPHA * (target - @flexibility)
94
+ @flexibility = @flexibility.clamp(FLEXIBILITY_FLOOR, 1.0)
95
+ end
96
+
97
+ def record_switch(from:, to:)
98
+ @switch_history << { from: from, to: to, cost: @switch_cost, at: Time.now.utc }
99
+ @switch_history.shift while @switch_history.size > MAX_SWITCH_HISTORY
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveFlexibility
6
+ module Helpers
7
+ class TaskSet
8
+ include Constants
9
+
10
+ attr_reader :id, :name, :domain, :rules, :created_at, :use_count
11
+ attr_accessor :activation
12
+
13
+ def initialize(id:, name:, domain: :general, activation: 0.5)
14
+ @id = id
15
+ @name = name
16
+ @domain = domain
17
+ @rules = []
18
+ @activation = activation.to_f.clamp(0.0, 1.0)
19
+ @created_at = Time.now.utc
20
+ @use_count = 0
21
+ end
22
+
23
+ def add_rule(type:, condition:, action:)
24
+ return nil unless RULE_TYPES.include?(type)
25
+ return nil if @rules.size >= MAX_RULES_PER_SET
26
+
27
+ rule = { type: type, condition: condition, action: action }
28
+ @rules << rule
29
+ rule
30
+ end
31
+
32
+ def activate(amount = 0.2)
33
+ @activation = [@activation + amount, 1.0].min
34
+ @use_count += 1
35
+ end
36
+
37
+ def deactivate(amount = 0.2)
38
+ @activation = [@activation - amount, 0.0].max
39
+ end
40
+
41
+ def active?
42
+ @activation >= 0.5
43
+ end
44
+
45
+ def dominant?
46
+ @activation >= PERSEVERATION_THRESHOLD
47
+ end
48
+
49
+ def to_h
50
+ {
51
+ id: @id,
52
+ name: @name,
53
+ domain: @domain,
54
+ activation: @activation.round(4),
55
+ active: active?,
56
+ dominant: dominant?,
57
+ rule_count: @rules.size,
58
+ use_count: @use_count
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveFlexibility
6
+ module Runners
7
+ module CognitiveFlexibility
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def create_task_set(name:, domain: :general, **)
12
+ Legion::Logging.debug "[cognitive_flexibility] create: #{name}"
13
+ ts = engine.create_task_set(name: name, domain: domain)
14
+ ts ? { success: true, task_set: ts.to_h } : { success: false, reason: :limit_reached }
15
+ end
16
+
17
+ def add_rule(set_id:, type:, condition:, action:, **)
18
+ Legion::Logging.debug "[cognitive_flexibility] rule: set=#{set_id} type=#{type}"
19
+ rule = engine.add_rule(set_id: set_id.to_sym, type: type.to_sym, condition: condition, action: action)
20
+ rule ? { success: true, rule: rule } : { success: false, reason: :invalid }
21
+ end
22
+
23
+ def switch_set(set_id:, **)
24
+ Legion::Logging.debug "[cognitive_flexibility] switch to #{set_id}"
25
+ ts = engine.switch_to(set_id: set_id.to_sym)
26
+ if ts
27
+ { success: true, task_set: ts.to_h, switch_cost: engine.switch_cost.round(4) }
28
+ else
29
+ { success: false, reason: :not_found }
30
+ end
31
+ end
32
+
33
+ def current_task_set(**)
34
+ cs = engine.current_set
35
+ cs ? { success: true, task_set: cs.to_h } : { success: true, task_set: nil }
36
+ end
37
+
38
+ def available_sets(**)
39
+ sets = engine.available_sets
40
+ { success: true, sets: sets, count: sets.size }
41
+ end
42
+
43
+ def flexibility_level(**)
44
+ {
45
+ success: true,
46
+ flexibility: engine.flexibility.round(4),
47
+ label: engine.flexibility_label,
48
+ perseverating: engine.perseverating?,
49
+ switch_cost: engine.switch_cost.round(4)
50
+ }
51
+ end
52
+
53
+ def update_cognitive_flexibility(**)
54
+ Legion::Logging.debug '[cognitive_flexibility] tick'
55
+ engine.tick
56
+ { success: true, switch_cost: engine.switch_cost.round(4) }
57
+ end
58
+
59
+ def cognitive_flexibility_stats(**)
60
+ Legion::Logging.debug '[cognitive_flexibility] stats'
61
+ { success: true, stats: engine.to_h }
62
+ end
63
+
64
+ private
65
+
66
+ def engine
67
+ @engine ||= Helpers::FlexibilityEngine.new
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveFlexibility
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/cognitive_flexibility/version'
4
+ require 'legion/extensions/cognitive_flexibility/helpers/constants'
5
+ require 'legion/extensions/cognitive_flexibility/helpers/task_set'
6
+ require 'legion/extensions/cognitive_flexibility/helpers/flexibility_engine'
7
+ require 'legion/extensions/cognitive_flexibility/runners/cognitive_flexibility'
8
+ require 'legion/extensions/cognitive_flexibility/client'
9
+
10
+ module Legion
11
+ module Extensions
12
+ module CognitiveFlexibility
13
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined?(:Core)
14
+ end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-cognitive-flexibility
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: Cognitive flexibility for LegionIO — switching between task sets, mental
27
+ rules, and strategies with perseveration detection
28
+ email:
29
+ - matthewdiverson@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - lib/legion/extensions/cognitive_flexibility.rb
36
+ - lib/legion/extensions/cognitive_flexibility/actors/tick.rb
37
+ - lib/legion/extensions/cognitive_flexibility/client.rb
38
+ - lib/legion/extensions/cognitive_flexibility/helpers/constants.rb
39
+ - lib/legion/extensions/cognitive_flexibility/helpers/flexibility_engine.rb
40
+ - lib/legion/extensions/cognitive_flexibility/helpers/task_set.rb
41
+ - lib/legion/extensions/cognitive_flexibility/runners/cognitive_flexibility.rb
42
+ - lib/legion/extensions/cognitive_flexibility/version.rb
43
+ homepage: https://github.com/LegionIO/lex-cognitive-flexibility
44
+ licenses:
45
+ - MIT
46
+ metadata:
47
+ homepage_uri: https://github.com/LegionIO/lex-cognitive-flexibility
48
+ source_code_uri: https://github.com/LegionIO/lex-cognitive-flexibility
49
+ documentation_uri: https://github.com/LegionIO/lex-cognitive-flexibility/blob/master/README.md
50
+ changelog_uri: https://github.com/LegionIO/lex-cognitive-flexibility/blob/master/CHANGELOG.md
51
+ bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-flexibility/issues
52
+ rubygems_mfa_required: 'true'
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '3.4'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.6.9
68
+ specification_version: 4
69
+ summary: LegionIO cognitive flexibility extension
70
+ test_files: []