llmemory 0.1.13 → 0.1.15
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.
- checksums.yaml +4 -4
- data/README.md +104 -3
- data/lib/generators/llmemory/install/templates/create_llmemory_tables.rb +1 -0
- data/lib/llmemory/configuration.rb +51 -1
- data/lib/llmemory/extractors/fact_extractor.rb +9 -2
- data/lib/llmemory/long_term/file_based/memory.rb +53 -6
- data/lib/llmemory/long_term/file_based/storages/active_record_storage.rb +12 -6
- data/lib/llmemory/long_term/file_based/storages/base.rb +1 -1
- data/lib/llmemory/long_term/file_based/storages/database_storage.rb +11 -8
- data/lib/llmemory/long_term/file_based/storages/file_storage.rb +30 -1
- data/lib/llmemory/long_term/file_based/storages/memory_storage.rb +2 -1
- data/lib/llmemory/long_term/graph_based/memory.rb +5 -1
- data/lib/llmemory/mcp/tools/memory_add_message.rb +1 -1
- data/lib/llmemory/memory.rb +157 -6
- data/lib/llmemory/noise_filter.rb +36 -0
- data/lib/llmemory/retrieval/bm25_scorer.rb +60 -0
- data/lib/llmemory/retrieval/engine.rb +23 -1
- data/lib/llmemory/retrieval/mmr_reranker.rb +56 -0
- data/lib/llmemory/retrieval/temporal_ranker.rb +9 -1
- data/lib/llmemory/short_term/message_sanitizer.rb +43 -0
- data/lib/llmemory/short_term/pruner.rb +65 -0
- data/lib/llmemory/short_term/session_lifecycle.rb +88 -0
- data/lib/llmemory/short_term.rb +2 -0
- data/lib/llmemory/vector_store/openai_embeddings.rb +45 -10
- data/lib/llmemory/version.rb +1 -1
- data/lib/tasks/release.rake +100 -0
- metadata +22 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "faraday"
|
|
4
4
|
require "json"
|
|
5
|
+
require "digest"
|
|
5
6
|
require_relative "base"
|
|
6
7
|
|
|
7
8
|
module Llmemory
|
|
@@ -13,10 +14,46 @@ module Llmemory
|
|
|
13
14
|
def initialize(api_key: nil, model: nil)
|
|
14
15
|
@api_key = api_key || Llmemory.configuration.llm_api_key
|
|
15
16
|
@model = model || DEFAULT_MODEL
|
|
17
|
+
@cache = {}
|
|
18
|
+
@cache_order = []
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def embed(text)
|
|
19
22
|
return Array.new(DEFAULT_DIMS, 0.0) if text.to_s.strip.empty?
|
|
23
|
+
|
|
24
|
+
if Llmemory.configuration.embedding_cache_enabled
|
|
25
|
+
key = cache_key(text)
|
|
26
|
+
return @cache[key].dup if @cache.key?(key)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
result = fetch_embedding(text)
|
|
30
|
+
|
|
31
|
+
if Llmemory.configuration.embedding_cache_enabled
|
|
32
|
+
evict_if_needed
|
|
33
|
+
@cache[cache_key(text)] = result.dup
|
|
34
|
+
@cache_order << cache_key(text)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def cache_key(text)
|
|
43
|
+
Digest::SHA256.hexdigest("#{@model}:#{text.to_s.strip}")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def evict_if_needed
|
|
47
|
+
max = Llmemory.configuration.embedding_cache_max_entries.to_i
|
|
48
|
+
return if max <= 0 || @cache.size < max
|
|
49
|
+
|
|
50
|
+
while @cache_order.any? && @cache.size >= max
|
|
51
|
+
k = @cache_order.shift
|
|
52
|
+
@cache.delete(k)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def fetch_embedding(text)
|
|
20
57
|
response = connection.post("embeddings") do |req|
|
|
21
58
|
req.headers["Authorization"] = "Bearer #{@api_key}"
|
|
22
59
|
req.headers["Content-Type"] = "application/json"
|
|
@@ -27,16 +64,6 @@ module Llmemory
|
|
|
27
64
|
body.dig("data", 0, "embedding")&.map(&:to_f) || Array.new(DEFAULT_DIMS, 0.0)
|
|
28
65
|
end
|
|
29
66
|
|
|
30
|
-
def store(id:, embedding:, metadata: {})
|
|
31
|
-
raise NotImplementedError, "OpenAIEmbeddings does not store; use a VectorStore backend (e.g. MemoryStore)"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def search(query_embedding, top_k: 10)
|
|
35
|
-
raise NotImplementedError, "OpenAIEmbeddings does not search; use a VectorStore backend"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
67
|
def connection
|
|
41
68
|
@connection ||= Faraday.new(url: "https://api.openai.com/v1") do |f|
|
|
42
69
|
f.request :json
|
|
@@ -44,6 +71,14 @@ module Llmemory
|
|
|
44
71
|
f.adapter Faraday.default_adapter
|
|
45
72
|
end
|
|
46
73
|
end
|
|
74
|
+
|
|
75
|
+
def store(id:, embedding:, metadata: {})
|
|
76
|
+
raise NotImplementedError, "OpenAIEmbeddings does not store; use a VectorStore backend (e.g. MemoryStore)"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def search(query_embedding, top_k: 10)
|
|
80
|
+
raise NotImplementedError, "OpenAIEmbeddings does not search; use a VectorStore backend"
|
|
81
|
+
end
|
|
47
82
|
end
|
|
48
83
|
end
|
|
49
84
|
end
|
data/lib/llmemory/version.rb
CHANGED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :release do
|
|
4
|
+
desc "Bump version (patch|minor|major). Checks: branch=main, no uncommitted changes, tests pass. Then: Gemfile.lock, CHANGELOG, commit, push, tag"
|
|
5
|
+
task :bump, [:bump_type] => [] do |_t, args|
|
|
6
|
+
require_relative "../llmemory/version"
|
|
7
|
+
|
|
8
|
+
# Pre-flight checks
|
|
9
|
+
current_branch = `git rev-parse --abbrev-ref HEAD`.strip
|
|
10
|
+
abort "Current branch must be main (got: #{current_branch})" unless current_branch == "main"
|
|
11
|
+
|
|
12
|
+
# Allow only release-related files to be modified (we'll commit them)
|
|
13
|
+
release_files = %w[lib/llmemory/version.rb Gemfile.lock CHANGELOG.txt]
|
|
14
|
+
status_lines = `git status --porcelain`.strip.lines
|
|
15
|
+
other_changes = status_lines.reject do |line|
|
|
16
|
+
path = line.sub(/\A..\s+/, "").strip
|
|
17
|
+
release_files.include?(path)
|
|
18
|
+
end
|
|
19
|
+
abort "Working tree has uncommitted changes outside release files. Commit or stash them first." unless other_changes.empty?
|
|
20
|
+
|
|
21
|
+
puts "Running tests..."
|
|
22
|
+
sh "bundle exec rspec"
|
|
23
|
+
puts "Tests passed.\n\n"
|
|
24
|
+
|
|
25
|
+
bump_type = (args[:bump_type] || "patch").to_s.downcase
|
|
26
|
+
unless %w[patch minor major].include?(bump_type)
|
|
27
|
+
abort "Bump type must be: patch, minor, or major"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
seg = Gem::Version.new(Llmemory::VERSION).segments
|
|
31
|
+
new_version = case bump_type
|
|
32
|
+
when "patch" then Gem::Version.new("#{seg[0]}.#{seg[1] || 0}.#{(seg[2] || 0) + 1}")
|
|
33
|
+
when "minor" then Gem::Version.new("#{seg[0]}.#{(seg[1] || 0) + 1}.0")
|
|
34
|
+
when "major" then Gem::Version.new("#{(seg[0] || 0) + 1}.0.0")
|
|
35
|
+
end
|
|
36
|
+
new_version_s = new_version.to_s
|
|
37
|
+
|
|
38
|
+
puts "Bumping #{Llmemory::VERSION} -> #{new_version_s} (#{bump_type})"
|
|
39
|
+
|
|
40
|
+
# 1. Update version.rb
|
|
41
|
+
version_file = File.expand_path("../llmemory/version.rb", __dir__)
|
|
42
|
+
content = File.read(version_file)
|
|
43
|
+
content = content.sub(/VERSION = "[^"]+"/, %(VERSION = "#{new_version_s}"))
|
|
44
|
+
File.write(version_file, content)
|
|
45
|
+
puts " Updated lib/llmemory/version.rb"
|
|
46
|
+
|
|
47
|
+
# 2. bundle install
|
|
48
|
+
sh "bundle install"
|
|
49
|
+
puts " Updated Gemfile.lock"
|
|
50
|
+
|
|
51
|
+
# 3. Update CHANGELOG.txt
|
|
52
|
+
changelog_path = File.expand_path("../../CHANGELOG.txt", __dir__)
|
|
53
|
+
changelog_content = if File.exist?(changelog_path)
|
|
54
|
+
File.read(changelog_path)
|
|
55
|
+
else
|
|
56
|
+
""
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
today = Time.now.strftime("%Y-%m-%d")
|
|
60
|
+
last_tag = `git describe --tags --abbrev=0 2>/dev/null`.strip
|
|
61
|
+
commits = if last_tag.empty?
|
|
62
|
+
`git log --oneline`.strip
|
|
63
|
+
else
|
|
64
|
+
`git log #{last_tag}..HEAD --oneline`.strip
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
new_entry = <<~CHANGELOG
|
|
68
|
+
|
|
69
|
+
## [#{new_version_s}] - #{today}
|
|
70
|
+
|
|
71
|
+
### Changes
|
|
72
|
+
#{commits.lines.map { |l| "- #{l.strip}" }.join("\n")}
|
|
73
|
+
CHANGELOG
|
|
74
|
+
|
|
75
|
+
header = "# Changelog\n\n"
|
|
76
|
+
if changelog_content.empty?
|
|
77
|
+
changelog_content = header + new_entry
|
|
78
|
+
else
|
|
79
|
+
changelog_content = header + changelog_content unless changelog_content.start_with?(header)
|
|
80
|
+
changelog_content = changelog_content.sub(/(# Changelog\n\n)/m, "\\1#{new_entry.lstrip}")
|
|
81
|
+
end
|
|
82
|
+
File.write(changelog_path, changelog_content)
|
|
83
|
+
puts " Updated CHANGELOG.txt"
|
|
84
|
+
|
|
85
|
+
# 4. Commit
|
|
86
|
+
sh "git add lib/llmemory/version.rb Gemfile.lock CHANGELOG.txt"
|
|
87
|
+
sh "git commit -m 'Release v#{new_version_s}'"
|
|
88
|
+
|
|
89
|
+
# 5. Push
|
|
90
|
+
sh "git push"
|
|
91
|
+
|
|
92
|
+
# 6. Tag
|
|
93
|
+
sh "git tag v#{new_version_s}"
|
|
94
|
+
|
|
95
|
+
# 7. Push tag
|
|
96
|
+
sh "git push origin v#{new_version_s}"
|
|
97
|
+
|
|
98
|
+
puts "\nDone. Released v#{new_version_s}"
|
|
99
|
+
end
|
|
100
|
+
end
|
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.1.
|
|
4
|
+
version: 0.1.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- llmemory
|
|
@@ -37,6 +37,20 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0.6'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: simplecov
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.22'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.22'
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
55
|
name: rspec
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -180,12 +194,18 @@ files:
|
|
|
180
194
|
- lib/llmemory/mcp/tools/memory_timeline.rb
|
|
181
195
|
- lib/llmemory/mcp/tools/memory_timeline_context.rb
|
|
182
196
|
- lib/llmemory/memory.rb
|
|
197
|
+
- lib/llmemory/noise_filter.rb
|
|
183
198
|
- lib/llmemory/retrieval.rb
|
|
199
|
+
- lib/llmemory/retrieval/bm25_scorer.rb
|
|
184
200
|
- lib/llmemory/retrieval/context_assembler.rb
|
|
185
201
|
- lib/llmemory/retrieval/engine.rb
|
|
202
|
+
- lib/llmemory/retrieval/mmr_reranker.rb
|
|
186
203
|
- lib/llmemory/retrieval/temporal_ranker.rb
|
|
187
204
|
- lib/llmemory/short_term.rb
|
|
188
205
|
- lib/llmemory/short_term/checkpoint.rb
|
|
206
|
+
- lib/llmemory/short_term/message_sanitizer.rb
|
|
207
|
+
- lib/llmemory/short_term/pruner.rb
|
|
208
|
+
- lib/llmemory/short_term/session_lifecycle.rb
|
|
189
209
|
- lib/llmemory/short_term/stores/active_record_checkpoint.rb
|
|
190
210
|
- lib/llmemory/short_term/stores/active_record_store.rb
|
|
191
211
|
- lib/llmemory/short_term/stores/base.rb
|
|
@@ -199,6 +219,7 @@ files:
|
|
|
199
219
|
- lib/llmemory/vector_store/memory_store.rb
|
|
200
220
|
- lib/llmemory/vector_store/openai_embeddings.rb
|
|
201
221
|
- lib/llmemory/version.rb
|
|
222
|
+
- lib/tasks/release.rake
|
|
202
223
|
homepage: https://github.com/entaina/llmemory
|
|
203
224
|
licenses:
|
|
204
225
|
- MIT
|