llmemory 0.1.11 → 0.1.13
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 +19 -0
- data/lib/llmemory/cli/commands/mcp.rb +11 -0
- data/lib/llmemory/configuration.rb +3 -1
- data/lib/llmemory/mcp.rb +23 -2
- data/lib/llmemory/memory.rb +65 -0
- data/lib/llmemory/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a188251e04aac90f929fcc952f7902a8e9b86728742ab88e5f209953821efd30
|
|
4
|
+
data.tar.gz: 01f3cd9d68c50a1a52ed33683b973a379073890db1ae5a3073676fd598581eca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e24d411ffaca985dc360bc6a6d271f8fd57c687cef26c8242e047fab6bcda400b3764b78aeea8d58ec253bc33ce48b89a46d6a8bdb71db8152c4d175880f3b87
|
|
7
|
+
data.tar.gz: d7097e4c6cc8442088f5adb0614015b8d9d0541db8f4843e729c8e94cfefe0faf49969e9abb75381b070386c53623631b937fc72989a1f7cb0aa27b3b45a9171
|
data/README.md
CHANGED
|
@@ -33,6 +33,9 @@ context = memory.retrieve("¿Qué preferencias tiene el usuario?", max_tokens: 2
|
|
|
33
33
|
# Optionally consolidate current conversation into long-term (extract facts)
|
|
34
34
|
memory.consolidate!
|
|
35
35
|
|
|
36
|
+
# Compact short-term memory when it gets too large (summarizes old messages)
|
|
37
|
+
memory.compact!(max_bytes: 8192) # or use config default
|
|
38
|
+
|
|
36
39
|
# Clear session (short-term) while keeping long-term intact
|
|
37
40
|
memory.clear_session!
|
|
38
41
|
```
|
|
@@ -41,6 +44,7 @@ memory.clear_session!
|
|
|
41
44
|
- **`messages`** — Returns the current conversation history.
|
|
42
45
|
- **`retrieve(query, max_tokens: nil)`** — Returns combined context: recent conversation + relevant long-term memories.
|
|
43
46
|
- **`consolidate!`** — Extracts facts from the current conversation and stores them in long-term.
|
|
47
|
+
- **`compact!(max_bytes: nil)`** — Compacts short-term memory by summarizing old messages when byte size exceeds limit. Uses LLM to create a summary, keeping recent messages intact.
|
|
44
48
|
- **`clear_session!`** — Clears short-term only.
|
|
45
49
|
|
|
46
50
|
## Configuration
|
|
@@ -59,6 +63,7 @@ Llmemory.configure do |config|
|
|
|
59
63
|
config.time_decay_half_life_days = 30
|
|
60
64
|
config.max_retrieval_tokens = 2000
|
|
61
65
|
config.prune_after_days = 90
|
|
66
|
+
config.compact_max_bytes = 8192 # max bytes before compact! triggers
|
|
62
67
|
end
|
|
63
68
|
```
|
|
64
69
|
|
|
@@ -263,6 +268,20 @@ The dashboard uses your existing `Llmemory.configuration` (short-term store, lon
|
|
|
263
268
|
|
|
264
269
|
llmemory includes an MCP server that allows LLM agents (like Claude Code) to interact directly with the memory system. This gives agents "agency" over their own memory—they can search, save, and retrieve memories autonomously.
|
|
265
270
|
|
|
271
|
+
### Installation
|
|
272
|
+
|
|
273
|
+
The MCP server requires the `mcp` gem, which is **optional**. Install it separately:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
gem install mcp
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Or add to your Gemfile:
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
gem "mcp", "~> 0.6"
|
|
283
|
+
```
|
|
284
|
+
|
|
266
285
|
### Starting the Server
|
|
267
286
|
|
|
268
287
|
**Stdio mode** (default, for local use with Claude Code):
|
|
@@ -88,6 +88,17 @@ module Llmemory
|
|
|
88
88
|
def run_server
|
|
89
89
|
require_relative "../../mcp"
|
|
90
90
|
|
|
91
|
+
unless Llmemory::MCP.available?
|
|
92
|
+
$stderr.puts "Error: The 'mcp' gem is required for MCP server functionality."
|
|
93
|
+
$stderr.puts ""
|
|
94
|
+
$stderr.puts "Install it with:"
|
|
95
|
+
$stderr.puts " gem install mcp"
|
|
96
|
+
$stderr.puts ""
|
|
97
|
+
$stderr.puts "Or add to your Gemfile:"
|
|
98
|
+
$stderr.puts " gem 'mcp', '~> 0.6'"
|
|
99
|
+
exit 1
|
|
100
|
+
end
|
|
101
|
+
|
|
91
102
|
server = Llmemory::MCP::Server.new(name: @server_name)
|
|
92
103
|
|
|
93
104
|
if @http_mode
|
|
@@ -15,7 +15,8 @@ module Llmemory
|
|
|
15
15
|
:vector_store,
|
|
16
16
|
:time_decay_half_life_days,
|
|
17
17
|
:max_retrieval_tokens,
|
|
18
|
-
:prune_after_days
|
|
18
|
+
:prune_after_days,
|
|
19
|
+
:compact_max_bytes
|
|
19
20
|
|
|
20
21
|
def initialize
|
|
21
22
|
@llm_provider = :openai
|
|
@@ -32,6 +33,7 @@ module Llmemory
|
|
|
32
33
|
@time_decay_half_life_days = 30
|
|
33
34
|
@max_retrieval_tokens = 2000
|
|
34
35
|
@prune_after_days = 90
|
|
36
|
+
@compact_max_bytes = 8192
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
|
data/lib/llmemory/mcp.rb
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "mcp/server"
|
|
4
|
-
|
|
5
3
|
module Llmemory
|
|
6
4
|
module MCP
|
|
5
|
+
class << self
|
|
6
|
+
def available?
|
|
7
|
+
@available
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def require_mcp!
|
|
11
|
+
unless available?
|
|
12
|
+
raise LoadError, <<~MSG
|
|
13
|
+
The 'mcp' gem is required for MCP server functionality.
|
|
14
|
+
Install it with: gem install mcp
|
|
15
|
+
Or add to your Gemfile: gem 'mcp', '~> 0.6'
|
|
16
|
+
MSG
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
require "mcp"
|
|
23
|
+
@available = true
|
|
24
|
+
require_relative "mcp/server"
|
|
25
|
+
rescue LoadError
|
|
26
|
+
@available = false
|
|
27
|
+
end
|
|
7
28
|
end
|
|
8
29
|
end
|
data/lib/llmemory/memory.rb
CHANGED
|
@@ -52,12 +52,77 @@ module Llmemory
|
|
|
52
52
|
true
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
def compact!(max_bytes: nil)
|
|
56
|
+
max = max_bytes || Llmemory.configuration.compact_max_bytes
|
|
57
|
+
msgs = messages
|
|
58
|
+
current_bytes = messages_byte_size(msgs)
|
|
59
|
+
return false if current_bytes <= max
|
|
60
|
+
|
|
61
|
+
old_msgs, recent_msgs = split_messages_by_bytes(msgs, max)
|
|
62
|
+
return false if old_msgs.empty?
|
|
63
|
+
|
|
64
|
+
summary = summarize_messages(old_msgs)
|
|
65
|
+
compacted = [{ role: :system, content: summary }] + recent_msgs
|
|
66
|
+
save_state(messages: compacted)
|
|
67
|
+
true
|
|
68
|
+
end
|
|
69
|
+
|
|
55
70
|
def user_id
|
|
56
71
|
@user_id
|
|
57
72
|
end
|
|
58
73
|
|
|
59
74
|
private
|
|
60
75
|
|
|
76
|
+
def summarize_messages(msgs)
|
|
77
|
+
conversation = msgs.map { |m| format_message(m) }.join("\n")
|
|
78
|
+
prompt = <<~PROMPT
|
|
79
|
+
Summarize the following conversation into a concise summary that preserves key information, decisions, and context. Write it as a brief narrative (max 200 words).
|
|
80
|
+
|
|
81
|
+
Conversation:
|
|
82
|
+
#{conversation}
|
|
83
|
+
|
|
84
|
+
Summary:
|
|
85
|
+
PROMPT
|
|
86
|
+
llm_client.invoke(prompt.strip).to_s.strip
|
|
87
|
+
rescue Llmemory::LLMError
|
|
88
|
+
msgs.map { |m| format_message(m) }.join("\n")[0..500]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def llm_client
|
|
92
|
+
@llm ||= Llmemory::LLM.client
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def messages_byte_size(msgs)
|
|
96
|
+
msgs.sum { |m| message_byte_size(m) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def message_byte_size(msg)
|
|
100
|
+
role = msg[:role] || msg["role"]
|
|
101
|
+
content = msg[:content] || msg["content"]
|
|
102
|
+
role.to_s.bytesize + content.to_s.bytesize
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def split_messages_by_bytes(msgs, max_bytes)
|
|
106
|
+
target_recent_bytes = max_bytes / 2
|
|
107
|
+
recent_bytes = 0
|
|
108
|
+
split_index = msgs.size
|
|
109
|
+
|
|
110
|
+
(msgs.size - 1).downto(0) do |i|
|
|
111
|
+
msg_bytes = message_byte_size(msgs[i])
|
|
112
|
+
if recent_bytes + msg_bytes <= target_recent_bytes
|
|
113
|
+
recent_bytes += msg_bytes
|
|
114
|
+
split_index = i
|
|
115
|
+
else
|
|
116
|
+
break
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
split_index = [split_index, msgs.size - 1].min
|
|
121
|
+
split_index = [split_index, 1].max if msgs.size > 1
|
|
122
|
+
|
|
123
|
+
[msgs[0...split_index], msgs[split_index..]]
|
|
124
|
+
end
|
|
125
|
+
|
|
61
126
|
def build_long_term(long_term_type)
|
|
62
127
|
llm_opts = @llm ? { llm: @llm } : {}
|
|
63
128
|
case long_term_type.to_s.to_sym
|
data/lib/llmemory/version.rb
CHANGED
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.13
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- llmemory
|
|
@@ -30,7 +30,7 @@ dependencies:
|
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '0.6'
|
|
33
|
-
type: :
|
|
33
|
+
type: :development
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|