llmemory 0.1.1 → 0.1.8
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 +48 -1
- data/exe/llmemory +8 -0
- data/lib/llmemory/cli/commands/base.rb +76 -0
- data/lib/llmemory/cli/commands/long_term/categories.rb +34 -0
- data/lib/llmemory/cli/commands/long_term/edges.rb +43 -0
- data/lib/llmemory/cli/commands/long_term/facts.rb +42 -0
- data/lib/llmemory/cli/commands/long_term/graph.rb +76 -0
- data/lib/llmemory/cli/commands/long_term/nodes.rb +42 -0
- data/lib/llmemory/cli/commands/long_term/resources.rb +42 -0
- data/lib/llmemory/cli/commands/long_term.rb +19 -0
- data/lib/llmemory/cli/commands/search.rb +86 -0
- data/lib/llmemory/cli/commands/short_term.rb +59 -0
- data/lib/llmemory/cli/commands/stats.rb +70 -0
- data/lib/llmemory/cli/commands/users.rb +27 -0
- data/lib/llmemory/cli.rb +76 -0
- data/lib/llmemory/dashboard/app/controllers/llmemory/dashboard/application_controller.rb +83 -0
- data/lib/llmemory/dashboard/app/controllers/llmemory/dashboard/graph_controller.rb +18 -0
- data/lib/llmemory/dashboard/app/controllers/llmemory/dashboard/long_term_controller.rb +31 -0
- data/lib/llmemory/dashboard/app/controllers/llmemory/dashboard/search_controller.rb +58 -0
- data/lib/llmemory/dashboard/app/controllers/llmemory/dashboard/short_term_controller.rb +15 -0
- data/lib/llmemory/dashboard/app/controllers/llmemory/dashboard/stats_controller.rb +21 -0
- data/lib/llmemory/dashboard/app/controllers/llmemory/dashboard/users_controller.rb +27 -0
- data/lib/llmemory/dashboard/app/views/layouts/application.html.erb +53 -0
- data/lib/llmemory/dashboard/app/views/llmemory/dashboard/graph/index.html.erb +41 -0
- data/lib/llmemory/dashboard/app/views/llmemory/dashboard/long_term/categories.html.erb +12 -0
- data/lib/llmemory/dashboard/app/views/llmemory/dashboard/long_term/index.html.erb +42 -0
- data/lib/llmemory/dashboard/app/views/llmemory/dashboard/search/index.html.erb +43 -0
- data/lib/llmemory/dashboard/app/views/llmemory/dashboard/short_term/show.html.erb +25 -0
- data/lib/llmemory/dashboard/app/views/llmemory/dashboard/stats/index.html.erb +32 -0
- data/lib/llmemory/dashboard/app/views/llmemory/dashboard/users/index.html.erb +30 -0
- data/lib/llmemory/dashboard/app/views/llmemory/dashboard/users/show.html.erb +14 -0
- data/lib/llmemory/dashboard/config/routes.rb +12 -0
- data/lib/llmemory/dashboard/engine.rb +9 -0
- data/lib/llmemory/dashboard.rb +8 -0
- data/lib/llmemory/llm.rb +4 -3
- data/lib/llmemory/long_term/file_based/storages/active_record_storage.rb +24 -0
- data/lib/llmemory/long_term/file_based/storages/base.rb +16 -0
- data/lib/llmemory/long_term/file_based/storages/database_storage.rb +35 -0
- data/lib/llmemory/long_term/file_based/storages/file_storage.rb +21 -0
- data/lib/llmemory/long_term/file_based/storages/memory_storage.rb +23 -0
- data/lib/llmemory/long_term/graph_based/conflict_resolver.rb +2 -2
- data/lib/llmemory/long_term/graph_based/edge.rb +3 -3
- data/lib/llmemory/long_term/graph_based/knowledge_graph.rb +2 -2
- data/lib/llmemory/long_term/graph_based/memory.rb +2 -2
- data/lib/llmemory/long_term/graph_based/storages/active_record_storage.rb +28 -5
- data/lib/llmemory/long_term/graph_based/storages/base.rb +17 -1
- data/lib/llmemory/long_term/graph_based/storages/memory_storage.rb +24 -5
- data/lib/llmemory/memory.rb +7 -4
- data/lib/llmemory/short_term/stores/active_record_store.rb +8 -0
- data/lib/llmemory/short_term/stores/base.rb +8 -0
- data/lib/llmemory/short_term/stores/memory_store.rb +9 -0
- data/lib/llmemory/short_term/stores/postgres_store.rb +15 -0
- data/lib/llmemory/short_term/stores/redis_store.rb +10 -0
- data/lib/llmemory/version.rb +1 -1
- metadata +39 -8
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Llmemory::Dashboard::Engine.routes.draw do
|
|
4
|
+
root to: "users#index"
|
|
5
|
+
get "u/:user_id", to: "users#show", as: :user
|
|
6
|
+
get "u/:user_id/short_term", to: "short_term#show", as: :user_short_term
|
|
7
|
+
get "u/:user_id/long_term", to: "long_term#index", as: :user_long_term
|
|
8
|
+
get "u/:user_id/long_term/categories", to: "long_term#categories", as: :user_long_term_categories
|
|
9
|
+
get "u/:user_id/graph", to: "graph#index", as: :user_graph
|
|
10
|
+
get "u/:user_id/stats", to: "stats#index", as: :user_stats
|
|
11
|
+
get "search", to: "search#index", as: :search
|
|
12
|
+
end
|
data/lib/llmemory/llm.rb
CHANGED
|
@@ -6,10 +6,11 @@ require_relative "llm/anthropic"
|
|
|
6
6
|
|
|
7
7
|
module Llmemory
|
|
8
8
|
module LLM
|
|
9
|
-
def self.client
|
|
9
|
+
def self.client(api_key: nil)
|
|
10
|
+
opts = api_key.to_s.empty? ? {} : { api_key: api_key }
|
|
10
11
|
case Llmemory.configuration.llm_provider.to_sym
|
|
11
|
-
when :openai then OpenAI.new
|
|
12
|
-
when :anthropic then Anthropic.new
|
|
12
|
+
when :openai then OpenAI.new(**opts)
|
|
13
|
+
when :anthropic then Anthropic.new(**opts)
|
|
13
14
|
else
|
|
14
15
|
raise Llmemory::ConfigurationError, "Unknown LLM provider: #{Llmemory.configuration.llm_provider}"
|
|
15
16
|
end
|
|
@@ -114,6 +114,30 @@ module Llmemory
|
|
|
114
114
|
LlmemoryResource.where(user_id: user_id, id: resource_ids).destroy_all
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
+
def list_users
|
|
118
|
+
(LlmemoryResource.distinct.pluck(:user_id) +
|
|
119
|
+
LlmemoryItem.distinct.pluck(:user_id) +
|
|
120
|
+
LlmemoryCategory.distinct.pluck(:user_id)).uniq
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def list_resources(user_id:, limit: nil)
|
|
124
|
+
scope = LlmemoryResource.where(user_id: user_id).order(:created_at)
|
|
125
|
+
scope = scope.limit(limit) if limit && limit.to_i.positive?
|
|
126
|
+
scope.map { |r| row_to_resource(r) }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def list_items(user_id:, category: nil, limit: nil)
|
|
130
|
+
scope = LlmemoryItem.where(user_id: user_id)
|
|
131
|
+
scope = scope.where(category: category) if category
|
|
132
|
+
scope = scope.order(:created_at)
|
|
133
|
+
scope = scope.limit(limit) if limit && limit.to_i.positive?
|
|
134
|
+
scope.map { |r| row_to_item(r) }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def count_items(user_id:)
|
|
138
|
+
LlmemoryItem.where(user_id: user_id).count
|
|
139
|
+
end
|
|
140
|
+
|
|
117
141
|
private
|
|
118
142
|
|
|
119
143
|
def sanitize_like(str)
|
|
@@ -64,6 +64,22 @@ module Llmemory
|
|
|
64
64
|
def archive_resources(user_id, resource_ids)
|
|
65
65
|
raise NotImplementedError, "#{self.class}#archive_resources must be implemented"
|
|
66
66
|
end
|
|
67
|
+
|
|
68
|
+
def list_users
|
|
69
|
+
raise NotImplementedError, "#{self.class}#list_users must be implemented"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def list_resources(user_id:, limit: nil)
|
|
73
|
+
raise NotImplementedError, "#{self.class}#list_resources must be implemented"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def list_items(user_id:, category: nil, limit: nil)
|
|
77
|
+
raise NotImplementedError, "#{self.class}#list_items must be implemented"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def count_items(user_id:)
|
|
81
|
+
raise NotImplementedError, "#{self.class}#count_items must be implemented"
|
|
82
|
+
end
|
|
67
83
|
end
|
|
68
84
|
end
|
|
69
85
|
end
|
|
@@ -162,6 +162,41 @@ module Llmemory
|
|
|
162
162
|
resource_ids.each { |id| conn.exec_params("DELETE FROM llmemory_resources WHERE user_id = $1 AND id = $2", [user_id, id]) }
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
+
def list_users
|
|
166
|
+
ensure_tables!
|
|
167
|
+
(conn.exec("SELECT DISTINCT user_id FROM llmemory_resources").map { |r| r["user_id"] } +
|
|
168
|
+
conn.exec("SELECT DISTINCT user_id FROM llmemory_items").map { |r| r["user_id"] } +
|
|
169
|
+
conn.exec("SELECT DISTINCT user_id FROM llmemory_categories").map { |r| r["user_id"] }).uniq
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def list_resources(user_id:, limit: nil)
|
|
173
|
+
ensure_tables!
|
|
174
|
+
sql = "SELECT id, text, created_at FROM llmemory_resources WHERE user_id = $1 ORDER BY created_at"
|
|
175
|
+
sql += " LIMIT #{limit.to_i}" if limit && limit.to_i.positive?
|
|
176
|
+
rows = conn.exec_params(sql, [user_id])
|
|
177
|
+
rows_to_resources(rows)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def list_items(user_id:, category: nil, limit: nil)
|
|
181
|
+
ensure_tables!
|
|
182
|
+
sql = "SELECT id, category, content, source_resource_id, created_at FROM llmemory_items WHERE user_id = $1"
|
|
183
|
+
params = [user_id]
|
|
184
|
+
if category
|
|
185
|
+
sql += " AND category = $2"
|
|
186
|
+
params << category
|
|
187
|
+
end
|
|
188
|
+
sql += " ORDER BY created_at"
|
|
189
|
+
sql += " LIMIT #{limit.to_i}" if limit && limit.to_i.positive?
|
|
190
|
+
rows = params.size == 1 ? conn.exec_params(sql, params) : conn.exec_params(sql, params)
|
|
191
|
+
rows_to_items(rows)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def count_items(user_id:)
|
|
195
|
+
ensure_tables!
|
|
196
|
+
result = conn.exec_params("SELECT COUNT(*) AS c FROM llmemory_items WHERE user_id = $1", [user_id])
|
|
197
|
+
result.first["c"].to_i
|
|
198
|
+
end
|
|
199
|
+
|
|
165
200
|
private
|
|
166
201
|
|
|
167
202
|
def conn
|
|
@@ -124,6 +124,27 @@ module Llmemory
|
|
|
124
124
|
resource_ids.each { |id| File.delete(resource_path(user_id, id)) if File.file?(resource_path(user_id, id)) }
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
+
def list_users
|
|
128
|
+
return [] unless Dir.exist?(@base_path)
|
|
129
|
+
Dir.children(@base_path).select { |e| File.directory?(File.join(@base_path, e)) && !e.start_with?(".") }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def list_resources(user_id:, limit: nil)
|
|
133
|
+
list = get_all_resources(user_id)
|
|
134
|
+
limit ? list.take(limit) : list
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def list_items(user_id:, category: nil, limit: nil)
|
|
138
|
+
list = get_all_items(user_id)
|
|
139
|
+
list = list.select { |i| (i[:category] || i["category"]).to_s == category.to_s } if category
|
|
140
|
+
list = list.take(limit) if limit
|
|
141
|
+
list
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def count_items(user_id:)
|
|
145
|
+
get_all_items(user_id).size
|
|
146
|
+
end
|
|
147
|
+
|
|
127
148
|
private
|
|
128
149
|
|
|
129
150
|
def user_path(user_id, *parts)
|
|
@@ -93,6 +93,29 @@ module Llmemory
|
|
|
93
93
|
def archive_resources(user_id, resource_ids)
|
|
94
94
|
@resources[user_id].reject! { |r| resource_ids.include?(r[:id]) }
|
|
95
95
|
end
|
|
96
|
+
|
|
97
|
+
def list_users
|
|
98
|
+
(@resources.keys + @items.keys + @categories.keys).uniq
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def list_resources(user_id:, limit: nil)
|
|
102
|
+
list = @resources[user_id].dup
|
|
103
|
+
limit ? list.take(limit) : list
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def list_items(user_id:, category: nil, limit: nil)
|
|
107
|
+
list = if category
|
|
108
|
+
@items[user_id].select { |i| i[:category].to_s == category.to_s }
|
|
109
|
+
else
|
|
110
|
+
@items[user_id].dup
|
|
111
|
+
end
|
|
112
|
+
list = list.take(limit) if limit
|
|
113
|
+
list
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def count_items(user_id:)
|
|
117
|
+
@items[user_id].size
|
|
118
|
+
end
|
|
96
119
|
end
|
|
97
120
|
end
|
|
98
121
|
end
|
|
@@ -17,9 +17,9 @@ module Llmemory
|
|
|
17
17
|
|
|
18
18
|
subject_id = new_edge.subject_id
|
|
19
19
|
existing = @graph.find_edges(subject: subject_id, predicate: new_edge.predicate, include_archived: false)
|
|
20
|
-
to_archive = existing.reject { |e| e.
|
|
20
|
+
to_archive = existing.reject { |e| e.target_id == new_edge.target_id }
|
|
21
21
|
to_archive.each do |e|
|
|
22
|
-
@graph.archive_edge(e.id, reason: "replaced by #{new_edge.
|
|
22
|
+
@graph.archive_edge(e.id, reason: "replaced by #{new_edge.target_id}")
|
|
23
23
|
end
|
|
24
24
|
to_archive.map(&:id)
|
|
25
25
|
end
|
|
@@ -8,7 +8,7 @@ module Llmemory
|
|
|
8
8
|
:user_id,
|
|
9
9
|
:subject_id,
|
|
10
10
|
:predicate,
|
|
11
|
-
:
|
|
11
|
+
:target_id,
|
|
12
12
|
:properties,
|
|
13
13
|
:created_at,
|
|
14
14
|
:archived_at,
|
|
@@ -20,7 +20,7 @@ module Llmemory
|
|
|
20
20
|
user_id: hash[:user_id] || hash["user_id"],
|
|
21
21
|
subject_id: hash[:subject_id] || hash["subject_id"],
|
|
22
22
|
predicate: (hash[:predicate] || hash["predicate"]).to_s,
|
|
23
|
-
|
|
23
|
+
target_id: hash[:object_id] || hash["object_id"],
|
|
24
24
|
properties: hash[:properties] || hash["properties"] || {},
|
|
25
25
|
created_at: hash[:created_at] || hash["created_at"],
|
|
26
26
|
archived_at: hash[:archived_at] || hash["archived_at"]
|
|
@@ -37,7 +37,7 @@ module Llmemory
|
|
|
37
37
|
user_id: user_id,
|
|
38
38
|
subject_id: subject_id,
|
|
39
39
|
predicate: predicate,
|
|
40
|
-
object_id:
|
|
40
|
+
object_id: target_id,
|
|
41
41
|
properties: properties || {},
|
|
42
42
|
created_at: created_at,
|
|
43
43
|
archived_at: archived_at
|
|
@@ -49,7 +49,7 @@ module Llmemory
|
|
|
49
49
|
user_id: @user_id,
|
|
50
50
|
subject_id: subject_id,
|
|
51
51
|
predicate: predicate.to_s,
|
|
52
|
-
|
|
52
|
+
target_id: object_id,
|
|
53
53
|
properties: properties,
|
|
54
54
|
created_at: Time.now,
|
|
55
55
|
archived_at: nil
|
|
@@ -87,7 +87,7 @@ module Llmemory
|
|
|
87
87
|
edges = @storage.find_edges(@user_id, subject_id: node_id, include_archived: false)
|
|
88
88
|
edges.each do |e|
|
|
89
89
|
result_edges << e
|
|
90
|
-
queue << [e.
|
|
90
|
+
queue << [e.target_id, d + 1] unless visited[e.target_id]
|
|
91
91
|
end
|
|
92
92
|
edges_out = @storage.find_edges(@user_id, object_id: node_id, include_archived: false)
|
|
93
93
|
edges_out.each do |e|
|
|
@@ -38,7 +38,7 @@ module Llmemory
|
|
|
38
38
|
user_id: @user_id,
|
|
39
39
|
subject_id: subject_id,
|
|
40
40
|
predicate: r[:predicate],
|
|
41
|
-
|
|
41
|
+
target_id: object_id,
|
|
42
42
|
properties: {},
|
|
43
43
|
created_at: Time.now,
|
|
44
44
|
archived_at: nil
|
|
@@ -109,7 +109,7 @@ module Llmemory
|
|
|
109
109
|
traversed = @kg.traverse(start_node: node, depth: 1)
|
|
110
110
|
traversed[:edges].each do |e|
|
|
111
111
|
subj = @kg.find_node_by_id(e.subject_id)
|
|
112
|
-
obj = @kg.find_node_by_id(e.
|
|
112
|
+
obj = @kg.find_node_by_id(e.target_id)
|
|
113
113
|
edge_text = "#{subj&.name} #{e.predicate} #{obj&.name}"
|
|
114
114
|
out << { text: edge_text, score: 0.85, created_at: e.created_at } unless out.any? { |o| o[:text] == edge_text }
|
|
115
115
|
end
|
|
@@ -55,8 +55,11 @@ module Llmemory
|
|
|
55
55
|
record_to_node(rec) if rec
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def list_nodes(user_id)
|
|
59
|
-
LlmemoryGraphNode.where(user_id: user_id)
|
|
58
|
+
def list_nodes(user_id, entity_type: nil, limit: nil)
|
|
59
|
+
scope = LlmemoryGraphNode.where(user_id: user_id)
|
|
60
|
+
scope = scope.where(entity_type: entity_type) if entity_type
|
|
61
|
+
scope = scope.limit(limit) if limit && limit.to_i.positive?
|
|
62
|
+
scope.map { |r| record_to_node(r) }
|
|
60
63
|
end
|
|
61
64
|
|
|
62
65
|
def save_edge(user_id, edge)
|
|
@@ -67,7 +70,7 @@ module Llmemory
|
|
|
67
70
|
rec.update!(
|
|
68
71
|
subject_id: e.subject_id,
|
|
69
72
|
predicate: e.predicate,
|
|
70
|
-
object_id: e.
|
|
73
|
+
object_id: e.target_id,
|
|
71
74
|
properties: e.properties || {}
|
|
72
75
|
)
|
|
73
76
|
else
|
|
@@ -76,7 +79,7 @@ module Llmemory
|
|
|
76
79
|
user_id: user_id,
|
|
77
80
|
subject_id: e.subject_id,
|
|
78
81
|
predicate: e.predicate,
|
|
79
|
-
object_id: e.
|
|
82
|
+
object_id: e.target_id,
|
|
80
83
|
properties: e.properties || {}
|
|
81
84
|
)
|
|
82
85
|
end
|
|
@@ -99,6 +102,26 @@ module Llmemory
|
|
|
99
102
|
true
|
|
100
103
|
end
|
|
101
104
|
|
|
105
|
+
def list_users
|
|
106
|
+
(LlmemoryGraphNode.distinct.pluck(:user_id) + LlmemoryGraphEdge.distinct.pluck(:user_id)).uniq
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def list_edges(user_id, subject_id: nil, predicate: nil, limit: nil)
|
|
110
|
+
scope = LlmemoryGraphEdge.where(user_id: user_id, archived_at: nil)
|
|
111
|
+
scope = scope.where(subject_id: subject_id) if subject_id
|
|
112
|
+
scope = scope.where(predicate: predicate) if predicate
|
|
113
|
+
scope = scope.limit(limit) if limit && limit.to_i.positive?
|
|
114
|
+
scope.map { |r| record_to_edge(r) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def count_nodes(user_id)
|
|
118
|
+
LlmemoryGraphNode.where(user_id: user_id).count
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def count_edges(user_id)
|
|
122
|
+
LlmemoryGraphEdge.where(user_id: user_id, archived_at: nil).count
|
|
123
|
+
end
|
|
124
|
+
|
|
102
125
|
private
|
|
103
126
|
|
|
104
127
|
def record_to_node(r)
|
|
@@ -119,7 +142,7 @@ module Llmemory
|
|
|
119
142
|
user_id: r.user_id,
|
|
120
143
|
subject_id: r.subject_id,
|
|
121
144
|
predicate: r.predicate,
|
|
122
|
-
|
|
145
|
+
target_id: r.object_id,
|
|
123
146
|
properties: r.properties || {},
|
|
124
147
|
created_at: r.created_at,
|
|
125
148
|
archived_at: r.archived_at
|
|
@@ -17,7 +17,7 @@ module Llmemory
|
|
|
17
17
|
raise NotImplementedError, "#{self.class}#find_node_by_name must be implemented"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def list_nodes(user_id)
|
|
20
|
+
def list_nodes(user_id, entity_type: nil, limit: nil)
|
|
21
21
|
raise NotImplementedError, "#{self.class}#list_nodes must be implemented"
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -32,6 +32,22 @@ module Llmemory
|
|
|
32
32
|
def archive_edge(user_id, edge_id, archived_at: nil)
|
|
33
33
|
raise NotImplementedError, "#{self.class}#archive_edge must be implemented"
|
|
34
34
|
end
|
|
35
|
+
|
|
36
|
+
def list_users
|
|
37
|
+
raise NotImplementedError, "#{self.class}#list_users must be implemented"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def list_edges(user_id, subject_id: nil, predicate: nil, limit: nil)
|
|
41
|
+
raise NotImplementedError, "#{self.class}#list_edges must be implemented"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def count_nodes(user_id)
|
|
45
|
+
raise NotImplementedError, "#{self.class}#count_nodes must be implemented"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def count_edges(user_id)
|
|
49
|
+
raise NotImplementedError, "#{self.class}#count_edges must be implemented"
|
|
50
|
+
end
|
|
35
51
|
end
|
|
36
52
|
end
|
|
37
53
|
end
|
|
@@ -43,8 +43,10 @@ module Llmemory
|
|
|
43
43
|
@nodes[user_id].values.find { |n| n.entity_type == entity_type.to_s && n.name.to_s == name.to_s }
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
def list_nodes(user_id)
|
|
47
|
-
@nodes[user_id].values
|
|
46
|
+
def list_nodes(user_id, entity_type: nil, limit: nil)
|
|
47
|
+
list = @nodes[user_id].values
|
|
48
|
+
list = list.select { |n| n.entity_type.to_s == entity_type.to_s } if entity_type
|
|
49
|
+
limit ? list.take(limit) : list
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
def save_edge(user_id, edge)
|
|
@@ -56,7 +58,7 @@ module Llmemory
|
|
|
56
58
|
user_id: user_id,
|
|
57
59
|
subject_id: e.subject_id,
|
|
58
60
|
predicate: e.predicate,
|
|
59
|
-
|
|
61
|
+
target_id: e.target_id,
|
|
60
62
|
properties: e.properties || {},
|
|
61
63
|
created_at: e.created_at || Time.now,
|
|
62
64
|
archived_at: nil
|
|
@@ -76,7 +78,7 @@ module Llmemory
|
|
|
76
78
|
next false unless include_archived || !e.archived?
|
|
77
79
|
next false if subject_id && e.subject_id != subject_id
|
|
78
80
|
next false if predicate && e.predicate != predicate.to_s
|
|
79
|
-
next false if object_id && e.
|
|
81
|
+
next false if object_id && e.target_id != object_id
|
|
80
82
|
true
|
|
81
83
|
end
|
|
82
84
|
list
|
|
@@ -92,13 +94,30 @@ module Llmemory
|
|
|
92
94
|
user_id: e.user_id,
|
|
93
95
|
subject_id: e.subject_id,
|
|
94
96
|
predicate: e.predicate,
|
|
95
|
-
|
|
97
|
+
target_id: e.target_id,
|
|
96
98
|
properties: e.properties,
|
|
97
99
|
created_at: e.created_at,
|
|
98
100
|
archived_at: t
|
|
99
101
|
)
|
|
100
102
|
true
|
|
101
103
|
end
|
|
104
|
+
|
|
105
|
+
def list_users
|
|
106
|
+
(@nodes.keys + @edges.keys).uniq
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def list_edges(user_id, subject_id: nil, predicate: nil, limit: nil)
|
|
110
|
+
list = find_edges(user_id, subject_id: subject_id, predicate: predicate, object_id: nil, include_archived: false)
|
|
111
|
+
limit ? list.take(limit) : list
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def count_nodes(user_id)
|
|
115
|
+
@nodes[user_id].size
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def count_edges(user_id)
|
|
119
|
+
@edges[user_id].count { |e| !e.archived? }
|
|
120
|
+
end
|
|
102
121
|
end
|
|
103
122
|
end
|
|
104
123
|
end
|
data/lib/llmemory/memory.rb
CHANGED
|
@@ -9,13 +9,14 @@ module Llmemory
|
|
|
9
9
|
DEFAULT_SESSION_ID = "default"
|
|
10
10
|
STATE_KEY_MESSAGES = :messages
|
|
11
11
|
|
|
12
|
-
def initialize(user_id:, session_id: DEFAULT_SESSION_ID, checkpoint: nil, long_term: nil, long_term_type: nil, retrieval_engine: nil)
|
|
12
|
+
def initialize(user_id:, session_id: DEFAULT_SESSION_ID, checkpoint: nil, long_term: nil, long_term_type: nil, retrieval_engine: nil, api_key: nil)
|
|
13
13
|
@user_id = user_id
|
|
14
14
|
@session_id = session_id
|
|
15
15
|
@checkpoint = checkpoint || ShortTerm::Checkpoint.new(user_id: user_id, session_id: session_id)
|
|
16
|
+
@llm = api_key.to_s.empty? ? nil : Llmemory::LLM.client(api_key: api_key)
|
|
16
17
|
type = long_term_type || Llmemory.configuration.long_term_type || :file_based
|
|
17
18
|
@long_term = long_term || build_long_term(type)
|
|
18
|
-
@retrieval_engine = retrieval_engine || Retrieval::Engine.new(@long_term)
|
|
19
|
+
@retrieval_engine = retrieval_engine || Retrieval::Engine.new(@long_term, llm: @llm)
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def add_message(role:, content:)
|
|
@@ -58,14 +59,16 @@ module Llmemory
|
|
|
58
59
|
private
|
|
59
60
|
|
|
60
61
|
def build_long_term(long_term_type)
|
|
62
|
+
llm_opts = @llm ? { llm: @llm } : {}
|
|
61
63
|
case long_term_type.to_s.to_sym
|
|
62
64
|
when :graph_based
|
|
63
65
|
LongTerm::GraphBased::Memory.new(
|
|
64
66
|
user_id: @user_id,
|
|
65
|
-
storage: LongTerm::GraphBased::Storages.build
|
|
67
|
+
storage: LongTerm::GraphBased::Storages.build,
|
|
68
|
+
**llm_opts
|
|
66
69
|
)
|
|
67
70
|
else
|
|
68
|
-
LongTerm::FileBased::Memory.new(user_id: @user_id, storage: LongTerm::FileBased::Storages.build)
|
|
71
|
+
LongTerm::FileBased::Memory.new(user_id: @user_id, storage: LongTerm::FileBased::Storages.build, **llm_opts)
|
|
69
72
|
end
|
|
70
73
|
end
|
|
71
74
|
|
|
@@ -46,6 +46,14 @@ module Llmemory
|
|
|
46
46
|
true
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
def list_users
|
|
50
|
+
Llmemory::ShortTerm::Stores::ActiveRecordCheckpoint.distinct.pluck(:user_id)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def list_sessions(user_id:)
|
|
54
|
+
Llmemory::ShortTerm::Stores::ActiveRecordCheckpoint.where(user_id: user_id).pluck(:session_id)
|
|
55
|
+
end
|
|
56
|
+
|
|
49
57
|
private
|
|
50
58
|
|
|
51
59
|
def deserialize(data)
|
|
@@ -15,6 +15,14 @@ module Llmemory
|
|
|
15
15
|
def delete(user_id, session_id)
|
|
16
16
|
raise NotImplementedError, "#{self.class}#delete must be implemented"
|
|
17
17
|
end
|
|
18
|
+
|
|
19
|
+
def list_users
|
|
20
|
+
raise NotImplementedError, "#{self.class}#list_users must be implemented"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def list_sessions(user_id:)
|
|
24
|
+
raise NotImplementedError, "#{self.class}#list_sessions must be implemented"
|
|
25
|
+
end
|
|
18
26
|
end
|
|
19
27
|
end
|
|
20
28
|
end
|
|
@@ -26,6 +26,15 @@ module Llmemory
|
|
|
26
26
|
true
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def list_users
|
|
30
|
+
@store.keys.map { |k| k.split(":", 2).first }.uniq
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def list_sessions(user_id:)
|
|
34
|
+
prefix = "#{user_id}:"
|
|
35
|
+
@store.keys.select { |k| k.start_with?(prefix) }.map { |k| k.split(":", 2).last }
|
|
36
|
+
end
|
|
37
|
+
|
|
29
38
|
private
|
|
30
39
|
|
|
31
40
|
def key_for(user_id, session_id)
|
|
@@ -44,6 +44,21 @@ module Llmemory
|
|
|
44
44
|
true
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def list_users
|
|
48
|
+
ensure_table!
|
|
49
|
+
result = conn.exec("SELECT DISTINCT user_id FROM llmemory_checkpoints")
|
|
50
|
+
result.map { |r| r["user_id"] }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def list_sessions(user_id:)
|
|
54
|
+
ensure_table!
|
|
55
|
+
result = conn.exec_params(
|
|
56
|
+
"SELECT session_id FROM llmemory_checkpoints WHERE user_id = $1",
|
|
57
|
+
[user_id]
|
|
58
|
+
)
|
|
59
|
+
result.map { |r| r["session_id"] }
|
|
60
|
+
end
|
|
61
|
+
|
|
47
62
|
private
|
|
48
63
|
|
|
49
64
|
def conn
|
|
@@ -26,6 +26,16 @@ module Llmemory
|
|
|
26
26
|
true
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def list_users
|
|
30
|
+
keys = redis.keys("llmemory:checkpoint:*:*")
|
|
31
|
+
keys.map { |k| k.split(":")[2] }.uniq
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def list_sessions(user_id:)
|
|
35
|
+
keys = redis.keys("llmemory:checkpoint:#{user_id}:*")
|
|
36
|
+
keys.map { |k| k.split(":", 4).last }
|
|
37
|
+
end
|
|
38
|
+
|
|
29
39
|
private
|
|
30
40
|
|
|
31
41
|
def redis
|
data/lib/llmemory/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
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.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- llmemory
|
|
8
|
-
|
|
9
|
-
bindir: bin
|
|
8
|
+
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: faraday
|
|
@@ -70,16 +69,50 @@ description: 'Memory infrastructure for agents: short-term checkpointing, long-t
|
|
|
70
69
|
file-based and graph-based memory, retrieval with time decay, and maintenance jobs.'
|
|
71
70
|
email:
|
|
72
71
|
- ''
|
|
73
|
-
executables:
|
|
72
|
+
executables:
|
|
73
|
+
- llmemory
|
|
74
74
|
extensions: []
|
|
75
75
|
extra_rdoc_files: []
|
|
76
76
|
files:
|
|
77
77
|
- LICENSE.txt
|
|
78
78
|
- README.md
|
|
79
|
+
- exe/llmemory
|
|
79
80
|
- lib/generators/llmemory/install/install_generator.rb
|
|
80
81
|
- lib/generators/llmemory/install/templates/create_llmemory_tables.rb
|
|
81
82
|
- lib/llmemory.rb
|
|
83
|
+
- lib/llmemory/cli.rb
|
|
84
|
+
- lib/llmemory/cli/commands/base.rb
|
|
85
|
+
- lib/llmemory/cli/commands/long_term.rb
|
|
86
|
+
- lib/llmemory/cli/commands/long_term/categories.rb
|
|
87
|
+
- lib/llmemory/cli/commands/long_term/edges.rb
|
|
88
|
+
- lib/llmemory/cli/commands/long_term/facts.rb
|
|
89
|
+
- lib/llmemory/cli/commands/long_term/graph.rb
|
|
90
|
+
- lib/llmemory/cli/commands/long_term/nodes.rb
|
|
91
|
+
- lib/llmemory/cli/commands/long_term/resources.rb
|
|
92
|
+
- lib/llmemory/cli/commands/search.rb
|
|
93
|
+
- lib/llmemory/cli/commands/short_term.rb
|
|
94
|
+
- lib/llmemory/cli/commands/stats.rb
|
|
95
|
+
- lib/llmemory/cli/commands/users.rb
|
|
82
96
|
- lib/llmemory/configuration.rb
|
|
97
|
+
- lib/llmemory/dashboard.rb
|
|
98
|
+
- lib/llmemory/dashboard/app/controllers/llmemory/dashboard/application_controller.rb
|
|
99
|
+
- lib/llmemory/dashboard/app/controllers/llmemory/dashboard/graph_controller.rb
|
|
100
|
+
- lib/llmemory/dashboard/app/controllers/llmemory/dashboard/long_term_controller.rb
|
|
101
|
+
- lib/llmemory/dashboard/app/controllers/llmemory/dashboard/search_controller.rb
|
|
102
|
+
- lib/llmemory/dashboard/app/controllers/llmemory/dashboard/short_term_controller.rb
|
|
103
|
+
- lib/llmemory/dashboard/app/controllers/llmemory/dashboard/stats_controller.rb
|
|
104
|
+
- lib/llmemory/dashboard/app/controllers/llmemory/dashboard/users_controller.rb
|
|
105
|
+
- lib/llmemory/dashboard/app/views/layouts/application.html.erb
|
|
106
|
+
- lib/llmemory/dashboard/app/views/llmemory/dashboard/graph/index.html.erb
|
|
107
|
+
- lib/llmemory/dashboard/app/views/llmemory/dashboard/long_term/categories.html.erb
|
|
108
|
+
- lib/llmemory/dashboard/app/views/llmemory/dashboard/long_term/index.html.erb
|
|
109
|
+
- lib/llmemory/dashboard/app/views/llmemory/dashboard/search/index.html.erb
|
|
110
|
+
- lib/llmemory/dashboard/app/views/llmemory/dashboard/short_term/show.html.erb
|
|
111
|
+
- lib/llmemory/dashboard/app/views/llmemory/dashboard/stats/index.html.erb
|
|
112
|
+
- lib/llmemory/dashboard/app/views/llmemory/dashboard/users/index.html.erb
|
|
113
|
+
- lib/llmemory/dashboard/app/views/llmemory/dashboard/users/show.html.erb
|
|
114
|
+
- lib/llmemory/dashboard/config/routes.rb
|
|
115
|
+
- lib/llmemory/dashboard/engine.rb
|
|
83
116
|
- lib/llmemory/extractors.rb
|
|
84
117
|
- lib/llmemory/extractors/entity_relation_extractor.rb
|
|
85
118
|
- lib/llmemory/extractors/fact_extractor.rb
|
|
@@ -141,7 +174,6 @@ licenses:
|
|
|
141
174
|
metadata:
|
|
142
175
|
homepage_uri: https://github.com/entaina/llmemory
|
|
143
176
|
source_code_uri: https://github.com/entaina/llmemory
|
|
144
|
-
post_install_message:
|
|
145
177
|
rdoc_options: []
|
|
146
178
|
require_paths:
|
|
147
179
|
- lib
|
|
@@ -156,8 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
156
188
|
- !ruby/object:Gem::Version
|
|
157
189
|
version: '0'
|
|
158
190
|
requirements: []
|
|
159
|
-
rubygems_version:
|
|
160
|
-
signing_key:
|
|
191
|
+
rubygems_version: 4.0.3
|
|
161
192
|
specification_version: 4
|
|
162
193
|
summary: Persistent memory system for LLM agents
|
|
163
194
|
test_files: []
|