lex-agentic-memory 0.1.8 → 0.1.11
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 +24 -1
- data/lib/legion/extensions/agentic/memory/archaeology/actors/decay.rb +22 -0
- data/lib/legion/extensions/agentic/memory/archaeology/runners/cognitive_archaeology.rb +6 -0
- data/lib/legion/extensions/agentic/memory/compression/actors/maintenance.rb +22 -0
- data/lib/legion/extensions/agentic/memory/echo/actors/decay.rb +22 -0
- data/lib/legion/extensions/agentic/memory/echo_chamber/actors/decay.rb +22 -0
- data/lib/legion/extensions/agentic/memory/echo_chamber/runners/cognitive_echo_chamber.rb +6 -0
- data/lib/legion/extensions/agentic/memory/immune_memory/actors/decay.rb +22 -0
- data/lib/legion/extensions/agentic/memory/nostalgia/actors/maintenance.rb +22 -0
- data/lib/legion/extensions/agentic/memory/palimpsest/actors/decay.rb +22 -0
- data/lib/legion/extensions/agentic/memory/reserve/actors/maintenance.rb +22 -0
- data/lib/legion/extensions/agentic/memory/semantic_priming/actors/decay.rb +22 -0
- data/lib/legion/extensions/agentic/memory/semantic_satiation/actors/recovery.rb +22 -0
- data/lib/legion/extensions/agentic/memory/trace/actors/quota.rb +22 -0
- data/lib/legion/extensions/agentic/memory/trace/helpers/hot_tier.rb +98 -0
- data/lib/legion/extensions/agentic/memory/trace/helpers/postgres_store.rb +393 -0
- data/lib/legion/extensions/agentic/memory/trace/runners/consolidation.rb +7 -0
- data/lib/legion/extensions/agentic/memory/trace.rb +22 -1
- data/lib/legion/extensions/agentic/memory/version.rb +1 -1
- data/spec/legion/extensions/agentic/memory/archaeology/actors/decay_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/compression/actors/maintenance_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/echo/actors/decay_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/echo_chamber/actors/decay_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/immune_memory/actors/decay_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/nostalgia/actors/maintenance_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/palimpsest/actors/decay_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/reserve/actors/maintenance_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/semantic_priming/actors/decay_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/semantic_satiation/actors/recovery_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/trace/actors/quota_spec.rb +24 -0
- data/spec/legion/extensions/agentic/memory/trace/helpers/hot_tier_spec.rb +337 -0
- data/spec/legion/extensions/agentic/memory/trace/helpers/postgres_store_spec.rb +464 -0
- metadata +27 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 44a4999991835bdd28a7189717f6a53f38928644a86c7f653517331687659901
|
|
4
|
+
data.tar.gz: b3aeb50fddc4b82580430829ec924549690b8dd6171be74ff9213db40b135888
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32930b8b8092925f0793eef8ae8b1a6764d50cbc9692bb2952c46fb969137b41609eed96ecb455fdc7d0518373ebd912075eff8ac47c4deb8e97e1fc9176962b
|
|
7
|
+
data.tar.gz: a142cbf70545c4f80717ddda3d81abab26a1cc2cf5d8e40584625a416629d1551991d716fc6a9592fff194b523b0ebed2963be24fa414039cf11bdb3c934e60a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [0.1.11] - 2026-03-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `Helpers::HotTier` module: Redis hot-tier cache in front of PostgresStore using `Legion::Cache::RedisHash`. Stores traces as Redis hashes with 24-hour TTL, maintains a sorted-set index per tenant, and provides `cache_trace`, `fetch_trace`, and `evict_trace` operations
|
|
7
|
+
- `PostgresStore#retrieve` checks hot tier first; falls through to DB on miss and populates hot tier on DB hit
|
|
8
|
+
- `PostgresStore#store` writes through to hot tier after successful DB write
|
|
9
|
+
- `PostgresStore#delete` evicts from hot tier before DB delete
|
|
10
|
+
- `PostgresStore#update` evicts stale hot-tier entry after DB update
|
|
11
|
+
- 32 new specs covering HotTier interface, serialize/deserialize round-trip, availability guard, and all four PostgresStore integration points
|
|
12
|
+
|
|
13
|
+
## [0.1.10] - 2026-03-25
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `Helpers::PostgresStore`: write-through durable store backed by Legion::Data (PostgreSQL or MySQL), scoped by tenant_id. Implements full store interface: store, retrieve, retrieve_by_type, retrieve_by_domain, all_traces, delete, update, record_coactivation, associations_for, walk_associations, delete_lowest_confidence, delete_least_recently_used, firmware_traces, flush (no-op), db_ready?
|
|
17
|
+
- `create_store` in `Trace` module now selects PostgresStore when a PostgreSQL or MySQL connection is available with both required tables; falls back to CacheStore or in-memory Store
|
|
18
|
+
- `postgres_available?` and `resolve_tenant_id` private helpers on `Trace` module
|
|
19
|
+
- 46 new specs covering all PostgresStore methods using an in-memory SQLite DB
|
|
20
|
+
|
|
21
|
+
## [0.1.9] - 2026-03-25
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- Periodic actors for 10 sub-modules: Archaeology (decay), Compression (maintenance), Echo (decay), EchoChamber (decay), ImmuneMemory (decay), Nostalgia (maintenance), Palimpsest (decay), Reserve (maintenance), SemanticPriming (decay), SemanticSatiation (recovery) (closes #1)
|
|
25
|
+
- Quota enforcement actor for Trace (runs every 300s, calls enforce_quota) (closes #2)
|
|
26
|
+
- Runner methods: `decay_all` for Archaeology and EchoChamber, `enforce_quota` for Trace::Consolidation
|
|
4
27
|
|
|
5
28
|
### Fixed
|
|
6
29
|
- Add Mutex synchronization to trace Store and CacheStore for thread safety during concurrent tick cycles
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module Archaeology
|
|
8
|
+
module Actors
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::CognitiveArchaeology
|
|
11
|
+
def runner_function = 'decay_all'
|
|
12
|
+
def time = 120
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -53,6 +53,12 @@ module Legion
|
|
|
53
53
|
count: results.size }
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def decay_all(rate: Helpers::Constants::PRESERVATION_DECAY, engine: nil, **)
|
|
57
|
+
eng = resolve_engine(engine)
|
|
58
|
+
eng.decay_all!(rate: rate)
|
|
59
|
+
{ success: true, remaining: eng.all_artifacts.size }
|
|
60
|
+
end
|
|
61
|
+
|
|
56
62
|
def archaeology_status(engine: nil, **)
|
|
57
63
|
eng = resolve_engine(engine)
|
|
58
64
|
{ success: true, report: eng.archaeology_report }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module Compression
|
|
8
|
+
module Actors
|
|
9
|
+
class Maintenance < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::CognitiveCompression
|
|
11
|
+
def runner_function = 'compress_all'
|
|
12
|
+
def time = 300
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module Echo
|
|
8
|
+
module Actors
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::CognitiveEcho
|
|
11
|
+
def runner_function = 'decay_all'
|
|
12
|
+
def time = 60
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module EchoChamber
|
|
8
|
+
module Actors
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::CognitiveEchoChamber
|
|
11
|
+
def runner_function = 'decay_all'
|
|
12
|
+
def time = 60
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -65,6 +65,12 @@ module Legion
|
|
|
65
65
|
{ success: true, echoes: echoes.map(&:to_h), count: echoes.size }
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
def decay_all(engine: nil, **)
|
|
69
|
+
eng = engine || default_engine
|
|
70
|
+
eng.decay_all!
|
|
71
|
+
{ success: true, **eng.echo_report }
|
|
72
|
+
end
|
|
73
|
+
|
|
68
74
|
def chamber_status(engine: nil, **)
|
|
69
75
|
eng = engine || default_engine
|
|
70
76
|
{ success: true, **eng.echo_report }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module ImmuneMemory
|
|
8
|
+
module Actors
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::CognitiveImmuneMemory
|
|
11
|
+
def runner_function = 'decay_all'
|
|
12
|
+
def time = 60
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module Nostalgia
|
|
8
|
+
module Actors
|
|
9
|
+
class Maintenance < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::Recall
|
|
11
|
+
def runner_function = 'age_memories'
|
|
12
|
+
def time = 120
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module Palimpsest
|
|
8
|
+
module Actors
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::CognitivePalimpsest
|
|
11
|
+
def runner_function = 'decay_all_ghosts'
|
|
12
|
+
def time = 60
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module Reserve
|
|
8
|
+
module Actors
|
|
9
|
+
class Maintenance < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::CognitiveReserve
|
|
11
|
+
def runner_function = 'update_cognitive_reserve'
|
|
12
|
+
def time = 60
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module SemanticPriming
|
|
8
|
+
module Actors
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::SemanticPriming
|
|
11
|
+
def runner_function = 'decay'
|
|
12
|
+
def time = 30
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module SemanticSatiation
|
|
8
|
+
module Actors
|
|
9
|
+
class Recovery < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::SemanticSatiation
|
|
11
|
+
def runner_function = 'recover'
|
|
12
|
+
def time = 60
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module Trace
|
|
8
|
+
module Actor
|
|
9
|
+
class Quota < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class = Runners::Consolidation
|
|
11
|
+
def runner_function = 'enforce_quota'
|
|
12
|
+
def time = 300
|
|
13
|
+
def use_runner? = false
|
|
14
|
+
def check_subtask? = false
|
|
15
|
+
def generate_task? = false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Memory
|
|
7
|
+
module Trace
|
|
8
|
+
module Helpers
|
|
9
|
+
module HotTier
|
|
10
|
+
HOT_TTL = 86_400 # 24 hours
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# Cache a trace in the Redis hot tier.
|
|
15
|
+
def cache_trace(trace, tenant_id: nil)
|
|
16
|
+
return unless available?
|
|
17
|
+
|
|
18
|
+
tid = tenant_id || trace[:partition_id]
|
|
19
|
+
key = trace_key(tid, trace[:trace_id])
|
|
20
|
+
data = serialize_trace(trace)
|
|
21
|
+
Legion::Cache::RedisHash.hset(key, data)
|
|
22
|
+
Legion::Cache::RedisHash.expire(key, HOT_TTL)
|
|
23
|
+
|
|
24
|
+
index_key = "legion:tier:hot:#{tid}"
|
|
25
|
+
Legion::Cache::RedisHash.zadd(index_key, Time.now.to_f, trace[:trace_id])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Fetch a trace from the hot tier. Returns a deserialized trace hash or nil on miss.
|
|
29
|
+
def fetch_trace(trace_id, tenant_id: nil)
|
|
30
|
+
return nil unless available?
|
|
31
|
+
|
|
32
|
+
key = trace_key(tenant_id, trace_id)
|
|
33
|
+
data = Legion::Cache::RedisHash.hgetall(key)
|
|
34
|
+
return nil if data.nil? || data.empty?
|
|
35
|
+
|
|
36
|
+
deserialize_trace(data)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Evict a trace from the hot tier and remove it from the sorted-set index.
|
|
40
|
+
def evict_trace(trace_id, tenant_id: nil)
|
|
41
|
+
return unless available?
|
|
42
|
+
|
|
43
|
+
key = trace_key(tenant_id, trace_id)
|
|
44
|
+
Legion::Cache.delete(key)
|
|
45
|
+
|
|
46
|
+
index_key = "legion:tier:hot:#{tenant_id}"
|
|
47
|
+
Legion::Cache::RedisHash.zrem(index_key, trace_id)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns true when the RedisHash module is loaded and Redis is reachable.
|
|
51
|
+
def available?
|
|
52
|
+
defined?(Legion::Cache::RedisHash) &&
|
|
53
|
+
Legion::Cache::RedisHash.redis_available?
|
|
54
|
+
rescue StandardError
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Build the namespaced Redis key for a trace.
|
|
59
|
+
def trace_key(tenant_id, trace_id)
|
|
60
|
+
"legion:trace:#{tenant_id}:#{trace_id}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Serialize a trace hash to a string-only flat hash suitable for Redis HSET.
|
|
64
|
+
def serialize_trace(trace)
|
|
65
|
+
{
|
|
66
|
+
'trace_id' => trace[:trace_id].to_s,
|
|
67
|
+
'trace_type' => trace[:trace_type].to_s,
|
|
68
|
+
'content_payload' => trace[:content_payload].to_s,
|
|
69
|
+
'strength' => trace[:strength].to_s,
|
|
70
|
+
'peak_strength' => trace[:peak_strength].to_s,
|
|
71
|
+
'confidence' => trace[:confidence].to_s,
|
|
72
|
+
'storage_tier' => 'hot',
|
|
73
|
+
'partition_id' => trace[:partition_id].to_s,
|
|
74
|
+
'last_reinforced' => (trace[:last_reinforced] || Time.now).to_s
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Deserialize a Redis string-hash back to a typed trace hash.
|
|
79
|
+
def deserialize_trace(data)
|
|
80
|
+
{
|
|
81
|
+
trace_id: data['trace_id'],
|
|
82
|
+
trace_type: data['trace_type']&.to_sym,
|
|
83
|
+
content_payload: data['content_payload'],
|
|
84
|
+
strength: data['strength']&.to_f,
|
|
85
|
+
peak_strength: data['peak_strength']&.to_f,
|
|
86
|
+
confidence: data['confidence']&.to_f,
|
|
87
|
+
storage_tier: :hot,
|
|
88
|
+
partition_id: data['partition_id'],
|
|
89
|
+
last_reinforced: data['last_reinforced']
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|