lex-cognitive-offloading 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: f8942fc76e24c051b63e26f5ed53f408a22bf9c97cb442dd4c5808ccbf24d1ed
4
+ data.tar.gz: 0e3b27ef6d00e2cf9d41f13ad354c7b8c3973b78dd30f8c414b9171fe31c9a5a
5
+ SHA512:
6
+ metadata.gz: 162c624136fbedff1e2f7f319c1b83399fca03a891bc6c4ecd7f28c16feeba70197e86836c1301fd6a996535e72d9c0e98bbfc8c8a3295277a5428371a3db430
7
+ data.tar.gz: 8a9a349b69e99aa01d9ce4b6f2d17595a76d8d88f15a8c93c8f272d49b81e127b88e5a6f94f0454224c93615c41563760812de716af4ce209cc5891a829bc543
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ gem 'rspec', '~> 3.13'
8
+ gem 'rubocop', '~> 1.75', require: false
9
+ gem 'rubocop-rspec', require: false
10
+
11
+ gem 'legion-gaia', path: '../../legion-gaia'
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/cognitive_offloading/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-cognitive-offloading'
7
+ spec.version = Legion::Extensions::CognitiveOffloading::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Cognitive Offloading'
12
+ spec.description = 'Strategic externalization of cognitive tasks — offloading decisions, store trust, and retrieval tracking for brain-modeled agentic AI'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-cognitive-offloading'
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-cognitive-offloading'
19
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-offloading'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-offloading'
21
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-offloading/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-cognitive-offloading.gemspec Gemfile]
26
+ end
27
+ spec.require_paths = ['lib']
28
+ spec.add_development_dependency 'legion-gaia'
29
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/cognitive_offloading/helpers/constants'
4
+ require 'legion/extensions/cognitive_offloading/helpers/external_store'
5
+ require 'legion/extensions/cognitive_offloading/helpers/offloaded_item'
6
+ require 'legion/extensions/cognitive_offloading/helpers/offloading_engine'
7
+ require 'legion/extensions/cognitive_offloading/runners/cognitive_offloading'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module CognitiveOffloading
12
+ class Client
13
+ include Runners::CognitiveOffloading
14
+
15
+ def initialize(engine: nil, **)
16
+ @offloading_engine = engine || Helpers::OffloadingEngine.new
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :offloading_engine
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveOffloading
6
+ module Helpers
7
+ module Constants
8
+ MAX_ITEMS = 500
9
+ MAX_STORES = 50
10
+ DEFAULT_STORE_TRUST = 0.7
11
+ TRUST_DECAY = 0.02
12
+ TRUST_BOOST = 0.05
13
+
14
+ ITEM_TYPES = %i[
15
+ fact procedure plan context delegation reminder calculation reference
16
+ ].freeze
17
+
18
+ STORE_TYPES = %i[
19
+ database file agent tool memory_aid external_service notes
20
+ ].freeze
21
+
22
+ TRUST_LABELS = {
23
+ (0.8..) => :highly_trusted,
24
+ (0.6...0.8) => :trusted,
25
+ (0.4...0.6) => :cautious,
26
+ (0.2...0.4) => :distrusted,
27
+ (..0.2) => :unreliable
28
+ }.freeze
29
+
30
+ IMPORTANCE_LABELS = {
31
+ (0.8..) => :critical,
32
+ (0.6...0.8) => :important,
33
+ (0.4...0.6) => :moderate,
34
+ (0.2...0.4) => :low,
35
+ (..0.2) => :trivial
36
+ }.freeze
37
+
38
+ OFFLOAD_LABELS = {
39
+ (0.8..) => :heavily_offloaded,
40
+ (0.6...0.8) => :mostly_offloaded,
41
+ (0.4...0.6) => :balanced,
42
+ (0.2...0.4) => :mostly_internal,
43
+ (..0.2) => :self_reliant
44
+ }.freeze
45
+
46
+ RETRIEVAL_SUCCESS_THRESHOLD = 0.7
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require_relative 'constants'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module CognitiveOffloading
9
+ module Helpers
10
+ class ExternalStore
11
+ include Constants
12
+
13
+ attr_reader :id, :name, :store_type, :trust, :items_stored,
14
+ :successful_retrievals, :failed_retrievals, :created_at
15
+
16
+ def initialize(name:, store_type:)
17
+ @id = ::SecureRandom.uuid
18
+ @name = name
19
+ @store_type = store_type
20
+ @trust = DEFAULT_STORE_TRUST
21
+ @items_stored = 0
22
+ @successful_retrievals = 0
23
+ @failed_retrievals = 0
24
+ @created_at = Time.now.utc
25
+ end
26
+
27
+ def increment_items!
28
+ @items_stored += 1
29
+ self
30
+ end
31
+
32
+ def record_success!
33
+ @successful_retrievals += 1
34
+ @trust = (@trust + TRUST_BOOST).clamp(0.0, 1.0).round(10)
35
+ self
36
+ end
37
+
38
+ def record_failure!
39
+ @failed_retrievals += 1
40
+ @trust = (@trust - TRUST_DECAY).clamp(0.0, 1.0).round(10)
41
+ self
42
+ end
43
+
44
+ def retrieval_rate
45
+ total = @successful_retrievals + @failed_retrievals
46
+ return 0.0 if total.zero?
47
+
48
+ (@successful_retrievals.to_f / total).round(10)
49
+ end
50
+
51
+ def reliable?
52
+ @trust >= RETRIEVAL_SUCCESS_THRESHOLD
53
+ end
54
+
55
+ def trust_label
56
+ TRUST_LABELS.find { |range, _| range.cover?(@trust) }&.last || :unreliable
57
+ end
58
+
59
+ def to_h
60
+ {
61
+ id: @id,
62
+ name: @name,
63
+ store_type: @store_type,
64
+ trust: @trust.round(10),
65
+ trust_label: trust_label,
66
+ items_stored: @items_stored,
67
+ successful_retrievals: @successful_retrievals,
68
+ failed_retrievals: @failed_retrievals,
69
+ retrieval_rate: retrieval_rate,
70
+ reliable: reliable?,
71
+ created_at: @created_at
72
+ }
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require_relative 'constants'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module CognitiveOffloading
9
+ module Helpers
10
+ class OffloadedItem
11
+ include Constants
12
+
13
+ attr_reader :id, :content, :item_type, :importance, :store_id,
14
+ :offloaded_at, :retrieved_count, :last_retrieved_at
15
+
16
+ def initialize(content:, item_type:, importance:, store_id:)
17
+ @id = ::SecureRandom.uuid
18
+ @content = content
19
+ @item_type = item_type
20
+ @importance = importance.clamp(0.0, 1.0)
21
+ @store_id = store_id
22
+ @offloaded_at = Time.now.utc
23
+ @retrieved_count = 0
24
+ @last_retrieved_at = nil
25
+ end
26
+
27
+ def retrieve!
28
+ @retrieved_count += 1
29
+ @last_retrieved_at = Time.now.utc
30
+ self
31
+ end
32
+
33
+ def stale?(threshold_seconds: 3600)
34
+ return false if @last_retrieved_at.nil? && @retrieved_count.zero?
35
+
36
+ reference = @last_retrieved_at || @offloaded_at
37
+ (Time.now.utc - reference) > threshold_seconds
38
+ end
39
+
40
+ def importance_label
41
+ IMPORTANCE_LABELS.find { |range, _| range.cover?(@importance) }&.last || :trivial
42
+ end
43
+
44
+ def to_h
45
+ {
46
+ id: @id,
47
+ content: @content,
48
+ item_type: @item_type,
49
+ importance: @importance.round(10),
50
+ importance_label: importance_label,
51
+ store_id: @store_id,
52
+ offloaded_at: @offloaded_at,
53
+ retrieved_count: @retrieved_count,
54
+ last_retrieved_at: @last_retrieved_at
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constants'
4
+ require_relative 'external_store'
5
+ require_relative 'offloaded_item'
6
+
7
+ module Legion
8
+ module Extensions
9
+ module CognitiveOffloading
10
+ module Helpers
11
+ class OffloadingEngine
12
+ include Constants
13
+
14
+ attr_reader :items, :stores
15
+
16
+ def initialize
17
+ @items = {}
18
+ @stores = {}
19
+ end
20
+
21
+ def register_store(name:, store_type:)
22
+ return nil if @stores.size >= MAX_STORES
23
+
24
+ store = ExternalStore.new(name: name, store_type: store_type)
25
+ @stores[store.id] = store
26
+ store
27
+ end
28
+
29
+ def offload(content:, item_type:, importance:, store_id:)
30
+ return nil if @items.size >= MAX_ITEMS
31
+ return nil unless @stores.key?(store_id)
32
+
33
+ item = OffloadedItem.new(
34
+ content: content,
35
+ item_type: item_type,
36
+ importance: importance,
37
+ store_id: store_id
38
+ )
39
+ @items[item.id] = item
40
+ @stores[store_id].increment_items!
41
+ item
42
+ end
43
+
44
+ def retrieve(item_id:)
45
+ item = @items[item_id]
46
+ return nil unless item
47
+
48
+ store = @stores[item.store_id]
49
+ store&.record_success!
50
+ item.retrieve!
51
+ item
52
+ end
53
+
54
+ def retrieve_failed(item_id:)
55
+ item = @items[item_id]
56
+ return nil unless item
57
+
58
+ store = @stores[item.store_id]
59
+ store&.record_failure!
60
+ item
61
+ end
62
+
63
+ def items_in_store(store_id:)
64
+ @items.values.select { |i| i.store_id == store_id }
65
+ end
66
+
67
+ def items_by_type(item_type:)
68
+ @items.values.select { |i| i.item_type == item_type }
69
+ end
70
+
71
+ def most_important_offloaded(limit: 10)
72
+ @items.values
73
+ .sort_by { |i| -i.importance }
74
+ .first(limit)
75
+ end
76
+
77
+ def offloading_ratio
78
+ return 0.0 if MAX_ITEMS.zero?
79
+
80
+ (@items.size.to_f / MAX_ITEMS).round(10)
81
+ end
82
+
83
+ def overall_store_trust
84
+ return 0.0 if @stores.empty?
85
+
86
+ total = @stores.values.sum(&:trust)
87
+ (total / @stores.size).round(10)
88
+ end
89
+
90
+ def most_trusted_store
91
+ @stores.values.max_by(&:trust)
92
+ end
93
+
94
+ def least_trusted_store
95
+ @stores.values.min_by(&:trust)
96
+ end
97
+
98
+ def offloading_report
99
+ {
100
+ total_items: @items.size,
101
+ total_stores: @stores.size,
102
+ offloading_ratio: offloading_ratio,
103
+ offloading_label: offloading_label,
104
+ overall_store_trust: overall_store_trust,
105
+ most_trusted_store: most_trusted_store&.to_h,
106
+ least_trusted_store: least_trusted_store&.to_h,
107
+ items_by_type: items_type_summary,
108
+ stores_summary: @stores.values.map(&:to_h)
109
+ }
110
+ end
111
+
112
+ def to_h
113
+ {
114
+ items: @items.transform_values(&:to_h),
115
+ stores: @stores.transform_values(&:to_h)
116
+ }
117
+ end
118
+
119
+ private
120
+
121
+ def offloading_label
122
+ OFFLOAD_LABELS.find { |range, _| range.cover?(offloading_ratio) }&.last || :self_reliant
123
+ end
124
+
125
+ def items_type_summary
126
+ ITEM_TYPES.to_h do |type|
127
+ [type, @items.values.count { |i| i.item_type == type }]
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveOffloading
6
+ module Runners
7
+ module CognitiveOffloading
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def register_store(name:, store_type:, engine: nil, **)
12
+ eng = engine || offloading_engine
13
+ store = eng.register_store(name: name, store_type: store_type)
14
+ if store
15
+ Legion::Logging.info "[cognitive_offloading] registered store name=#{name} type=#{store_type} id=#{store.id}"
16
+ { success: true, store: store.to_h }
17
+ else
18
+ Legion::Logging.warn '[cognitive_offloading] register_store failed: limit reached or invalid store_type'
19
+ { success: false, reason: :limit_reached }
20
+ end
21
+ end
22
+
23
+ def offload_item(content:, item_type:, importance:, store_id:, engine: nil, **)
24
+ eng = engine || offloading_engine
25
+ item = eng.offload(content: content, item_type: item_type, importance: importance, store_id: store_id)
26
+ if item
27
+ Legion::Logging.info "[cognitive_offloading] offloaded item=#{item.id} type=#{item_type} importance=#{importance.round(2)} store=#{store_id}"
28
+ { success: true, item: item.to_h }
29
+ else
30
+ Legion::Logging.warn "[cognitive_offloading] offload failed: limit reached or store not found store_id=#{store_id}"
31
+ { success: false, reason: :offload_failed }
32
+ end
33
+ end
34
+
35
+ def retrieve_item(item_id:, engine: nil, **)
36
+ eng = engine || offloading_engine
37
+ item = eng.retrieve(item_id: item_id)
38
+ if item
39
+ Legion::Logging.debug "[cognitive_offloading] retrieved item=#{item_id} count=#{item.retrieved_count}"
40
+ { success: true, item: item.to_h }
41
+ else
42
+ Legion::Logging.warn "[cognitive_offloading] retrieve failed: item not found item_id=#{item_id}"
43
+ { success: false, reason: :not_found }
44
+ end
45
+ end
46
+
47
+ def report_retrieval_failure(item_id:, engine: nil, **)
48
+ eng = engine || offloading_engine
49
+ item = eng.retrieve_failed(item_id: item_id)
50
+ if item
51
+ store = eng.stores[item.store_id]
52
+ trust = store&.trust&.round(2)
53
+ Legion::Logging.warn "[cognitive_offloading] retrieval failure item=#{item_id} store_trust=#{trust}"
54
+ { success: true, item_id: item_id, store_trust: trust }
55
+ else
56
+ { success: false, reason: :not_found }
57
+ end
58
+ end
59
+
60
+ def items_in_store(store_id:, engine: nil, **)
61
+ eng = engine || offloading_engine
62
+ items = eng.items_in_store(store_id: store_id)
63
+ Legion::Logging.debug "[cognitive_offloading] items_in_store store=#{store_id} count=#{items.size}"
64
+ { success: true, items: items.map(&:to_h), count: items.size }
65
+ end
66
+
67
+ def items_by_type(item_type:, engine: nil, **)
68
+ eng = engine || offloading_engine
69
+ items = eng.items_by_type(item_type: item_type)
70
+ Legion::Logging.debug "[cognitive_offloading] items_by_type type=#{item_type} count=#{items.size}"
71
+ { success: true, items: items.map(&:to_h), count: items.size }
72
+ end
73
+
74
+ def most_important_offloaded(limit: 10, engine: nil, **)
75
+ eng = engine || offloading_engine
76
+ items = eng.most_important_offloaded(limit: limit)
77
+ Legion::Logging.debug "[cognitive_offloading] most_important limit=#{limit} count=#{items.size}"
78
+ { success: true, items: items.map(&:to_h), count: items.size }
79
+ end
80
+
81
+ def offloading_status(engine: nil, **)
82
+ eng = engine || offloading_engine
83
+ report = eng.offloading_report
84
+ ratio = report[:offloading_ratio]
85
+ Legion::Logging.debug "[cognitive_offloading] status items=#{report[:total_items]} stores=#{report[:total_stores]} ratio=#{ratio}"
86
+ { success: true, report: report }
87
+ end
88
+
89
+ private
90
+
91
+ def offloading_engine
92
+ @offloading_engine ||= Helpers::OffloadingEngine.new
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module CognitiveOffloading
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_offloading/version'
4
+ require 'legion/extensions/cognitive_offloading/helpers/constants'
5
+ require 'legion/extensions/cognitive_offloading/helpers/external_store'
6
+ require 'legion/extensions/cognitive_offloading/helpers/offloaded_item'
7
+ require 'legion/extensions/cognitive_offloading/helpers/offloading_engine'
8
+ require 'legion/extensions/cognitive_offloading/runners/cognitive_offloading'
9
+
10
+ module Legion
11
+ module Extensions
12
+ module CognitiveOffloading
13
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/cognitive_offloading/client'
4
+
5
+ RSpec.describe Legion::Extensions::CognitiveOffloading::Client do
6
+ let(:client) { described_class.new }
7
+
8
+ it 'responds to all runner methods' do
9
+ expect(client).to respond_to(:register_store)
10
+ expect(client).to respond_to(:offload_item)
11
+ expect(client).to respond_to(:retrieve_item)
12
+ expect(client).to respond_to(:report_retrieval_failure)
13
+ expect(client).to respond_to(:items_in_store)
14
+ expect(client).to respond_to(:items_by_type)
15
+ expect(client).to respond_to(:most_important_offloaded)
16
+ expect(client).to respond_to(:offloading_status)
17
+ end
18
+
19
+ it 'accepts an injected engine' do
20
+ engine = Legion::Extensions::CognitiveOffloading::Helpers::OffloadingEngine.new
21
+ c = described_class.new(engine: engine)
22
+ result = c.register_store(name: 'test', store_type: :file)
23
+ expect(result[:success]).to be true
24
+ end
25
+
26
+ it 'creates its own engine when none injected' do
27
+ result = client.register_store(name: 'auto', store_type: :notes)
28
+ expect(result[:success]).to be true
29
+ end
30
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::CognitiveOffloading::Helpers::Constants do
4
+ subject(:constants) { described_class }
5
+
6
+ it 'defines MAX_ITEMS as 500' do
7
+ expect(constants::MAX_ITEMS).to eq(500)
8
+ end
9
+
10
+ it 'defines MAX_STORES as 50' do
11
+ expect(constants::MAX_STORES).to eq(50)
12
+ end
13
+
14
+ it 'defines DEFAULT_STORE_TRUST as 0.7' do
15
+ expect(constants::DEFAULT_STORE_TRUST).to eq(0.7)
16
+ end
17
+
18
+ it 'defines TRUST_DECAY as 0.02' do
19
+ expect(constants::TRUST_DECAY).to eq(0.02)
20
+ end
21
+
22
+ it 'defines TRUST_BOOST as 0.05' do
23
+ expect(constants::TRUST_BOOST).to eq(0.05)
24
+ end
25
+
26
+ it 'defines RETRIEVAL_SUCCESS_THRESHOLD as 0.7' do
27
+ expect(constants::RETRIEVAL_SUCCESS_THRESHOLD).to eq(0.7)
28
+ end
29
+
30
+ it 'defines ITEM_TYPES as frozen array of symbols' do
31
+ expect(constants::ITEM_TYPES).to include(:fact, :procedure, :plan, :context, :delegation,
32
+ :reminder, :calculation, :reference)
33
+ expect(constants::ITEM_TYPES).to be_frozen
34
+ end
35
+
36
+ it 'defines STORE_TYPES as frozen array of symbols' do
37
+ expect(constants::STORE_TYPES).to include(:database, :file, :agent, :tool, :memory_aid,
38
+ :external_service, :notes)
39
+ expect(constants::STORE_TYPES).to be_frozen
40
+ end
41
+
42
+ it 'maps high trust to :highly_trusted' do
43
+ label = constants::TRUST_LABELS.find { |range, _| range.cover?(0.9) }&.last
44
+ expect(label).to eq(:highly_trusted)
45
+ end
46
+
47
+ it 'maps moderate trust to :trusted' do
48
+ label = constants::TRUST_LABELS.find { |range, _| range.cover?(0.7) }&.last
49
+ expect(label).to eq(:trusted)
50
+ end
51
+
52
+ it 'maps low trust to :unreliable' do
53
+ label = constants::TRUST_LABELS.find { |range, _| range.cover?(0.1) }&.last
54
+ expect(label).to eq(:unreliable)
55
+ end
56
+
57
+ it 'maps high importance to :critical' do
58
+ label = constants::IMPORTANCE_LABELS.find { |range, _| range.cover?(0.9) }&.last
59
+ expect(label).to eq(:critical)
60
+ end
61
+
62
+ it 'maps high offloading ratio to :heavily_offloaded' do
63
+ label = constants::OFFLOAD_LABELS.find { |range, _| range.cover?(0.9) }&.last
64
+ expect(label).to eq(:heavily_offloaded)
65
+ end
66
+
67
+ it 'maps low offloading ratio to :self_reliant' do
68
+ label = constants::OFFLOAD_LABELS.find { |range, _| range.cover?(0.1) }&.last
69
+ expect(label).to eq(:self_reliant)
70
+ end
71
+ end