Class: Engram::Adapters::InMemoryStore

Inherits:
Object
  • Object
show all
Includes:
Ports::MemoryStore
Defined in:
lib/engram/adapters/in_memory_store.rb

Overview

In-process MemoryStore. Used as the zero-config default and in unit tests. Search is exact cosine similarity over the stored vectors.

Instance Method Summary collapse

Constructor Details

#initializeInMemoryStore

Returns a new instance of InMemoryStore.



10
11
12
13
# File 'lib/engram/adapters/in_memory_store.rb', line 10

def initialize
  @records = {}
  @sequence = 0
end

Instance Method Details

#add(record) ⇒ Object



15
16
17
18
19
20
21
# File 'lib/engram/adapters/in_memory_store.rb', line 15

def add(record)
  validate_scope!(record.scope)

  record.id ||= (@sequence += 1)
  @records[record.id] = record
  record
end

#all(scope:, limit: nil, offset: 0, after_id: nil) ⇒ Object



39
40
41
42
43
44
45
# File 'lib/engram/adapters/in_memory_store.rb', line 39

def all(scope:, limit: nil, offset: 0, after_id: nil)
  records = @records.values.select { |r| r.scope == scope }.sort_by { |record| record.id }
  records = records.drop_while { |record| !after_id.nil? && record.id && record.id <= after_id }
  records = records.drop(offset) if offset > 0
  records = records.take(limit) if limit
  records
end

#clearObject



71
72
73
74
# File 'lib/engram/adapters/in_memory_store.rb', line 71

def clear
  @records.clear
  @sequence = 0
end

#delete(scope:, id:) ⇒ Object



56
57
58
59
60
61
# File 'lib/engram/adapters/in_memory_store.rb', line 56

def delete(scope:, id:)
  return 0 unless @records[id]&.scope == scope

  @records.delete(id)
  1
end

#search(embedding:, scope:, limit:, kinds: nil, embedding_metadata: nil) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/engram/adapters/in_memory_store.rb', line 23

def search(embedding:, scope:, limit:, kinds: nil, embedding_metadata: nil)
  Engram::EmbeddingMetadata.validate_query!(embedding, )
  allowed_kinds = normalize_kinds(kinds)

  results = @records
    .values
    .select { |r| searchable?(r, scope, allowed_kinds) }
    .map { |r| [r, Engram::Math.cosine_similarity(embedding, r.embedding)] }
    .sort_by { |(_, score)| -score }
    .first(limit)
    .map { |(record, _)| record }

  results.each { |record| Engram::EmbeddingMetadata.validate_record!(record, embedding, ) }
  results
end

#touch(scope:, id:, at: Time.now) ⇒ Object



63
64
65
66
67
68
69
# File 'lib/engram/adapters/in_memory_store.rb', line 63

def touch(scope:, id:, at: Time.now)
  record = @records[id]
  return 0 unless record&.scope == scope

  record.last_accessed_at = at
  1
end

#update(scope:, id:, record:) ⇒ Object

Raises:



47
48
49
50
51
52
53
54
# File 'lib/engram/adapters/in_memory_store.rb', line 47

def update(scope:, id:, record:)
  existing = @records[id]
  raise Engram::Error, "no memory with id #{id.inspect} in scope #{scope.inspect}" unless existing&.scope == scope
  raise Engram::Error, "cannot move memory across scopes" unless record.scope == scope

  record.id = id
  @records[id] = record
end