Class: RCrewAI::Memory::SqliteStore

Inherits:
Object
  • Object
show all
Defined in:
lib/rcrewai/memory/sqlite_store.rb

Overview

Persistent vector store backed by SQLite. Vectors are packed as little-endian floats; metadata is JSON. Cosine is computed in Ruby over rows filtered by scope — adequate for the thousands-of-memories scale an agent produces; an ANN index is a later optimization.

The sqlite3 gem is required lazily so the rest of the library (and the in-memory store) works even if it isn't installed.

Constant Summary collapse

DEFAULT_PATH =
File.join(Dir.home, '.rcrewai', 'memory.db')

Instance Method Summary collapse

Constructor Details

#initialize(path: DEFAULT_PATH, max_candidates: 1000) ⇒ SqliteStore

max_candidates bounds how many (most-recent) rows a search cosines, so recall cost stays constant as total memory grows. nil = consider all.



20
21
22
23
24
25
26
27
# File 'lib/rcrewai/memory/sqlite_store.rb', line 20

def initialize(path: DEFAULT_PATH, max_candidates: 1000)
  require 'sqlite3'
  ensure_parent_dir(path) unless path == ':memory:'
  @db = SQLite3::Database.new(path)
  @db.results_as_hash = true
  @max_candidates = max_candidates
  create_schema
end

Instance Method Details

#add(id:, text:, vector:, scope:, metadata: {}) ⇒ Object



29
30
31
32
33
34
35
36
# File 'lib/rcrewai/memory/sqlite_store.rb', line 29

def add(id:, text:, vector:, scope:, metadata: {})
  @db.execute(
    'INSERT INTO memories (id, scope, text, vector, metadata) VALUES (?, ?, ?, ?, ?) ' \
    'ON CONFLICT(id) DO UPDATE SET scope=excluded.scope, text=excluded.text, ' \
    'vector=excluded.vector, metadata=excluded.metadata',
    [id, scope, text, pack_vector(vector), JSON.generate( || {})]
  )
end

#all(scope:) ⇒ Object



38
39
40
# File 'lib/rcrewai/memory/sqlite_store.rb', line 38

def all(scope:)
  @db.execute('SELECT * FROM memories WHERE scope = ?', [scope]).map { |row| to_record(row) }
end

#delete(scope:) ⇒ Object



51
52
53
# File 'lib/rcrewai/memory/sqlite_store.rb', line 51

def delete(scope:)
  @db.execute('DELETE FROM memories WHERE scope = ?', [scope])
end

#delete_record(id:, scope:) ⇒ Object



55
56
57
# File 'lib/rcrewai/memory/sqlite_store.rb', line 55

def delete_record(id:, scope:)
  @db.execute('DELETE FROM memories WHERE id = ? AND scope = ?', [id, scope])
end

#search(vector, k:, scope:) ⇒ Object



42
43
44
45
46
47
48
49
# File 'lib/rcrewai/memory/sqlite_store.rb', line 42

def search(vector, k:, scope:)
  candidates(scope)
    .reject { |r| r[:vector].nil? }
    .map { |r| [r, Similarity.cosine(vector, r[:vector])] }
    .sort_by { |(_r, score)| -score }
    .first(k)
    .map(&:first)
end