Class: Llmemory::LongTerm::Episodic::Storages::DatabaseStorage

Inherits:
Base
  • Object
show all
Defined in:
lib/llmemory/long_term/episodic/storages/database_storage.rb

Overview

PostgreSQL backend. Each episode is stored as a JSONB ‘data` document (plus id/user_id/created_at and a denormalized search_text for keyword search), mirroring the file-based DatabaseStorage pattern.

Instance Method Summary collapse

Constructor Details

#initialize(database_url: nil) ⇒ DatabaseStorage

Returns a new instance of DatabaseStorage.



16
17
18
19
# File 'lib/llmemory/long_term/episodic/storages/database_storage.rb', line 16

def initialize(database_url: nil)
  @database_url = database_url || Llmemory.configuration.database_url
  @connection = nil
end

Instance Method Details

#count_episodes(user_id) ⇒ Object



57
58
59
60
# File 'lib/llmemory/long_term/episodic/storages/database_storage.rb', line 57

def count_episodes(user_id)
  ensure_tables!
  conn.exec_params("SELECT COUNT(*) AS c FROM llmemory_episodes WHERE user_id = $1", [user_id]).first["c"].to_i
end

#delete_episodes(user_id, ids) ⇒ Object



62
63
64
65
66
67
# File 'lib/llmemory/long_term/episodic/storages/database_storage.rb', line 62

def delete_episodes(user_id, ids)
  ensure_tables!
  Array(ids).sum do |id|
    conn.exec_params("DELETE FROM llmemory_episodes WHERE user_id = $1 AND id = $2", [user_id, id]).cmd_tuples
  end
end

#get_episode(user_id, id) ⇒ Object



35
36
37
38
39
# File 'lib/llmemory/long_term/episodic/storages/database_storage.rb', line 35

def get_episode(user_id, id)
  ensure_tables!
  rows = conn.exec_params("SELECT data FROM llmemory_episodes WHERE user_id = $1 AND id = $2", [user_id, id])
  rows.any? ? parse_data(rows.first["data"]) : nil
end

#list_episodes(user_id, limit: nil) ⇒ Object



41
42
43
44
45
46
# File 'lib/llmemory/long_term/episodic/storages/database_storage.rb', line 41

def list_episodes(user_id, limit: nil)
  ensure_tables!
  sql = "SELECT data FROM llmemory_episodes WHERE user_id = $1 ORDER BY created_at DESC"
  sql += " LIMIT #{limit.to_i}" if limit && limit.to_i.positive?
  conn.exec_params(sql, [user_id]).map { |r| parse_data(r["data"]) }
end

#list_usersObject



69
70
71
72
# File 'lib/llmemory/long_term/episodic/storages/database_storage.rb', line 69

def list_users
  ensure_tables!
  conn.exec("SELECT DISTINCT user_id FROM llmemory_episodes").map { |r| r["user_id"] }
end

#save_episode(user_id, episode) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/llmemory/long_term/episodic/storages/database_storage.rb', line 21

def save_episode(user_id, episode)
  ensure_tables!
  id = episode[:id] || episode["id"] || "ep_#{SecureRandom.hex(8)}"
  data = symbolize(episode).merge(id: id, user_id: user_id)
  data[:created_at] ||= Time.now.utc.iso8601
  conn.exec_params(
    "INSERT INTO llmemory_episodes (id, user_id, data, search_text, created_at) " \
    "VALUES ($1, $2, $3::jsonb, $4, $5) " \
    "ON CONFLICT (id) DO UPDATE SET data = $3::jsonb, search_text = $4",
    [id, user_id, JSON.generate(data), searchable_text(data), created_at_value(data)]
  )
  id
end

#search_episodes(user_id, query) ⇒ Object



48
49
50
51
52
53
54
55
# File 'lib/llmemory/long_term/episodic/storages/database_storage.rb', line 48

def search_episodes(user_id, query)
  ensure_tables!
  suffix, params = token_filter("search_text", query, 2)
  conn.exec_params(
    "SELECT data FROM llmemory_episodes WHERE user_id = $1#{suffix} ORDER BY created_at DESC",
    [user_id, *params]
  ).map { |r| parse_data(r["data"]) }
end