lex-memory 0.1.2
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 +7 -0
- data/.github/workflows/ci.yml +16 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +53 -0
- data/CHANGELOG.md +22 -0
- data/CLAUDE.md +129 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +99 -0
- data/README.md +158 -0
- data/lex-memory.gemspec +28 -0
- data/lib/legion/extensions/memory/actors/decay.rb +41 -0
- data/lib/legion/extensions/memory/actors/tier_migration.rb +41 -0
- data/lib/legion/extensions/memory/client.rb +28 -0
- data/lib/legion/extensions/memory/helpers/cache_store.rb +163 -0
- data/lib/legion/extensions/memory/helpers/decay.rb +64 -0
- data/lib/legion/extensions/memory/helpers/error_tracer.rb +90 -0
- data/lib/legion/extensions/memory/helpers/store.rb +256 -0
- data/lib/legion/extensions/memory/helpers/trace.rb +102 -0
- data/lib/legion/extensions/memory/local_migrations/20260316000001_create_memory_traces.rb +31 -0
- data/lib/legion/extensions/memory/local_migrations/20260316000002_create_memory_associations.rb +13 -0
- data/lib/legion/extensions/memory/runners/consolidation.rb +117 -0
- data/lib/legion/extensions/memory/runners/traces.rb +101 -0
- data/lib/legion/extensions/memory/version.rb +9 -0
- data/lib/legion/extensions/memory.rb +52 -0
- metadata +71 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 887236dfa6fe8f5f792dc57af29a84b1404bac3938fd3482644e15cbdfde394d
|
|
4
|
+
data.tar.gz: 751161d389e5b4c2a4ecb690b31c0a2223a2e4a7d205623c40585131dedbd75b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 25fdd67e7aa1b5ed17fbe66253c9a4b925cfd0d82dcc2ccafe63a08f06f7a7126c8b33b99163d415043b43c3456be6a6ff43d1912de27580d255e2e033e176f0
|
|
7
|
+
data.tar.gz: eda7ac4517f976d8b6f2672c872487e649970df67c1cb89a8249205f8c4294717f965871f6f43e7ab3deb9feefd68df15d1bb2af2f9a57002f2b956036a26f4b
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
ci:
|
|
9
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
needs: ci
|
|
13
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
15
|
+
secrets:
|
|
16
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.4
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
6
|
+
Layout/LineLength:
|
|
7
|
+
Max: 160
|
|
8
|
+
|
|
9
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
|
10
|
+
EnforcedStyle: space
|
|
11
|
+
|
|
12
|
+
Layout/HashAlignment:
|
|
13
|
+
EnforcedHashRocketStyle: table
|
|
14
|
+
EnforcedColonStyle: table
|
|
15
|
+
|
|
16
|
+
Metrics/MethodLength:
|
|
17
|
+
Max: 50
|
|
18
|
+
|
|
19
|
+
Metrics/ClassLength:
|
|
20
|
+
Max: 1500
|
|
21
|
+
|
|
22
|
+
Metrics/ModuleLength:
|
|
23
|
+
Max: 1500
|
|
24
|
+
|
|
25
|
+
Metrics/BlockLength:
|
|
26
|
+
Max: 40
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'spec/**/*'
|
|
29
|
+
|
|
30
|
+
Metrics/AbcSize:
|
|
31
|
+
Max: 60
|
|
32
|
+
|
|
33
|
+
Metrics/CyclomaticComplexity:
|
|
34
|
+
Max: 15
|
|
35
|
+
|
|
36
|
+
Metrics/PerceivedComplexity:
|
|
37
|
+
Max: 17
|
|
38
|
+
|
|
39
|
+
Style/Documentation:
|
|
40
|
+
Enabled: false
|
|
41
|
+
|
|
42
|
+
Style/SymbolArray:
|
|
43
|
+
Enabled: true
|
|
44
|
+
|
|
45
|
+
Style/FrozenStringLiteralComment:
|
|
46
|
+
Enabled: true
|
|
47
|
+
EnforcedStyle: always
|
|
48
|
+
|
|
49
|
+
Naming/FileName:
|
|
50
|
+
Enabled: false
|
|
51
|
+
|
|
52
|
+
Naming/PredicateMethod:
|
|
53
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.1.2] - 2026-03-16
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Add missing `require 'client'` to entry point — `Memory::Client` was never auto-loaded at runtime, causing dream cycle and other consumers to skip memory integration
|
|
9
|
+
|
|
10
|
+
## [0.1.1] - 2026-03-16
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Strip default procs from associations hash before Memcached serialization in `CacheStore#flush` to prevent `can't dump hash with default proc` marshalling error
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `spec/legion/extensions/memory/actors/decay_spec.rb` (7 examples) — tests for the Decay actor (Every 60s)
|
|
17
|
+
- `spec/legion/extensions/memory/actors/tier_migration_spec.rb` (7 examples) — tests for the TierMigration actor (Every 300s)
|
|
18
|
+
|
|
19
|
+
## [0.1.0] - 2026-03-13
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- Initial release
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# lex-memory
|
|
2
|
+
|
|
3
|
+
**Level 3 Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
|
|
5
|
+
- **Grandparent**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Memory trace system for the LegionIO cognitive architecture. Implements typed trace storage, power-law decay with emotional modulation, reinforcement (with imprint window boost), Hebbian association, tiered retrieval scoring, and selective erasure.
|
|
10
|
+
|
|
11
|
+
## Gem Info
|
|
12
|
+
|
|
13
|
+
- **Gem name**: `lex-memory`
|
|
14
|
+
- **Version**: `0.1.2`
|
|
15
|
+
- **Module**: `Legion::Extensions::Memory`
|
|
16
|
+
- **Ruby**: `>= 3.4`
|
|
17
|
+
- **License**: MIT
|
|
18
|
+
|
|
19
|
+
## File Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
lib/legion/extensions/memory/
|
|
23
|
+
version.rb
|
|
24
|
+
helpers/
|
|
25
|
+
trace.rb # Trace structure, TRACE_TYPES, decay constants, new_trace factory
|
|
26
|
+
decay.rb # compute_decay, compute_reinforcement, compute_retrieval_score, compute_storage_tier
|
|
27
|
+
store.rb # In-memory Store class (hash-backed, not DB-persisted)
|
|
28
|
+
cache_store.rb # Cache-backed Store using Legion::Cache (Memcached/Redis) — flush/reload API
|
|
29
|
+
error_tracer.rb # Wraps Legion::Logging.error/fatal to auto-create episodic traces (60s debounce)
|
|
30
|
+
runners/
|
|
31
|
+
traces.rb # store_trace, get_trace, retrieve_by_type/domain/associated/ranked,
|
|
32
|
+
# delete_trace, retrieve_and_reinforce
|
|
33
|
+
consolidation.rb # reinforce, decay_cycle, migrate_tier, hebbian_link, erase_by_type/agent
|
|
34
|
+
actors/
|
|
35
|
+
decay.rb # Every 60s - calls decay_cycle
|
|
36
|
+
tier_migration.rb # Every 300s - calls migrate_tier
|
|
37
|
+
local_migrations/
|
|
38
|
+
20260316000001_create_memory_traces.rb # SQLite persistence for traces
|
|
39
|
+
20260316000002_create_memory_associations.rb # SQLite persistence for associations
|
|
40
|
+
spec/
|
|
41
|
+
legion/extensions/memory/
|
|
42
|
+
helpers/
|
|
43
|
+
trace_spec.rb
|
|
44
|
+
decay_spec.rb
|
|
45
|
+
store_spec.rb
|
|
46
|
+
runners/
|
|
47
|
+
traces_spec.rb
|
|
48
|
+
consolidation_spec.rb
|
|
49
|
+
client_spec.rb
|
|
50
|
+
local_persistence_spec.rb
|
|
51
|
+
legion/extensions/memory_spec.rb
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Key Constants (Helpers::Trace)
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
E_WEIGHT = 0.3 # emotional intensity weight on decay
|
|
58
|
+
R_AMOUNT = 0.10 # base reinforcement per call
|
|
59
|
+
IMPRINT_MULTIPLIER = 3.0 # multiplier during imprint window
|
|
60
|
+
ARCHIVE_THRESHOLD = 0.05 # traces below this are archived
|
|
61
|
+
AUTO_FIRE_THRESHOLD = 0.85 # procedural auto-fire threshold
|
|
62
|
+
PRUNE_THRESHOLD = 0.01 # traces below this are deleted
|
|
63
|
+
HOT_TIER_WINDOW = 86_400 # 24h
|
|
64
|
+
WARM_TIER_WINDOW = 7_776_000 # 90 days
|
|
65
|
+
RETRIEVAL_RECENCY_HALF = 3600 # 1h half-life for recency scoring
|
|
66
|
+
ASSOCIATION_BONUS = 0.15 # Hebbian association retrieval bonus
|
|
67
|
+
MAX_ASSOCIATIONS = 20 # max Hebbian links per trace
|
|
68
|
+
COACTIVATION_THRESHOLD = 3 # co-activations before link forms
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Store (Development vs Production)
|
|
72
|
+
|
|
73
|
+
`Helpers::Store` is an in-memory hash-backed store for development/testing. `Helpers::CacheStore` is a cache-backed store backed by `Legion::Cache` (Memcached/Redis) for shared cross-process access.
|
|
74
|
+
|
|
75
|
+
**Shared store singleton**: `Legion::Extensions::Memory.shared_store` returns a process-wide store instance. All runner modules (`Traces`, `Consolidation`), the `Client` class, and ad-hoc runners created via `Object.new.extend(Runners::Traces)` all delegate to this singleton. This ensures traces written by one component (ErrorTracer, coldstart, tick retrieval) are visible to all others (dream cycle, predictions, cortex). Auto-selects `CacheStore` when `Legion::Cache.connected?` returns true, falls back to `Store` otherwise. `Memory.reset_store!` clears the singleton (used in test setup).
|
|
76
|
+
|
|
77
|
+
Both stores implement the same API:
|
|
78
|
+
- `store(trace)` / `get(trace_id)` / `delete(trace_id)`
|
|
79
|
+
- `retrieve_by_type(type, min_strength:, limit:)`
|
|
80
|
+
- `retrieve_by_domain(domain_tag, min_strength:, limit:)`
|
|
81
|
+
- `retrieve_associated(trace_id, min_strength:, limit:)`
|
|
82
|
+
- `record_coactivation(id_a, id_b)` - increments counter, links when >= COACTIVATION_THRESHOLD
|
|
83
|
+
- `all_traces(min_strength:)` / `count` / `firmware_traces`
|
|
84
|
+
- `walk_associations(start_id:, max_hops:, min_strength:)` - BFS traversal with cycle detection
|
|
85
|
+
|
|
86
|
+
`CacheStore` additionally provides:
|
|
87
|
+
- `flush` - writes local state to cache (only when dirty)
|
|
88
|
+
- `reload` - pulls latest state from cache (after another process wrote)
|
|
89
|
+
- TTL: 24 hours (`TRACES_KEY = 'legion:memory:traces'`, `ASSOC_KEY = 'legion:memory:associations'`)
|
|
90
|
+
|
|
91
|
+
**SQLite persistence**: `Store#save_to_local` and `Store#load_from_local` persist traces and associations to SQLite via `Legion::Data::Local`. Two local migrations create `memory_traces` and `memory_associations` tables. This enables trace survival across process restarts without requiring Memcached.
|
|
92
|
+
|
|
93
|
+
**Client class**: `Legion::Extensions::Memory::Client` holds `@default_store` injected via constructor, includes both `Runners::Traces` and `Runners::Consolidation`.
|
|
94
|
+
|
|
95
|
+
## Runners
|
|
96
|
+
|
|
97
|
+
### Traces
|
|
98
|
+
CRUD operations. All runners accept `store:` keyword to inject a custom store instance (used in specs). Default store delegates to `Memory.shared_store`.
|
|
99
|
+
|
|
100
|
+
Additional method:
|
|
101
|
+
- `retrieve_and_reinforce(limit: 10)` — retrieves top N traces by strength, increments `reinforcement_count` and `last_reinforced` on each (skips firmware traces); used by lex-cortex's `memory_retrieval` phase
|
|
102
|
+
|
|
103
|
+
### Consolidation
|
|
104
|
+
Lifecycle operations:
|
|
105
|
+
- `decay_cycle` - iterates all traces, applies power-law decay, prunes below PRUNE_THRESHOLD
|
|
106
|
+
- `reinforce` - boosts trace strength (firmware traces are skipped)
|
|
107
|
+
- `migrate_tier` - reassigns storage_tier based on last_reinforced timestamp
|
|
108
|
+
- `hebbian_link` - records co-activation, defers actual linking to Store
|
|
109
|
+
- `erase_by_type` / `erase_by_agent` - bulk delete (used by lex-privatecore)
|
|
110
|
+
|
|
111
|
+
## Integration Points
|
|
112
|
+
|
|
113
|
+
- **lex-coldstart**: `imprint_active:` flag passed to `reinforce` for the 3x multiplier
|
|
114
|
+
- **lex-emotion**: `emotional_intensity` on traces slows decay (E_WEIGHT)
|
|
115
|
+
- **lex-privatecore**: calls `erase_by_type` / `erase_by_agent` for cryptographic erasure
|
|
116
|
+
- **lex-tick**: `memory_retrieval` and `memory_consolidation` phases call into this extension
|
|
117
|
+
|
|
118
|
+
## Development Notes
|
|
119
|
+
|
|
120
|
+
- `new_trace` validates trace type and origin; raises `ArgumentError` on invalid values
|
|
121
|
+
- `firmware` traces: `base_decay_rate = 0.0`, `compute_decay` short-circuits to return `peak_strength`
|
|
122
|
+
- Retrieval score formula: `strength * recency_factor * emotional_weight * association_bonus`
|
|
123
|
+
- `retrieve_ranked` re-scores by retrieval score, not stored strength
|
|
124
|
+
- The gemspec uses `git ls-files` (differs from other LEX gems using `Dir.glob`) — both approaches are fine
|
|
125
|
+
- Actor namespace is `module Actor` (singular), not `Actors`
|
|
126
|
+
- `content_embedding:` is a field on trace hashes (reserved for future vector search)
|
|
127
|
+
- `CacheStore` stores all traces in a single Memcached key (`legion:memory:traces`) — large trace sets require sufficient Memcached item size limits
|
|
128
|
+
- `CacheStore#flush` only writes when `@dirty` is true; safe to call frequently
|
|
129
|
+
- `retrieve_and_reinforce` increments `reinforcement_count` in-place on the trace hash and re-stores it — this makes retrieval itself a reinforcing action
|
data/Gemfile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
gemspec
|
|
5
|
+
|
|
6
|
+
group :test do
|
|
7
|
+
gem 'rake'
|
|
8
|
+
gem 'rspec'
|
|
9
|
+
gem 'rspec_junit_formatter'
|
|
10
|
+
gem 'rubocop', require: false
|
|
11
|
+
gem 'rubocop-rspec', require: false
|
|
12
|
+
gem 'sequel'
|
|
13
|
+
gem 'simplecov'
|
|
14
|
+
gem 'sqlite3'
|
|
15
|
+
end
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
lex-memory (0.1.2)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
addressable (2.8.9)
|
|
10
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
11
|
+
ast (2.4.3)
|
|
12
|
+
bigdecimal (4.0.1)
|
|
13
|
+
diff-lcs (1.6.2)
|
|
14
|
+
docile (1.4.1)
|
|
15
|
+
json (2.19.1)
|
|
16
|
+
json-schema (6.2.0)
|
|
17
|
+
addressable (~> 2.8)
|
|
18
|
+
bigdecimal (>= 3.1, < 5)
|
|
19
|
+
language_server-protocol (3.17.0.5)
|
|
20
|
+
lint_roller (1.1.0)
|
|
21
|
+
mcp (0.8.0)
|
|
22
|
+
json-schema (>= 4.1)
|
|
23
|
+
mini_portile2 (2.8.9)
|
|
24
|
+
parallel (1.27.0)
|
|
25
|
+
parser (3.3.10.2)
|
|
26
|
+
ast (~> 2.4.1)
|
|
27
|
+
racc
|
|
28
|
+
prism (1.9.0)
|
|
29
|
+
public_suffix (7.0.5)
|
|
30
|
+
racc (1.8.1)
|
|
31
|
+
rainbow (3.1.1)
|
|
32
|
+
rake (13.3.1)
|
|
33
|
+
regexp_parser (2.11.3)
|
|
34
|
+
rspec (3.13.2)
|
|
35
|
+
rspec-core (~> 3.13.0)
|
|
36
|
+
rspec-expectations (~> 3.13.0)
|
|
37
|
+
rspec-mocks (~> 3.13.0)
|
|
38
|
+
rspec-core (3.13.6)
|
|
39
|
+
rspec-support (~> 3.13.0)
|
|
40
|
+
rspec-expectations (3.13.5)
|
|
41
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
42
|
+
rspec-support (~> 3.13.0)
|
|
43
|
+
rspec-mocks (3.13.8)
|
|
44
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
45
|
+
rspec-support (~> 3.13.0)
|
|
46
|
+
rspec-support (3.13.7)
|
|
47
|
+
rspec_junit_formatter (0.6.0)
|
|
48
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
|
49
|
+
rubocop (1.85.1)
|
|
50
|
+
json (~> 2.3)
|
|
51
|
+
language_server-protocol (~> 3.17.0.2)
|
|
52
|
+
lint_roller (~> 1.1.0)
|
|
53
|
+
mcp (~> 0.6)
|
|
54
|
+
parallel (~> 1.10)
|
|
55
|
+
parser (>= 3.3.0.2)
|
|
56
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
57
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
58
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
59
|
+
ruby-progressbar (~> 1.7)
|
|
60
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
61
|
+
rubocop-ast (1.49.1)
|
|
62
|
+
parser (>= 3.3.7.2)
|
|
63
|
+
prism (~> 1.7)
|
|
64
|
+
rubocop-rspec (3.9.0)
|
|
65
|
+
lint_roller (~> 1.1)
|
|
66
|
+
rubocop (~> 1.81)
|
|
67
|
+
ruby-progressbar (1.13.0)
|
|
68
|
+
sequel (5.102.0)
|
|
69
|
+
bigdecimal
|
|
70
|
+
simplecov (0.22.0)
|
|
71
|
+
docile (~> 1.1)
|
|
72
|
+
simplecov-html (~> 0.11)
|
|
73
|
+
simplecov_json_formatter (~> 0.1)
|
|
74
|
+
simplecov-html (0.13.2)
|
|
75
|
+
simplecov_json_formatter (0.1.4)
|
|
76
|
+
sqlite3 (2.9.2)
|
|
77
|
+
mini_portile2 (~> 2.8.0)
|
|
78
|
+
sqlite3 (2.9.2-arm64-darwin)
|
|
79
|
+
unicode-display_width (3.2.0)
|
|
80
|
+
unicode-emoji (~> 4.1)
|
|
81
|
+
unicode-emoji (4.2.0)
|
|
82
|
+
|
|
83
|
+
PLATFORMS
|
|
84
|
+
arm64-darwin-25
|
|
85
|
+
ruby
|
|
86
|
+
|
|
87
|
+
DEPENDENCIES
|
|
88
|
+
lex-memory!
|
|
89
|
+
rake
|
|
90
|
+
rspec
|
|
91
|
+
rspec_junit_formatter
|
|
92
|
+
rubocop
|
|
93
|
+
rubocop-rspec
|
|
94
|
+
sequel
|
|
95
|
+
simplecov
|
|
96
|
+
sqlite3
|
|
97
|
+
|
|
98
|
+
BUNDLED WITH
|
|
99
|
+
2.6.9
|
data/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# lex-memory
|
|
2
|
+
|
|
3
|
+
Memory trace system for brain-modeled agentic AI. Implements trace storage, power-law decay, reinforcement, Hebbian association, and tiered retrieval.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`lex-memory` models the agent's long-term memory as a collection of typed traces. Traces decay over time according to a power-law formula, are strengthened by reinforcement, and form associative links through co-activation (Hebbian learning). Storage tiers (hot/warm/cold) reflect recency of access.
|
|
8
|
+
|
|
9
|
+
## Trace Types
|
|
10
|
+
|
|
11
|
+
| Type | Starting Strength | Base Decay Rate | Notes |
|
|
12
|
+
|------|------------------|----------------|-------|
|
|
13
|
+
| `firmware` | 1.0 | 0.0 | Never decays — hardcoded values |
|
|
14
|
+
| `identity` | 1.0 | 0.001 | Self-model |
|
|
15
|
+
| `procedural` | 0.4 | 0.005 | How-to knowledge |
|
|
16
|
+
| `trust` | 0.3 | 0.008 | Agent trust records |
|
|
17
|
+
| `semantic` | 0.5 | 0.010 | Conceptual knowledge |
|
|
18
|
+
| `episodic` | 0.6 | 0.020 | Event memories |
|
|
19
|
+
| `sensory` | 0.4 | 0.100 | Transient perceptual data |
|
|
20
|
+
|
|
21
|
+
## Storage Tiers
|
|
22
|
+
|
|
23
|
+
| Tier | Condition |
|
|
24
|
+
|------|-----------|
|
|
25
|
+
| `hot` | Last accessed within 24 hours |
|
|
26
|
+
| `warm` | Last accessed within 90 days |
|
|
27
|
+
| `cold` | Older than 90 days |
|
|
28
|
+
| `erased` | Strength <= 0.01 (pruned) |
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Add to your Gemfile:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
gem 'lex-memory'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### Storing Traces
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
require 'legion/extensions/memory'
|
|
44
|
+
|
|
45
|
+
# Store a new trace
|
|
46
|
+
result = Legion::Extensions::Memory::Runners::Traces.store_trace(
|
|
47
|
+
type: :episodic,
|
|
48
|
+
content_payload: { event: "first conversation", summary: "..." },
|
|
49
|
+
emotional_intensity: 0.7,
|
|
50
|
+
domain_tags: [:conversation]
|
|
51
|
+
)
|
|
52
|
+
# => { trace_id: "uuid", trace_type: :episodic, strength: 0.6 }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Retrieving Traces
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
# By type
|
|
59
|
+
Legion::Extensions::Memory::Runners::Traces.retrieve_by_type(type: :semantic, min_strength: 0.3)
|
|
60
|
+
|
|
61
|
+
# By domain tag
|
|
62
|
+
Legion::Extensions::Memory::Runners::Traces.retrieve_by_domain(domain_tag: :conversation)
|
|
63
|
+
|
|
64
|
+
# Associated traces (Hebbian links)
|
|
65
|
+
Legion::Extensions::Memory::Runners::Traces.retrieve_associated(trace_id: "uuid")
|
|
66
|
+
|
|
67
|
+
# Ranked retrieval (composite score: strength * recency * emotion * association)
|
|
68
|
+
Legion::Extensions::Memory::Runners::Traces.retrieve_ranked(trace_ids: ["uuid1", "uuid2"])
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Active Retrieval (used by lex-cortex)
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
# Retrieve top N traces by strength and mark them as reinforced
|
|
75
|
+
Legion::Extensions::Memory::Runners::Traces.retrieve_and_reinforce(limit: 10)
|
|
76
|
+
# => { count: 10, traces: [...] }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Memory Consolidation
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
# Reinforce a trace (strengthens it; 3x multiplier during imprint window)
|
|
83
|
+
Legion::Extensions::Memory::Runners::Consolidation.reinforce(
|
|
84
|
+
trace_id: "uuid",
|
|
85
|
+
imprint_active: false
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Run decay cycle (called each tick)
|
|
89
|
+
Legion::Extensions::Memory::Runners::Consolidation.decay_cycle(tick_count: 1)
|
|
90
|
+
|
|
91
|
+
# Migrate traces to appropriate storage tiers
|
|
92
|
+
Legion::Extensions::Memory::Runners::Consolidation.migrate_tier
|
|
93
|
+
|
|
94
|
+
# Form Hebbian link between co-activated traces
|
|
95
|
+
Legion::Extensions::Memory::Runners::Consolidation.hebbian_link(
|
|
96
|
+
trace_id_a: "uuid1",
|
|
97
|
+
trace_id_b: "uuid2"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Selective erasure (for lex-privatecore integration)
|
|
101
|
+
Legion::Extensions::Memory::Runners::Consolidation.erase_by_type(type: :sensory)
|
|
102
|
+
Legion::Extensions::Memory::Runners::Consolidation.erase_by_agent(partition_id: "agent-123")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Decay Formula
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
new_strength = peak_strength * (ticks_since_access + 1)^(-base_decay_rate / (1 + emotional_intensity * 0.3))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
High emotional intensity slows decay. Firmware traces have `base_decay_rate = 0.0` and never decay.
|
|
112
|
+
|
|
113
|
+
## Reinforcement Formula
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
new_strength = min(1.0, current_strength + 0.10 * imprint_multiplier)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
During the imprint window (first 7 days), the multiplier is 3.0.
|
|
120
|
+
|
|
121
|
+
## Hebbian Association
|
|
122
|
+
|
|
123
|
+
Traces that co-activate 3 or more times form a permanent associative link. Each trace stores up to 20 links. Linked traces receive a 15% bonus in retrieval scoring.
|
|
124
|
+
|
|
125
|
+
## ErrorTracer
|
|
126
|
+
|
|
127
|
+
`Helpers::ErrorTracer` wraps `Legion::Logging.error` and `fatal` to auto-create episodic traces when errors occur. A 60-second debounce prevents trace flooding from repeated errors.
|
|
128
|
+
|
|
129
|
+
## Standalone Client
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
client = Legion::Extensions::Memory::Client.new
|
|
133
|
+
client.store_trace(type: :semantic, content_payload: { fact: "..." })
|
|
134
|
+
client.retrieve_by_type(type: :semantic)
|
|
135
|
+
client.decay_cycle(tick_count: 1)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## SQLite Persistence
|
|
139
|
+
|
|
140
|
+
When `legion-data` Local is available, traces can be persisted to SQLite:
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
store = Legion::Extensions::Memory.shared_store
|
|
144
|
+
store.save_to_local # persist to SQLite
|
|
145
|
+
store.load_from_local # restore from SQLite
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Development
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
bundle install
|
|
152
|
+
bundle exec rspec
|
|
153
|
+
bundle exec rubocop
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT
|
data/lex-memory.gemspec
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/memory/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-memory'
|
|
7
|
+
spec.version = Legion::Extensions::Memory::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Memory'
|
|
12
|
+
spec.description = 'Memory trace system for brain-modeled agentic AI — consolidation, reinforcement, and decay'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-memory'
|
|
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-memory'
|
|
19
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-memory'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-memory'
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-memory/issues'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
23
|
+
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
26
|
+
end
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/actors/every'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Memory
|
|
8
|
+
module Actor
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class
|
|
11
|
+
Legion::Extensions::Memory::Runners::Consolidation
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def runner_function
|
|
15
|
+
'decay_cycle'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def time
|
|
19
|
+
60
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run_now?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def use_runner?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def check_subtask?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def generate_task?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/actors/every'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Memory
|
|
8
|
+
module Actor
|
|
9
|
+
class TierMigration < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class
|
|
11
|
+
Legion::Extensions::Memory::Runners::Consolidation
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def runner_function
|
|
15
|
+
'migrate_tier'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def time
|
|
19
|
+
300
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run_now?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def use_runner?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def check_subtask?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def generate_task?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/memory/helpers/trace'
|
|
4
|
+
require 'legion/extensions/memory/helpers/decay'
|
|
5
|
+
require 'legion/extensions/memory/helpers/store'
|
|
6
|
+
require 'legion/extensions/memory/runners/traces'
|
|
7
|
+
require 'legion/extensions/memory/runners/consolidation'
|
|
8
|
+
|
|
9
|
+
module Legion
|
|
10
|
+
module Extensions
|
|
11
|
+
module Memory
|
|
12
|
+
class Client
|
|
13
|
+
include Legion::Extensions::Memory::Runners::Traces
|
|
14
|
+
include Legion::Extensions::Memory::Runners::Consolidation
|
|
15
|
+
|
|
16
|
+
attr_reader :store
|
|
17
|
+
|
|
18
|
+
def initialize(store: nil, **)
|
|
19
|
+
@default_store = store || Legion::Extensions::Memory.shared_store
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
attr_reader :default_store
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|