lex-agentic-memory 0.1.31 → 0.1.32

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9ec98fbb5ad987564ae50bc87572aa2d23eb3c0011dfa2c5c2a000926286eed
4
- data.tar.gz: 318bae8b3abd84f2ec9e1b8101a0aed4e4f8656822356d1a9ef65557984234e9
3
+ metadata.gz: 07b491ea4b919f623905cf3fe0c651c6a462e46bfe096e06e4221e9eb9e6b801
4
+ data.tar.gz: b92fc5808525806e2dca50c86ea4382583adcbe5b6b9e84d0dd8e1c4add0f14b
5
5
  SHA512:
6
- metadata.gz: be83bf5dd3d3d283f00ab26f16f2159f01d12bb0e2f561d956ff6039902baacb5cbce96d61fb2becfd34b83f282daa0f63b7581770d204728629d088dda6cfcf
7
- data.tar.gz: 36af01ed5dc06602f8233667623f53f067f11a19ef01b350f1bd8d6aaf08af4ff44dbde0987a10add8b015dd14115a74005a04e237b527e83076979a42743bb5
6
+ metadata.gz: ed8eb6cff096080b1435aab249896d4c473d1aab7b11ebec1f687081aa9494bc8671c029d3d7e2e80b4144b705b477e6d58fc796c412d2a6bf75a852fa02f27e
7
+ data.tar.gz: 834d02319dbf0c63722d8bc8e59e5f63d6c18c42c740a71a47ac5f1c6df0c1ad9b983e73b246f751bd24a784aa4e43c5d5f508ed516dbd7f94321fea220f2cf3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.32] - 2026-05-07
4
+ ### Fixed
5
+ - Trace association retrieval now snapshots associated traces under the store mutex before filtering.
6
+ - Local trace restore preserves symbol keys for JSON fields that are consumed as symbol-keyed hashes.
7
+
3
8
  ## [0.1.31] - 2026-04-27
4
9
  ### Added
5
10
  - Add a heuristic pre-compaction memory save path and synchronous pre-compaction event listeners for `chat.pre_compact`, `context.pre_compact`, and `conversation.pre_compact`.
@@ -70,11 +70,14 @@ module Legion
70
70
  end
71
71
 
72
72
  def retrieve_associated(trace_id, min_strength: 0.0, limit: 20)
73
- trace = @traces[trace_id]
74
- return [] unless trace
73
+ associated = @mutex.synchronize do
74
+ trace = @traces[trace_id]
75
+ next [] unless trace
75
76
 
76
- trace[:associated_traces]
77
- .filter_map { |id| @traces[id] }
77
+ trace[:associated_traces].filter_map { |id| @traces[id]&.dup }
78
+ end
79
+
80
+ associated
78
81
  .select { |t| t[:strength] >= min_strength }
79
82
  .sort_by { |t| -t[:strength] }
80
83
  .first(limit)
@@ -354,13 +357,27 @@ module Legion
354
357
  raw
355
358
  end
356
359
 
357
- def parse_db_json(raw, field, **_opts, &default)
358
- Legion::JSON.load(raw.to_s)
360
+ def parse_db_json(raw, field, symbolize: false, &default)
361
+ parsed = Legion::JSON.load(raw.to_s)
362
+ symbolize ? symbolize_keys(parsed) : parsed
359
363
  rescue StandardError => e
360
364
  log.error "[trace_persistence] deserialize_trace_from_db #{field}: #{e.message}"
361
365
  default&.call
362
366
  end
363
367
 
368
+ def symbolize_keys(value)
369
+ case value
370
+ when Hash
371
+ value.each_with_object({}) do |(key, nested), memo|
372
+ memo[key.to_sym] = symbolize_keys(nested)
373
+ end
374
+ when Array
375
+ value.map { |nested| symbolize_keys(nested) }
376
+ else
377
+ value
378
+ end
379
+ end
380
+
364
381
  def link_traces(id_a, id_b)
365
382
  trace_a = @traces[id_a]
366
383
  trace_b = @traces[id_b]
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Agentic
6
6
  module Memory
7
- VERSION = '0.1.31'
7
+ VERSION = '0.1.32'
8
8
  end
9
9
  end
10
10
  end
@@ -95,6 +95,18 @@ RSpec.describe Legion::Extensions::Agentic::Memory::Trace::Helpers::Store do
95
95
  associated = store.retrieve_associated(semantic_trace[:trace_id])
96
96
  expect(associated.size).to eq(0)
97
97
  end
98
+
99
+ it 'snapshots associated traces while holding the store mutex' do
100
+ store.store(semantic_trace)
101
+ store.store(episodic_trace)
102
+ semantic_trace[:associated_traces] << episodic_trace[:trace_id]
103
+
104
+ mutex = store.instance_variable_get(:@mutex)
105
+ expect(mutex).to receive(:synchronize).and_call_original
106
+
107
+ associated = store.retrieve_associated(semantic_trace[:trace_id])
108
+ expect(associated.map { |trace| trace[:trace_id] }).to eq([episodic_trace[:trace_id]])
109
+ end
98
110
  end
99
111
 
100
112
  describe '#all_traces' do
@@ -249,6 +249,23 @@ RSpec.describe 'lex-memory local SQLite persistence' do
249
249
  expect(fresh.get(episodic_trace[:trace_id])).not_to be_nil
250
250
  end
251
251
 
252
+ it 'honors symbolize parsing for JSON hash fields' do
253
+ trace = trace_helper.new_trace(
254
+ type: :semantic,
255
+ content_payload: { fact: 'symbolized' },
256
+ domain_tags: ['json'],
257
+ emotional_valence: { joy: 0.8 }
258
+ )
259
+ store.store(trace)
260
+ store.save_to_local
261
+
262
+ fresh = Legion::Extensions::Agentic::Memory::Trace::Helpers::Store.new
263
+ restored = fresh.get(trace[:trace_id])
264
+
265
+ expect(restored[:emotional_valence]).to include(joy: 0.8)
266
+ expect(restored[:emotional_valence]).not_to have_key('joy')
267
+ end
268
+
252
269
  it 'restores associations from the database into a fresh store' do
253
270
  store.store(semantic_trace)
254
271
  store.store(episodic_trace)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-agentic-memory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.31
4
+ version: 0.1.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity