Module: Legion::Tools::EmbeddingCache

Defined in:
lib/legion/tools/embedding_cache.rb

Constant Summary collapse

MIGRATION_PATH =
File.expand_path('embedding_cache/migrations', __dir__)
L0_MAX_ENTRIES =
1000
CACHE_TTL =

24 hours

86_400

Class Method Summary collapse

Class Method Details

.available?Boolean

Returns:

  • (Boolean)


32
33
34
# File 'lib/legion/tools/embedding_cache.rb', line 32

def available?
  true # L0 is always available
end

.bulk_lookup(content_hashes:, model:) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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/tools/embedding_cache.rb', line 96

def bulk_lookup(content_hashes:, model:)
  return {} if content_hashes.empty?

  result = {}
  remaining = content_hashes.dup

  # L0 / Tier 1 / Tier 2
  remaining.dup.each do |h|
    key = "embed:#{h}:#{model}"

    vec = memory_get(key)
    if vec
      result[h] = vec
      remaining.delete(h)
      next
    end

    vec = cache_local_get(key)
    if vec
      result[h] = vec
      memory_set(key, vec)
      remaining.delete(h)
      next
    end

    vec = cache_global_get(key)
    next unless vec

    result[h] = vec
    memory_set(key, vec)
    cache_local_set(key, vec)
    remaining.delete(h)
  end

  # Tier 3
  bulk_data_lookup(remaining, model, result, :local) if remaining.any?

  # Tier 4
  bulk_data_lookup(remaining, model, result, :global) if remaining.any?

  result
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_bulk_lookup)
  result || {}
end

.bulk_store(entries) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/legion/tools/embedding_cache.rb', line 156

def bulk_store(entries)
  return if entries.nil? || entries.empty?

  cache_hash = {}
  entries.each do |entry|
    key = "embed:#{entry[:content_hash]}:#{entry[:model]}"
    memory_set(key, entry[:vector])
    cache_hash[key] = entry[:vector]
  end

  bulk_cache_store(cache_hash)
  bulk_data_local_store(entries) if data_local_available?
  bulk_data_global_store(entries) if data_global_available?
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_bulk_store)
end

.clearObject



173
174
175
176
177
# File 'lib/legion/tools/embedding_cache.rb', line 173

def clear
  clear_memory
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_clear)
end

.clear_memoryObject



179
180
181
182
183
# File 'lib/legion/tools/embedding_cache.rb', line 179

def clear_memory
  @memory_mutex.synchronize { @memory_cache.clear }
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_clear_memory)
end

.content_hash(text) ⇒ Object



36
37
38
# File 'lib/legion/tools/embedding_cache.rb', line 36

def content_hash(text)
  Digest::MD5.hexdigest(text.to_s)
end

.handle_exception(err, **opts) ⇒ Object



22
23
24
# File 'lib/legion/tools/embedding_cache.rb', line 22

def handle_exception(err, **opts)
  log&.warn("[Tools::EmbeddingCache] #{opts[:operation]}: #{err.message}")
end

.logObject



18
19
20
# File 'lib/legion/tools/embedding_cache.rb', line 18

def log
  Legion::Logging.respond_to?(:logger) ? Legion::Logging.logger : nil
end

.lookup(content_hash:, model:) ⇒ Object

— 5-tier read cascade —



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/legion/tools/embedding_cache.rb', line 42

def lookup(content_hash:, model:)
  key = "embed:#{content_hash}:#{model}"

  # L0
  vec = memory_get(key)
  return vec if vec

  # Tier 1
  vec = cache_local_get(key)
  if vec
    memory_set(key, vec)
    return vec
  end

  # Tier 2
  vec = cache_global_get(key)
  if vec
    memory_set(key, vec)
    cache_local_set(key, vec)
    return vec
  end

  # Tier 3
  row = data_local_get(content_hash, model)
  if row
    vec = parse_vector(row[:vector])
    if vec
      memory_set(key, vec)
      cache_local_set(key, vec)
      cache_global_set(key, vec)
      return vec
    end
  end

  # Tier 4
  row = data_global_get(content_hash, model)
  if row
    vec = parse_vector(row[:vector])
    if vec
      memory_set(key, vec)
      cache_local_set(key, vec)
      cache_global_set(key, vec)
      data_local_store(content_hash: content_hash, model: model,
                       tool_name: row[:tool_name], vector: vec)
      return vec
    end
  end

  nil
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_lookup)
  nil
end

.purge_persistent!Object



185
186
187
188
189
190
191
# File 'lib/legion/tools/embedding_cache.rb', line 185

def purge_persistent!
  clear_memory
  data_local_connection[:tool_embedding_cache].delete if data_local_available?
  data_global_connection[:tool_embedding_cache].delete if data_global_available?
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_purge_persistent)
end

.setupObject



26
27
28
29
30
# File 'lib/legion/tools/embedding_cache.rb', line 26

def setup
  Legion::Data::Local.register_migrations(name: 'embedding_cache', path: MIGRATION_PATH)
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_setup)
end

.statsObject



193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/legion/tools/embedding_cache.rb', line 193

def stats
  {
    memory:       @memory_mutex.synchronize { @memory_cache.size },
    cache_local:  cache_local_available?,
    cache_global: cache_global_available?,
    data_local:   data_local_available? ? data_local_connection[:tool_embedding_cache].count : 0,
    data_global:  data_global_available? ? data_global_connection[:tool_embedding_cache].count : 0
  }
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_stats)
  {}
end

.store(content_hash:, model:, tool_name:, vector:) ⇒ Object

Write to all 5 tiers



143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/legion/tools/embedding_cache.rb', line 143

def store(content_hash:, model:, tool_name:, vector:)
  key = "embed:#{content_hash}:#{model}"
  memory_set(key, vector)
  cache_local_set(key, vector)
  cache_global_set(key, vector)
  data_local_store(content_hash: content_hash, model: model,
                   tool_name: tool_name, vector: vector)
  data_global_store(content_hash: content_hash, model: model,
                    tool_name: tool_name, vector: vector)
rescue StandardError => e
  handle_exception(e, level: :warn, handled: true, operation: :embedding_cache_store)
end