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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2016468aa862476d2bfc4fafef85e340433b0bf940519351a9fb72f3bda5e796
4
- data.tar.gz: 31d1d72a3ef8b94b6cef5e696ed29f72357377670d484897350cddcdf17a20ae
3
+ metadata.gz: a188251e04aac90f929fcc952f7902a8e9b86728742ab88e5f209953821efd30
4
+ data.tar.gz: 01f3cd9d68c50a1a52ed33683b973a379073890db1ae5a3073676fd598581eca
5
5
  SHA512:
6
- metadata.gz: f360315f571e5641f53af4006f661a9e432f80280d53a2a7f99d7563f318f23b04950925d2bb2e0716bcad6088b5f929fa15d2b25523f7d83af04b0ac269db3b
7
- data.tar.gz: aef3ce481555a0f84630207ff0333fe749d841656dd41c1c6449b4e3797cff73ecd033ce1817ff50d8e87913e783dfed3198eb85ccadfd6683388f9a4e5a8780
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Llmemory
4
- VERSION = "0.1.11"
4
+ VERSION = "0.1.13"
5
5
  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.11
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: :runtime
33
+ type: :development
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements: