llmemory 0.2.2 → 0.2.4

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +65 -1
  3. data/lib/llmemory/cli/commands/stats.rb +5 -0
  4. data/lib/llmemory/configuration.rb +22 -2
  5. data/lib/llmemory/crypto/cipher.rb +147 -0
  6. data/lib/llmemory/crypto/field_helpers.rb +110 -0
  7. data/lib/llmemory/instrumentation.rb +4 -2
  8. data/lib/llmemory/llm/anthropic.rb +10 -4
  9. data/lib/llmemory/llm/base.rb +42 -0
  10. data/lib/llmemory/llm/openai.rb +29 -13
  11. data/lib/llmemory/llm/response.rb +18 -0
  12. data/lib/llmemory/llm/tracking_client.rb +61 -0
  13. data/lib/llmemory/llm/usage.rb +31 -0
  14. data/lib/llmemory/llm/usage_ledger.rb +118 -0
  15. data/lib/llmemory/llm/usage_recorder.rb +37 -0
  16. data/lib/llmemory/llm.rb +5 -0
  17. data/lib/llmemory/long_term/episodic/memory.rb +16 -4
  18. data/lib/llmemory/long_term/episodic/storage.rb +11 -4
  19. data/lib/llmemory/long_term/episodic/storages/active_record_storage.rb +19 -6
  20. data/lib/llmemory/long_term/episodic/storages/database_storage.rb +25 -3
  21. data/lib/llmemory/long_term/episodic/storages/file_storage.rb +22 -5
  22. data/lib/llmemory/long_term/file_based/storage.rb +11 -4
  23. data/lib/llmemory/long_term/file_based/storages/active_record_storage.rb +16 -10
  24. data/lib/llmemory/long_term/file_based/storages/database_storage.rb +24 -8
  25. data/lib/llmemory/long_term/file_based/storages/file_storage.rb +28 -14
  26. data/lib/llmemory/long_term/graph_based/memory.rb +17 -3
  27. data/lib/llmemory/long_term/graph_based/storage.rb +3 -2
  28. data/lib/llmemory/long_term/graph_based/storages/active_record_storage.rb +47 -21
  29. data/lib/llmemory/long_term/procedural/memory.rb +16 -4
  30. data/lib/llmemory/long_term/procedural/storage.rb +11 -4
  31. data/lib/llmemory/long_term/procedural/storages/active_record_storage.rb +33 -13
  32. data/lib/llmemory/long_term/procedural/storages/database_storage.rb +25 -4
  33. data/lib/llmemory/long_term/procedural/storages/file_storage.rb +23 -6
  34. data/lib/llmemory/mcp/tools/memory_stats.rb +13 -0
  35. data/lib/llmemory/memory.rb +66 -15
  36. data/lib/llmemory/short_term/checkpoint.rb +5 -2
  37. data/lib/llmemory/short_term/stores/active_record_store.rb +12 -10
  38. data/lib/llmemory/short_term/stores/memory_store.rb +1 -1
  39. data/lib/llmemory/short_term/stores/postgres_store.rb +11 -5
  40. data/lib/llmemory/short_term/stores/redis_store.rb +7 -5
  41. data/lib/llmemory/short_term/stores.rb +7 -6
  42. data/lib/llmemory/vector_store/active_record_store.rb +30 -3
  43. data/lib/llmemory/vector_store/memory_store.rb +29 -3
  44. data/lib/llmemory/vector_store/openai_embeddings.rb +23 -2
  45. data/lib/llmemory/vector_store.rb +4 -3
  46. data/lib/llmemory/version.rb +1 -1
  47. data/lib/llmemory.rb +2 -0
  48. metadata +8 -1
@@ -4,6 +4,7 @@ require "faraday"
4
4
  require "json"
5
5
  require "digest"
6
6
  require_relative "base"
7
+ require_relative "../llm/usage"
7
8
 
8
9
  module Llmemory
9
10
  module VectorStore
@@ -11,11 +12,14 @@ module Llmemory
11
12
  DEFAULT_MODEL = "text-embedding-3-small"
12
13
  DEFAULT_DIMS = 1536
13
14
 
15
+ attr_reader :last_usage
16
+
14
17
  def initialize(api_key: nil, model: nil)
15
18
  @api_key = api_key || Llmemory.configuration.llm_api_key
16
19
  @model = model || DEFAULT_MODEL
17
20
  @cache = {}
18
21
  @cache_order = []
22
+ @last_usage = Llmemory::LLM::Usage.zero
19
23
  end
20
24
 
21
25
  def embed(text)
@@ -23,7 +27,10 @@ module Llmemory
23
27
 
24
28
  if Llmemory.configuration.embedding_cache_enabled
25
29
  key = cache_key(text)
26
- return @cache[key].dup if @cache.key?(key)
30
+ if @cache.key?(key)
31
+ @last_usage = Llmemory::LLM::Usage.zero
32
+ return @cache[key].dup
33
+ end
27
34
  end
28
35
 
29
36
  result = fetch_embedding(text)
@@ -55,7 +62,8 @@ module Llmemory
55
62
 
56
63
  def fetch_embedding(text)
57
64
  result = nil
58
- Llmemory::Instrumentation.instrument(:llm_embed, provider: :openai, model: @model, text_chars: text.to_s.length) do
65
+ payload = { provider: :openai, model: @model, text_chars: text.to_s.length }
66
+ Llmemory::Instrumentation.instrument(:llm_embed, payload) do
59
67
  response = connection.post("embeddings") do |req|
60
68
  req.headers["Authorization"] = "Bearer #{@api_key}"
61
69
  req.headers["Content-Type"] = "application/json"
@@ -63,11 +71,24 @@ module Llmemory
63
71
  end
64
72
  raise Llmemory::LLMError, "OpenAI Embeddings API error: #{response.body}" unless response.success?
65
73
  body = response.body.is_a?(Hash) ? response.body : JSON.parse(response.body.to_s)
74
+ @last_usage = parse_embed_usage(body["usage"])
75
+ payload.merge!(
76
+ input_tokens: @last_usage.input_tokens,
77
+ output_tokens: @last_usage.output_tokens,
78
+ total_tokens: @last_usage.total_tokens
79
+ )
66
80
  result = body.dig("data", 0, "embedding")&.map(&:to_f) || Array.new(DEFAULT_DIMS, 0.0)
67
81
  end
68
82
  result
69
83
  end
70
84
 
85
+ def parse_embed_usage(raw)
86
+ return Llmemory::LLM::Usage.zero unless raw.is_a?(Hash)
87
+
88
+ total = raw["total_tokens"] || raw[:total_tokens] || 0
89
+ Llmemory::LLM::Usage.new(input_tokens: 0, output_tokens: 0, total_tokens: total)
90
+ end
91
+
71
92
  def connection
72
93
  @connection ||= Faraday.new(url: "https://api.openai.com/v1") do |f|
73
94
  f.request :json
@@ -10,14 +10,15 @@ module Llmemory
10
10
  # from config (:active_record persists in llmemory_embeddings; otherwise
11
11
  # in-process). `source_type` namespaces persisted embeddings so different
12
12
  # memory types (edges, episodes, skills) never collide in the shared table.
13
- def self.build(source_type: "edge")
13
+ def self.build(source_type: "edge", cipher: nil)
14
+ resolved_cipher = cipher || Llmemory.build_cipher
14
15
  embeddings = OpenAIEmbeddings.new
15
16
  store_type = (Llmemory.configuration.long_term_store || :memory).to_s.to_sym
16
17
  if store_type == :active_record || store_type == :activerecord
17
18
  require_relative "vector_store/active_record_store"
18
- ActiveRecordStore.new(embedding_provider: embeddings, source_type: source_type)
19
+ ActiveRecordStore.new(embedding_provider: embeddings, source_type: source_type, cipher: resolved_cipher)
19
20
  else
20
- MemoryStore.new(embedding_provider: embeddings)
21
+ MemoryStore.new(embedding_provider: embeddings, cipher: resolved_cipher)
21
22
  end
22
23
  end
23
24
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Llmemory
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.4"
5
5
  end
data/lib/llmemory.rb CHANGED
@@ -26,3 +26,5 @@ module Llmemory
26
26
  class StoreError < Error; end
27
27
  class LLMError < Error; end
28
28
  end
29
+
30
+ require_relative "llmemory/crypto/cipher"
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.2
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - llmemory
@@ -162,6 +162,8 @@ files:
162
162
  - lib/llmemory/cli/commands/users.rb
163
163
  - lib/llmemory/cli/commands/working.rb
164
164
  - lib/llmemory/configuration.rb
165
+ - lib/llmemory/crypto/cipher.rb
166
+ - lib/llmemory/crypto/field_helpers.rb
165
167
  - lib/llmemory/dashboard.rb
166
168
  - lib/llmemory/dashboard/engine.rb
167
169
  - lib/llmemory/extractors.rb
@@ -173,6 +175,11 @@ files:
173
175
  - lib/llmemory/llm/anthropic.rb
174
176
  - lib/llmemory/llm/base.rb
175
177
  - lib/llmemory/llm/openai.rb
178
+ - lib/llmemory/llm/response.rb
179
+ - lib/llmemory/llm/tracking_client.rb
180
+ - lib/llmemory/llm/usage.rb
181
+ - lib/llmemory/llm/usage_ledger.rb
182
+ - lib/llmemory/llm/usage_recorder.rb
176
183
  - lib/llmemory/long_term.rb
177
184
  - lib/llmemory/long_term/episodic.rb
178
185
  - lib/llmemory/long_term/episodic/episode.rb