llmemory 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -5
  3. data/app/controllers/llmemory/dashboard/application_controller.rb +12 -0
  4. data/app/views/llmemory/dashboard/short_term/show.html.erb +1 -1
  5. data/lib/llmemory/active_record_helpers.rb +21 -0
  6. data/lib/llmemory/cli/commands/mcp.rb +5 -3
  7. data/lib/llmemory/configuration.rb +41 -6
  8. data/lib/llmemory/crypto/field_helpers.rb +5 -0
  9. data/lib/llmemory/extractors/entity_relation_extractor.rb +9 -2
  10. data/lib/llmemory/extractors/fact_extractor.rb +67 -1
  11. data/lib/llmemory/forget_log.rb +7 -3
  12. data/lib/llmemory/llm/anthropic.rb +4 -6
  13. data/lib/llmemory/llm/http_client.rb +55 -0
  14. data/lib/llmemory/llm/openai.rb +5 -7
  15. data/lib/llmemory/llm/tracking_client.rb +22 -12
  16. data/lib/llmemory/llm/usage.rb +4 -0
  17. data/lib/llmemory/llm/usage_ledger.rb +25 -24
  18. data/lib/llmemory/long_term/episodic/memory.rb +5 -2
  19. data/lib/llmemory/long_term/episodic/storages/active_record_storage.rb +3 -1
  20. data/lib/llmemory/long_term/file_based/memory.rb +9 -4
  21. data/lib/llmemory/long_term/file_based/storage.rb +15 -2
  22. data/lib/llmemory/long_term/file_based/storages/active_record_storage.rb +3 -1
  23. data/lib/llmemory/long_term/graph_based/memory.rb +39 -5
  24. data/lib/llmemory/long_term/graph_based/storage.rb +15 -2
  25. data/lib/llmemory/long_term/procedural/memory.rb +5 -2
  26. data/lib/llmemory/long_term/procedural/storages/active_record_storage.rb +3 -1
  27. data/lib/llmemory/maintenance/consolidator.rb +6 -2
  28. data/lib/llmemory/mcp/authentication.rb +5 -3
  29. data/lib/llmemory/mcp/server.rb +21 -6
  30. data/lib/llmemory/mcp/store_helpers.rb +35 -0
  31. data/lib/llmemory/mcp/tools/memory_save.rb +15 -18
  32. data/lib/llmemory/mcp/tools/memory_search.rb +16 -19
  33. data/lib/llmemory/memory.rb +29 -11
  34. data/lib/llmemory/memory_module.rb +6 -2
  35. data/lib/llmemory/provenance.rb +23 -1
  36. data/lib/llmemory/retrieval/feedback_store.rb +11 -5
  37. data/lib/llmemory/retrieval/multi_source.rb +26 -0
  38. data/lib/llmemory/retrieval/temporal_ranker.rb +4 -2
  39. data/lib/llmemory/retrieval.rb +1 -0
  40. data/lib/llmemory/short_term/pruner.rb +16 -4
  41. data/lib/llmemory/short_term/session_lifecycle.rb +18 -9
  42. data/lib/llmemory/short_term/stores/active_record_store.rb +37 -8
  43. data/lib/llmemory/short_term/stores/base.rb +9 -0
  44. data/lib/llmemory/short_term/stores/key_codec.rb +33 -0
  45. data/lib/llmemory/short_term/stores/memory_store.rb +38 -7
  46. data/lib/llmemory/short_term/stores/postgres_store.rb +81 -33
  47. data/lib/llmemory/short_term/stores/redis_store.rb +85 -9
  48. data/lib/llmemory/short_term/stores.rb +17 -2
  49. data/lib/llmemory/vector_store/active_record_store.rb +23 -8
  50. data/lib/llmemory/vector_store/base.rb +4 -0
  51. data/lib/llmemory/vector_store/memory_store.rb +9 -0
  52. data/lib/llmemory/vector_store/openai_embeddings.rb +6 -7
  53. data/lib/llmemory/version.rb +1 -1
  54. data/lib/llmemory/working_memory.rb +10 -7
  55. metadata +6 -15
@@ -13,58 +13,103 @@ module Llmemory
13
13
  @database_url = database_url || Llmemory.configuration.database_url
14
14
  @connection = nil
15
15
  @cipher = cipher || Llmemory.build_cipher
16
+ @table_ready = false
17
+ @conn_mutex = Mutex.new
16
18
  end
17
19
 
18
20
  def save(user_id, session_id, state)
19
- ensure_table!
20
- conn.exec_params(
21
- <<~SQL,
22
- INSERT INTO llmemory_checkpoints (user_id, session_id, state, updated_at)
23
- VALUES ($1, $2, $3, $4)
24
- ON CONFLICT (user_id, session_id)
25
- DO UPDATE SET state = $3, updated_at = $4
26
- SQL
27
- [user_id, session_id, serialize(state), Time.now.utc.iso8601]
28
- )
21
+ with_conn do |connection|
22
+ ensure_table!(connection)
23
+ connection.exec_params(
24
+ <<~SQL,
25
+ INSERT INTO llmemory_checkpoints (user_id, session_id, state, updated_at)
26
+ VALUES ($1, $2, $3, $4)
27
+ ON CONFLICT (user_id, session_id)
28
+ DO UPDATE SET state = $3, updated_at = $4
29
+ SQL
30
+ [user_id, session_id, serialize(state), Time.now.utc.iso8601]
31
+ )
32
+ end
29
33
  true
30
34
  end
31
35
 
32
36
  def load(user_id, session_id)
33
- ensure_table!
34
- result = conn.exec_params(
35
- "SELECT state FROM llmemory_checkpoints WHERE user_id = $1 AND session_id = $2",
36
- [user_id, session_id]
37
- )
38
- row = result.first
39
- row ? deserialize(row["state"]) : nil
37
+ with_conn do |connection|
38
+ ensure_table!(connection)
39
+ result = connection.exec_params(
40
+ "SELECT state FROM llmemory_checkpoints WHERE user_id = $1 AND session_id = $2",
41
+ [user_id, session_id]
42
+ )
43
+ row = result.first
44
+ row ? deserialize(row["state"]) : nil
45
+ end
40
46
  end
41
47
 
42
48
  def delete(user_id, session_id)
43
- ensure_table!
44
- conn.exec_params(
45
- "DELETE FROM llmemory_checkpoints WHERE user_id = $1 AND session_id = $2",
46
- [user_id, session_id]
47
- )
49
+ with_conn do |connection|
50
+ ensure_table!(connection)
51
+ connection.exec_params(
52
+ "DELETE FROM llmemory_checkpoints WHERE user_id = $1 AND session_id = $2",
53
+ [user_id, session_id]
54
+ )
55
+ end
48
56
  true
49
57
  end
50
58
 
59
+ def update(user_id, session_id, &block)
60
+ with_conn do |connection|
61
+ ensure_table!(connection)
62
+ connection.transaction do
63
+ result = connection.exec_params(
64
+ "SELECT state FROM llmemory_checkpoints WHERE user_id = $1 AND session_id = $2 FOR UPDATE",
65
+ [user_id, session_id]
66
+ )
67
+ row = result.first
68
+ current = row ? deserialize(row["state"]) : nil
69
+ new_state = yield(current)
70
+ next new_state if new_state.nil?
71
+
72
+ connection.exec_params(
73
+ <<~SQL,
74
+ INSERT INTO llmemory_checkpoints (user_id, session_id, state, updated_at)
75
+ VALUES ($1, $2, $3, $4)
76
+ ON CONFLICT (user_id, session_id)
77
+ DO UPDATE SET state = $3, updated_at = $4
78
+ SQL
79
+ [user_id, session_id, serialize(new_state), Time.now.utc.iso8601]
80
+ )
81
+ new_state
82
+ end
83
+ end
84
+ end
85
+
51
86
  def list_users
52
- ensure_table!
53
- result = conn.exec("SELECT DISTINCT user_id FROM llmemory_checkpoints")
54
- result.map { |r| r["user_id"] }
87
+ with_conn do |connection|
88
+ ensure_table!(connection)
89
+ result = connection.exec("SELECT DISTINCT user_id FROM llmemory_checkpoints")
90
+ result.map { |r| r["user_id"] }
91
+ end
55
92
  end
56
93
 
57
94
  def list_sessions(user_id:)
58
- ensure_table!
59
- result = conn.exec_params(
60
- "SELECT session_id FROM llmemory_checkpoints WHERE user_id = $1",
61
- [user_id]
62
- )
63
- result.map { |r| r["session_id"] }
95
+ with_conn do |connection|
96
+ ensure_table!(connection)
97
+ result = connection.exec_params(
98
+ "SELECT session_id FROM llmemory_checkpoints WHERE user_id = $1",
99
+ [user_id]
100
+ )
101
+ result.map { |r| r["session_id"] }
102
+ end
64
103
  end
65
104
 
66
105
  private
67
106
 
107
+ def with_conn
108
+ @conn_mutex.synchronize do
109
+ yield(conn)
110
+ end
111
+ end
112
+
68
113
  def conn
69
114
  @connection ||= begin
70
115
  require "pg"
@@ -72,8 +117,10 @@ module Llmemory
72
117
  end
73
118
  end
74
119
 
75
- def ensure_table!
76
- conn.exec(<<~SQL)
120
+ def ensure_table!(connection)
121
+ return if @table_ready
122
+
123
+ connection.exec(<<~SQL)
77
124
  CREATE TABLE IF NOT EXISTS llmemory_checkpoints (
78
125
  user_id TEXT NOT NULL,
79
126
  session_id TEXT NOT NULL,
@@ -82,6 +129,7 @@ module Llmemory
82
129
  PRIMARY KEY (user_id, session_id)
83
130
  )
84
131
  SQL
132
+ @table_ready = true
85
133
  end
86
134
 
87
135
  def serialize(state)
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative "base"
4
4
  require_relative "../../crypto/field_helpers"
5
+ require_relative "key_codec"
6
+ require_relative "../session_lifecycle"
5
7
 
6
8
  module Llmemory
7
9
  module ShortTerm
@@ -9,39 +11,96 @@ module Llmemory
9
11
  class RedisStore < Base
10
12
  include Llmemory::Crypto::FieldHelpers
11
13
 
14
+ KEY_PREFIX = "llmemory:checkpoint"
15
+ MAX_CAS_RETRIES = 5
16
+
12
17
  def initialize(redis_url: nil, cipher: nil)
13
18
  @redis_url = redis_url || Llmemory.configuration.redis_url
14
19
  @redis = nil
15
20
  @cipher = cipher || Llmemory.build_cipher
21
+ @conn_mutex = Mutex.new
16
22
  end
17
23
 
18
24
  def save(user_id, session_id, state)
19
- redis.set(key_for(user_id, session_id), serialize(state), ex: 86400 * 7) # 7 days TTL
25
+ with_redis do |client|
26
+ key = key_for(user_id, session_id)
27
+ payload = serialize(state)
28
+ if pseudo_session?(session_id)
29
+ client.set(key, payload)
30
+ else
31
+ client.set(key, payload, ex: session_ttl_seconds)
32
+ end
33
+ end
20
34
  true
21
35
  end
22
36
 
23
37
  def load(user_id, session_id)
24
- data = redis.get(key_for(user_id, session_id))
25
- data ? deserialize(data) : nil
38
+ with_redis do |client|
39
+ data = client.get(key_for(user_id, session_id))
40
+ data ? deserialize(data) : nil
41
+ end
26
42
  end
27
43
 
28
44
  def delete(user_id, session_id)
29
- redis.del(key_for(user_id, session_id))
45
+ with_redis { |client| client.del(key_for(user_id, session_id)) }
30
46
  true
31
47
  end
32
48
 
49
+ def update(user_id, session_id, &block)
50
+ key = key_for(user_id, session_id)
51
+ retries = 0
52
+
53
+ loop do
54
+ result = with_redis do |client|
55
+ client.watch(key) do
56
+ raw = client.get(key)
57
+ current = raw ? deserialize(raw) : nil
58
+ new_state = yield(current)
59
+ next new_state if new_state.nil?
60
+
61
+ payload = serialize(new_state)
62
+ if pseudo_session?(session_id)
63
+ client.multi { |multi| multi.set(key, payload) }
64
+ else
65
+ client.multi { |multi| multi.set(key, payload, ex: session_ttl_seconds) }
66
+ end
67
+ new_state
68
+ end
69
+ end
70
+ return result unless result.nil?
71
+
72
+ retries += 1
73
+ raise Llmemory::StoreError, "Redis CAS update failed after #{MAX_CAS_RETRIES} retries" if retries >= MAX_CAS_RETRIES
74
+ end
75
+ end
76
+
33
77
  def list_users
34
- keys = redis.keys("llmemory:checkpoint:*:*")
35
- keys.map { |k| k.split(":")[2] }.uniq
78
+ scan_keys("#{KEY_PREFIX}:*").filter_map do |k|
79
+ parts = k.split(":", 4)
80
+ next unless parts.size >= 4
81
+
82
+ KeyCodec.decode(parts[2])
83
+ end.uniq
36
84
  end
37
85
 
38
86
  def list_sessions(user_id:)
39
- keys = redis.keys("llmemory:checkpoint:#{user_id}:*")
40
- keys.map { |k| k.split(":", 4).last }
87
+ encoded_user = KeyCodec.encode(user_id)
88
+ scan_keys("#{KEY_PREFIX}:#{encoded_user}:*").filter_map do |k|
89
+ parts = k.split(":", 4)
90
+ next unless parts.size >= 4
91
+
92
+ KeyCodec.decode(parts[3])
93
+ end
41
94
  end
42
95
 
43
96
  private
44
97
 
98
+ def with_redis
99
+ @conn_mutex.synchronize do
100
+ yield(redis)
101
+ end
102
+ end
103
+
45
104
  def redis
46
105
  @redis ||= begin
47
106
  require "redis"
@@ -49,8 +108,16 @@ module Llmemory
49
108
  end
50
109
  end
51
110
 
111
+ def scan_keys(match)
112
+ keys = []
113
+ with_redis do |client|
114
+ client.scan_each(match: match) { |key| keys << key }
115
+ end
116
+ keys
117
+ end
118
+
52
119
  def key_for(user_id, session_id)
53
- "llmemory:checkpoint:#{user_id}:#{session_id}"
120
+ "#{KEY_PREFIX}:#{KeyCodec.encode(user_id)}:#{KeyCodec.encode(session_id)}"
54
121
  end
55
122
 
56
123
  def serialize(state)
@@ -60,6 +127,15 @@ module Llmemory
60
127
  def deserialize(data)
61
128
  deserialize_state(data)
62
129
  end
130
+
131
+ def pseudo_session?(session_id)
132
+ SessionLifecycle.pseudo_session?(session_id)
133
+ end
134
+
135
+ def session_ttl_seconds
136
+ ttl = Llmemory.configuration.redis_session_ttl_seconds
137
+ ttl.positive? ? ttl : 86_400 * 30
138
+ end
63
139
  end
64
140
  end
65
141
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "stores/key_codec"
3
4
  require_relative "stores/base"
4
5
  require_relative "stores/memory_store"
5
6
  require_relative "stores/redis_store"
@@ -12,8 +13,14 @@ module Llmemory
12
13
  # Shared by Checkpoint, SessionLifecycle and WorkingMemory.
13
14
  def self.build(store_type = nil, cipher: nil)
14
15
  resolved_cipher = cipher || Llmemory.build_cipher
15
- case (store_type || Llmemory.configuration.short_term_store).to_sym
16
- when :memory then MemoryStore.new(cipher: resolved_cipher)
16
+ type = (store_type || Llmemory.configuration.short_term_store).to_sym
17
+ case type
18
+ when :memory
19
+ if Llmemory.configuration.shared_memory_stores
20
+ shared_memory_store(resolved_cipher)
21
+ else
22
+ MemoryStore.new(cipher: resolved_cipher)
23
+ end
17
24
  when :redis then RedisStore.new(cipher: resolved_cipher)
18
25
  when :postgres then PostgresStore.new(cipher: resolved_cipher)
19
26
  when :active_record, :activerecord
@@ -23,6 +30,14 @@ module Llmemory
23
30
  MemoryStore.new(cipher: resolved_cipher)
24
31
  end
25
32
  end
33
+
34
+ def self.shared_memory_store(cipher)
35
+ @shared_memory_store ||= MemoryStore.new(cipher: cipher)
36
+ end
37
+
38
+ def self.reset_shared_singletons!
39
+ @shared_memory_store = nil
40
+ end
26
41
  end
27
42
  end
28
43
  end
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "base"
4
+ require_relative "../../active_record_helpers"
4
5
 
5
6
  module Llmemory
6
7
  module VectorStore
7
8
  # Persists embeddings in llmemory_embeddings (pgvector).
8
9
  # Use when long_term_store is :active_record so hybrid search finds persisted embeddings.
9
10
  class ActiveRecordStore < Base
11
+ include Llmemory::ActiveRecordHelpers
10
12
  def initialize(embedding_provider: nil, source_type: "edge", cipher: nil)
11
13
  self.class.load_model!
12
14
  @embedding_provider = embedding_provider
@@ -35,14 +37,16 @@ module Llmemory
35
37
  def store(id:, embedding:, metadata: {}, user_id: nil)
36
38
  return id if user_id.nil? || user_id.to_s.empty?
37
39
  text_content = (metadata || {}).dig("text") || (metadata || {}).dig(:text)
38
- rec = Llmemory::VectorStore::ActiveRecordEmbedding.find_or_initialize_by(
39
- user_id: user_id.to_s,
40
- source_type: @source_type,
41
- source_id: id.to_s
42
- )
43
- rec.embedding = embedding.to_a.map(&:to_f)
44
- rec.text_content = encrypt_text_content(text_content)
45
- rec.save!
40
+ with_unique_retry do
41
+ rec = Llmemory::VectorStore::ActiveRecordEmbedding.find_or_initialize_by(
42
+ user_id: user_id.to_s,
43
+ source_type: @source_type,
44
+ source_id: id.to_s
45
+ )
46
+ rec.embedding = embedding.to_a.map(&:to_f)
47
+ rec.text_content = encrypt_text_content(text_content)
48
+ rec.save!
49
+ end
46
50
  id
47
51
  end
48
52
 
@@ -81,6 +85,17 @@ module Llmemory
81
85
  search(query_embedding, top_k: top_k, user_id: user_id)
82
86
  end
83
87
 
88
+ def delete(id:, user_id: nil)
89
+ return false if user_id.nil? || user_id.to_s.empty?
90
+
91
+ Llmemory::VectorStore::ActiveRecordEmbedding.where(
92
+ user_id: user_id.to_s,
93
+ source_type: @source_type,
94
+ source_id: id.to_s
95
+ ).delete_all
96
+ true
97
+ end
98
+
84
99
  private
85
100
 
86
101
  def encrypt_text_content(text)
@@ -14,6 +14,10 @@ module Llmemory
14
14
  def search(query_embedding, top_k: 10)
15
15
  raise NotImplementedError, "#{self.class}#search must be implemented"
16
16
  end
17
+
18
+ def delete(id:, user_id: nil)
19
+ raise NotImplementedError, "#{self.class}#delete must be implemented"
20
+ end
17
21
  end
18
22
  end
19
23
  end
@@ -55,6 +55,15 @@ module Llmemory
55
55
  search(query_embedding, top_k: top_k, user_id: user_id)
56
56
  end
57
57
 
58
+ def delete(id:, user_id: nil)
59
+ if user_id
60
+ @entries.delete("#{user_id}:#{id}")
61
+ else
62
+ @entries.delete_if { |_key, data| data[:source_id].to_s == id.to_s }
63
+ end
64
+ true
65
+ end
66
+
58
67
  private
59
68
 
60
69
  def decrypt_metadata(meta)
@@ -5,18 +5,21 @@ require "json"
5
5
  require "digest"
6
6
  require_relative "base"
7
7
  require_relative "../llm/usage"
8
+ require_relative "../llm/http_client"
8
9
 
9
10
  module Llmemory
10
11
  module VectorStore
11
12
  class OpenAIEmbeddings < Base
13
+ include Llmemory::LLM::HttpClient
12
14
  DEFAULT_MODEL = "text-embedding-3-small"
13
15
  DEFAULT_DIMS = 1536
14
16
 
15
17
  attr_reader :last_usage
16
18
 
17
- def initialize(api_key: nil, model: nil)
19
+ def initialize(api_key: nil, model: nil, base_url: nil)
18
20
  @api_key = api_key || Llmemory.configuration.llm_api_key
19
21
  @model = model || DEFAULT_MODEL
22
+ @base_url = base_url || Llmemory.configuration.llm_base_url || "https://api.openai.com/v1"
20
23
  @cache = {}
21
24
  @cache_order = []
22
25
  @last_usage = Llmemory::LLM::Usage.zero
@@ -64,7 +67,7 @@ module Llmemory
64
67
  result = nil
65
68
  payload = { provider: :openai, model: @model, text_chars: text.to_s.length }
66
69
  Llmemory::Instrumentation.instrument(:llm_embed, payload) do
67
- response = connection.post("embeddings") do |req|
70
+ response = post_with_resilience(connection, "embeddings") do |req|
68
71
  req.headers["Authorization"] = "Bearer #{@api_key}"
69
72
  req.headers["Content-Type"] = "application/json"
70
73
  req.body = { input: text.to_s.strip, model: @model }.to_json
@@ -90,11 +93,7 @@ module Llmemory
90
93
  end
91
94
 
92
95
  def connection
93
- @connection ||= Faraday.new(url: "https://api.openai.com/v1") do |f|
94
- f.request :json
95
- f.response :json
96
- f.adapter Faraday.default_adapter
97
- end
96
+ @connection ||= build_faraday_connection(@base_url)
98
97
  end
99
98
 
100
99
  def store(id:, embedding:, metadata: {})
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Llmemory
4
- VERSION = "0.2.6"
4
+ VERSION = "0.2.7"
5
5
  end
@@ -36,18 +36,21 @@ module Llmemory
36
36
  end
37
37
 
38
38
  def set(slot, value)
39
- state = read
40
- state[slot.to_sym] = value
41
- persist(state)
39
+ @store.update(@user_id, @store_key) do |state|
40
+ state = symbolize(state || {})
41
+ state[slot.to_sym] = value
42
+ state
43
+ end
42
44
  value
43
45
  end
44
46
 
45
47
  # Bulk update in a single write.
46
48
  def update(**slots)
47
- state = read
48
- slots.each { |k, v| state[k.to_sym] = v }
49
- persist(state)
50
- state
49
+ @store.update(@user_id, @store_key) do |state|
50
+ state = symbolize(state || {})
51
+ slots.each { |k, v| state[k.to_sym] = v }
52
+ state
53
+ end
51
54
  end
52
55
 
53
56
  # Slots set by the caller beyond the predefined typed ones.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: llmemory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - llmemory
@@ -65,20 +65,6 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '3.12'
68
- - !ruby/object:Gem::Dependency
69
- name: vcr
70
- requirement: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '6.2'
75
- type: :development
76
- prerelease: false
77
- version_requirements: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '6.2'
82
68
  - !ruby/object:Gem::Dependency
83
69
  name: webmock
84
70
  requirement: !ruby/object:Gem::Requirement
@@ -141,6 +127,7 @@ files:
141
127
  - lib/llmemory.rb
142
128
  - lib/llmemory/actions.rb
143
129
  - lib/llmemory/actions/reason.rb
130
+ - lib/llmemory/active_record_helpers.rb
144
131
  - lib/llmemory/cli.rb
145
132
  - lib/llmemory/cli/commands/base.rb
146
133
  - lib/llmemory/cli/commands/episodic.rb
@@ -174,6 +161,7 @@ files:
174
161
  - lib/llmemory/llm.rb
175
162
  - lib/llmemory/llm/anthropic.rb
176
163
  - lib/llmemory/llm/base.rb
164
+ - lib/llmemory/llm/http_client.rb
177
165
  - lib/llmemory/llm/openai.rb
178
166
  - lib/llmemory/llm/response.rb
179
167
  - lib/llmemory/llm/tracking_client.rb
@@ -236,6 +224,7 @@ files:
236
224
  - lib/llmemory/mcp.rb
237
225
  - lib/llmemory/mcp/authentication.rb
238
226
  - lib/llmemory/mcp/server.rb
227
+ - lib/llmemory/mcp/store_helpers.rb
239
228
  - lib/llmemory/mcp/tools/memory_add_message.rb
240
229
  - lib/llmemory/mcp/tools/memory_consolidate.rb
241
230
  - lib/llmemory/mcp/tools/memory_episode_record.rb
@@ -265,6 +254,7 @@ files:
265
254
  - lib/llmemory/retrieval/engine.rb
266
255
  - lib/llmemory/retrieval/feedback_store.rb
267
256
  - lib/llmemory/retrieval/mmr_reranker.rb
257
+ - lib/llmemory/retrieval/multi_source.rb
268
258
  - lib/llmemory/retrieval/temporal_ranker.rb
269
259
  - lib/llmemory/short_term.rb
270
260
  - lib/llmemory/short_term/checkpoint.rb
@@ -275,6 +265,7 @@ files:
275
265
  - lib/llmemory/short_term/stores/active_record_checkpoint.rb
276
266
  - lib/llmemory/short_term/stores/active_record_store.rb
277
267
  - lib/llmemory/short_term/stores/base.rb
268
+ - lib/llmemory/short_term/stores/key_codec.rb
278
269
  - lib/llmemory/short_term/stores/memory_store.rb
279
270
  - lib/llmemory/short_term/stores/postgres_store.rb
280
271
  - lib/llmemory/short_term/stores/redis_store.rb