Class: Legion::Extensions::Agentic::Memory::Trace::Helpers::CacheStore

Inherits:
Object
  • Object
show all
Defined in:
lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb

Overview

Cache-backed store using Legion::Cache (Memcached/Redis). Each trace is stored as an individual cache key for scalability. An index key tracks all known trace IDs. Keeps a local in-memory copy for fast reads; syncs to cache on flush.

Constant Summary collapse

TRACE_PREFIX =
'legion:memory:trace:'
INDEX_KEY =
'legion:memory:trace_index'
ASSOC_KEY =
'legion:memory:associations'
TTL =

24 hours

86_400
FLUSH_BATCH =

traces per flush batch

500

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCacheStore

Returns a new instance of CacheStore.



22
23
24
25
26
27
28
29
30
31
32
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 22

def initialize
  log.info('[memory] CacheStore initialized (memcached-backed, per-key)')
  @mutex        = Mutex.new
  @traces       = {}
  @associations = {}
  @dirty_ids    = Set.new
  @deleted_ids  = Set.new
  @assoc_dirty  = false
  load_index
  log.info("[memory] CacheStore loaded #{@traces.size} traces from cache")
end

Instance Attribute Details

#associationsObject (readonly)

Returns the value of attribute associations.



20
21
22
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 20

def associations
  @associations
end

#tracesObject (readonly)

Returns the value of attribute traces.



20
21
22
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 20

def traces
  @traces
end

Instance Method Details

#all_traces(min_strength: 0.0) ⇒ Object



97
98
99
100
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 97

def all_traces(min_strength: 0.0)
  snapshot = @mutex.synchronize { @traces.values }
  snapshot.select { |t| t[:strength] >= min_strength }
end

#countObject



102
103
104
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 102

def count
  @traces.size
end

#delete(trace_id) ⇒ Object



46
47
48
49
50
51
52
53
54
55
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 46

def delete(trace_id)
  @mutex.synchronize do
    @traces.delete(trace_id)
    @associations.delete(trace_id)
    @associations.each_value { |links| links.delete(trace_id) }
    @dirty_ids.delete(trace_id)
    @deleted_ids << trace_id
    @assoc_dirty = true
  end
end

#firmware_tracesObject



108
109
110
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 108

def firmware_traces
  retrieve_by_type(:firmware)
end

#flushObject

Write dirty traces to cache as individual keys



143
144
145
146
147
148
149
150
151
152
153
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 143

def flush
  @mutex.synchronize do
    flush_deleted
    flush_traces
    flush_associations
    flush_index
    log.debug("[memory] CacheStore flushed #{@dirty_ids.size} dirty traces (#{@traces.size} total)")
    @dirty_ids.clear
    @deleted_ids.clear
  end
end

#get(trace_id) ⇒ Object



42
43
44
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 42

def get(trace_id)
  @traces[trace_id]
end

#record_coactivation(trace_id_a, trace_id_b) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 82

def record_coactivation(trace_id_a, trace_id_b)
  return if trace_id_a == trace_id_b

  @mutex.synchronize do
    @associations[trace_id_a] ||= {}
    @associations[trace_id_b] ||= {}
    @associations[trace_id_a][trace_id_b] = (@associations[trace_id_a][trace_id_b] || 0) + 1
    @associations[trace_id_b][trace_id_a] = (@associations[trace_id_b][trace_id_a] || 0) + 1

    threshold = Helpers::Trace::COACTIVATION_THRESHOLD
    link_traces(trace_id_a, trace_id_b) if @associations[trace_id_a][trace_id_b] >= threshold
    @assoc_dirty = true
  end
end

#reloadObject

Pull latest state from cache



156
157
158
159
160
161
162
163
164
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 156

def reload
  @traces.clear
  @associations = {}
  @dirty_ids.clear
  @deleted_ids.clear
  @assoc_dirty = false
  load_index
  log.debug("[memory] CacheStore reloaded #{@traces.size} traces from cache")
end

#retrieve_associated(trace_id, min_strength: 0.0, limit: 20) ⇒ Object



71
72
73
74
75
76
77
78
79
80
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 71

def retrieve_associated(trace_id, min_strength: 0.0, limit: 20)
  trace = @traces[trace_id]
  return [] unless trace

  trace[:associated_traces]
    .filter_map { |id| @traces[id] }
    .select { |t| t[:strength] >= min_strength }
    .sort_by { |t| -t[:strength] }
    .first(limit)
end

#retrieve_by_domain(domain_tag, min_strength: 0.0, limit: 100) ⇒ Object



64
65
66
67
68
69
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 64

def retrieve_by_domain(domain_tag, min_strength: 0.0, limit: 100)
  snapshot = @mutex.synchronize { @traces.values }
  snapshot.select { |t| t[:domain_tags].include?(domain_tag) && t[:strength] >= min_strength }
          .sort_by { |t| -t[:strength] }
          .first(limit)
end

#retrieve_by_type(type, min_strength: 0.0, limit: 100) ⇒ Object



57
58
59
60
61
62
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 57

def retrieve_by_type(type, min_strength: 0.0, limit: 100)
  snapshot = @mutex.synchronize { @traces.values }
  snapshot.select { |t| t[:trace_type] == type && t[:strength] >= min_strength }
          .sort_by { |t| -t[:strength] }
          .first(limit)
end

#store(trace) ⇒ Object



34
35
36
37
38
39
40
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 34

def store(trace)
  @mutex.synchronize do
    @traces[trace[:trace_id]] = trace
    @dirty_ids << trace[:trace_id]
  end
  trace[:trace_id]
end

#synchronizeObject



106
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 106

def synchronize(&) = @mutex.synchronize(&)

#walk_associations(start_id:, max_hops: 12, min_strength: 0.1) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb', line 112

def walk_associations(start_id:, max_hops: 12, min_strength: 0.1)
  snapshot = @mutex.synchronize { @traces.dup }
  return [] unless snapshot.key?(start_id)

  results = []
  visited = Set.new([start_id])
  queue   = [[start_id, 0, [start_id]]]

  until queue.empty?
    current_id, depth, path = queue.shift
    current = snapshot[current_id]
    next unless current

    current[:associated_traces].each do |neighbor_id|
      next if visited.include?(neighbor_id)

      neighbor = snapshot[neighbor_id]
      next unless neighbor
      next unless neighbor[:strength] >= min_strength

      visited << neighbor_id
      neighbor_path = path + [neighbor_id]
      results << { trace_id: neighbor_id, depth: depth + 1, path: neighbor_path }
      queue << [neighbor_id, depth + 1, neighbor_path] if depth + 1 < max_hops
    end
  end

  results
end