lex-memory 0.1.2 → 0.2.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 +4 -4
- data/CHANGELOG.md +6 -1
- data/Gemfile.lock +3 -1
- data/lib/legion/extensions/memory/batch_decay.rb +40 -0
- data/lib/legion/extensions/memory/persistent_store.rb +90 -0
- data/lib/legion/extensions/memory/quota.rb +51 -0
- data/lib/legion/extensions/memory/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 82f15c25a2be58044cf03787524080a9215578ab854733cf500bc97d68cd3424
|
|
4
|
+
data.tar.gz: f93a129b74c70e1ae602b14c53967f658171b8ff9b783c14f28ba0c1f9d015e8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c639e1b1ad0b3a19c454f37704a7b97fcf5e933c079be7d91dfb54215c6bbd334a2f79ae44c381a1560d92a3e7e76bbf8ed3b5b575ccd7c8307c7cec42fd06a6
|
|
7
|
+
data.tar.gz: a97d6a06e69470aadca4f7fadc92a7f4118818a13203a780506051e83e9f3990023dee1996cfc9863339e709882b24e755e9aaaf4d886e82290e691b950e319d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [0.2.0] - 2026-03-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `PersistentStore`: Sequel-backed durable memory storage per agent (write, read, touch, count, total_bytes, eviction)
|
|
7
|
+
- `Quota`: per-agent trace count and byte size limits with LRU/confidence eviction strategies
|
|
8
|
+
- `BatchDecay`: periodic DB-backed confidence reduction with configurable rate and min threshold
|
|
4
9
|
|
|
5
10
|
## [0.1.2] - 2026-03-16
|
|
6
11
|
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
lex-memory (0.
|
|
4
|
+
lex-memory (0.2.0)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
@@ -76,6 +76,7 @@ GEM
|
|
|
76
76
|
sqlite3 (2.9.2)
|
|
77
77
|
mini_portile2 (~> 2.8.0)
|
|
78
78
|
sqlite3 (2.9.2-arm64-darwin)
|
|
79
|
+
sqlite3 (2.9.2-x86_64-linux-gnu)
|
|
79
80
|
unicode-display_width (3.2.0)
|
|
80
81
|
unicode-emoji (~> 4.1)
|
|
81
82
|
unicode-emoji (4.2.0)
|
|
@@ -83,6 +84,7 @@ GEM
|
|
|
83
84
|
PLATFORMS
|
|
84
85
|
arm64-darwin-25
|
|
85
86
|
ruby
|
|
87
|
+
x86_64-linux
|
|
86
88
|
|
|
87
89
|
DEPENDENCIES
|
|
88
90
|
lex-memory!
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Memory
|
|
6
|
+
module BatchDecay
|
|
7
|
+
DEFAULTS = {
|
|
8
|
+
rate: 0.999,
|
|
9
|
+
min_confidence: 0.01,
|
|
10
|
+
interval_hours: 1
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def apply!(agent_id: nil, rate: DEFAULTS[:rate], min_confidence: DEFAULTS[:min_confidence])
|
|
15
|
+
return { updated: 0, evicted: 0 } unless db_ready?
|
|
16
|
+
|
|
17
|
+
conn = Legion::Data.connection
|
|
18
|
+
ds = conn[PersistentStore::TABLE]
|
|
19
|
+
ds = ds.where(agent_id: agent_id) if agent_id
|
|
20
|
+
|
|
21
|
+
updated = ds.where { confidence > min_confidence }
|
|
22
|
+
.update(confidence: Sequel[:confidence] * rate)
|
|
23
|
+
|
|
24
|
+
evicted = ds.where { confidence <= min_confidence }.delete
|
|
25
|
+
|
|
26
|
+
{ updated: updated, evicted: evicted }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def db_ready?
|
|
32
|
+
defined?(Legion::Data) && Legion::Data.connection&.table_exists?(PersistentStore::TABLE)
|
|
33
|
+
rescue StandardError
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Memory
|
|
6
|
+
class PersistentStore
|
|
7
|
+
TABLE = :memory_traces
|
|
8
|
+
|
|
9
|
+
def initialize(agent_id:)
|
|
10
|
+
@agent_id = agent_id
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def write(trace_type:, content:, associations: {}, confidence: 1.0)
|
|
14
|
+
return nil unless db_ready?
|
|
15
|
+
|
|
16
|
+
Legion::Data.connection[TABLE].insert(
|
|
17
|
+
agent_id: @agent_id,
|
|
18
|
+
trace_type: trace_type.to_s,
|
|
19
|
+
content: content.is_a?(String) ? content : Legion::JSON.dump(content),
|
|
20
|
+
associations: Legion::JSON.dump(associations),
|
|
21
|
+
confidence: confidence,
|
|
22
|
+
created_at: Time.now,
|
|
23
|
+
accessed_at: Time.now
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def read(trace_type: nil, limit: 50, min_confidence: 0.0)
|
|
28
|
+
return [] unless db_ready?
|
|
29
|
+
|
|
30
|
+
ds = Legion::Data.connection[TABLE].where(agent_id: @agent_id)
|
|
31
|
+
ds = ds.where(trace_type: trace_type.to_s) if trace_type
|
|
32
|
+
ds = ds.where { confidence >= min_confidence }
|
|
33
|
+
ds.order(Sequel.desc(:accessed_at)).limit(limit).all
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def touch(id)
|
|
37
|
+
return unless db_ready?
|
|
38
|
+
|
|
39
|
+
Legion::Data.connection[TABLE]
|
|
40
|
+
.where(id: id, agent_id: @agent_id)
|
|
41
|
+
.update(accessed_at: Time.now)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def count
|
|
45
|
+
return 0 unless db_ready?
|
|
46
|
+
|
|
47
|
+
Legion::Data.connection[TABLE].where(agent_id: @agent_id).count
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def total_bytes
|
|
51
|
+
return 0 unless db_ready?
|
|
52
|
+
|
|
53
|
+
Legion::Data.connection[TABLE]
|
|
54
|
+
.where(agent_id: @agent_id)
|
|
55
|
+
.sum(Sequel.function(:length, :content)) || 0
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def delete_lowest_confidence(count: 1)
|
|
59
|
+
return 0 unless db_ready?
|
|
60
|
+
|
|
61
|
+
ids = Legion::Data.connection[TABLE]
|
|
62
|
+
.where(agent_id: @agent_id)
|
|
63
|
+
.order(:confidence)
|
|
64
|
+
.limit(count)
|
|
65
|
+
.select(:id)
|
|
66
|
+
Legion::Data.connection[TABLE].where(id: ids).delete
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def delete_least_recently_used(count: 1)
|
|
70
|
+
return 0 unless db_ready?
|
|
71
|
+
|
|
72
|
+
ids = Legion::Data.connection[TABLE]
|
|
73
|
+
.where(agent_id: @agent_id)
|
|
74
|
+
.order(:accessed_at)
|
|
75
|
+
.limit(count)
|
|
76
|
+
.select(:id)
|
|
77
|
+
Legion::Data.connection[TABLE].where(id: ids).delete
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def db_ready?
|
|
83
|
+
defined?(Legion::Data) && Legion::Data.connection&.table_exists?(TABLE)
|
|
84
|
+
rescue StandardError
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Memory
|
|
6
|
+
class Quota
|
|
7
|
+
DEFAULTS = {
|
|
8
|
+
max_traces: 10_000,
|
|
9
|
+
max_bytes: 52_428_800, # 50MB
|
|
10
|
+
eviction: :lru
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
attr_reader :max_traces, :max_bytes, :eviction
|
|
14
|
+
|
|
15
|
+
def initialize(**opts)
|
|
16
|
+
config = DEFAULTS.merge(opts)
|
|
17
|
+
@max_traces = config[:max_traces]
|
|
18
|
+
@max_bytes = config[:max_bytes]
|
|
19
|
+
@eviction = config[:eviction].to_sym
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def enforce!(store)
|
|
23
|
+
evict_count = store.count - max_traces
|
|
24
|
+
evict!(store, evict_count) if evict_count.positive?
|
|
25
|
+
|
|
26
|
+
overage = store.total_bytes - max_bytes
|
|
27
|
+
evict!(store, estimate_eviction_count(overage)) if overage.positive?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def within_limits?(store)
|
|
31
|
+
store.count <= max_traces && store.total_bytes <= max_bytes
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def evict!(store, count)
|
|
37
|
+
return if count <= 0
|
|
38
|
+
|
|
39
|
+
case eviction
|
|
40
|
+
when :lru then store.delete_least_recently_used(count: count)
|
|
41
|
+
when :lowest_confidence then store.delete_lowest_confidence(count: count)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def estimate_eviction_count(overage_bytes)
|
|
46
|
+
[(overage_bytes / 1024.0 * 10).ceil, 1].max
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-memory
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -30,6 +30,7 @@ files:
|
|
|
30
30
|
- lib/legion/extensions/memory.rb
|
|
31
31
|
- lib/legion/extensions/memory/actors/decay.rb
|
|
32
32
|
- lib/legion/extensions/memory/actors/tier_migration.rb
|
|
33
|
+
- lib/legion/extensions/memory/batch_decay.rb
|
|
33
34
|
- lib/legion/extensions/memory/client.rb
|
|
34
35
|
- lib/legion/extensions/memory/helpers/cache_store.rb
|
|
35
36
|
- lib/legion/extensions/memory/helpers/decay.rb
|
|
@@ -38,6 +39,8 @@ files:
|
|
|
38
39
|
- lib/legion/extensions/memory/helpers/trace.rb
|
|
39
40
|
- lib/legion/extensions/memory/local_migrations/20260316000001_create_memory_traces.rb
|
|
40
41
|
- lib/legion/extensions/memory/local_migrations/20260316000002_create_memory_associations.rb
|
|
42
|
+
- lib/legion/extensions/memory/persistent_store.rb
|
|
43
|
+
- lib/legion/extensions/memory/quota.rb
|
|
41
44
|
- lib/legion/extensions/memory/runners/consolidation.rb
|
|
42
45
|
- lib/legion/extensions/memory/runners/traces.rb
|
|
43
46
|
- lib/legion/extensions/memory/version.rb
|