activematrix 0.0.5 → 0.0.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 +96 -28
- data/app/jobs/active_matrix/application_job.rb +11 -0
- data/app/models/active_matrix/agent/jobs/memory_reaper.rb +87 -0
- data/app/models/active_matrix/agent.rb +166 -0
- data/app/models/active_matrix/agent_store.rb +80 -0
- data/app/models/active_matrix/application_record.rb +15 -0
- data/app/models/active_matrix/chat_session.rb +105 -0
- data/app/models/active_matrix/knowledge_base.rb +100 -0
- data/exe/activematrix +7 -0
- data/lib/active_matrix/agent_manager.rb +160 -121
- data/lib/active_matrix/agent_registry.rb +25 -21
- data/lib/active_matrix/api.rb +8 -2
- data/lib/active_matrix/async_query.rb +58 -0
- data/lib/active_matrix/bot/base.rb +3 -3
- data/lib/active_matrix/bot/builtin_commands.rb +188 -0
- data/lib/active_matrix/bot/command_parser.rb +175 -0
- data/lib/active_matrix/cli.rb +273 -0
- data/lib/active_matrix/client.rb +21 -6
- data/lib/active_matrix/client_pool.rb +38 -27
- data/lib/active_matrix/daemon/probe_server.rb +118 -0
- data/lib/active_matrix/daemon/signal_handler.rb +156 -0
- data/lib/active_matrix/daemon/worker.rb +109 -0
- data/lib/active_matrix/daemon.rb +236 -0
- data/lib/active_matrix/engine.rb +18 -0
- data/lib/active_matrix/errors.rb +1 -1
- data/lib/active_matrix/event_router.rb +61 -49
- data/lib/active_matrix/events.rb +1 -0
- data/lib/active_matrix/instrumentation.rb +148 -0
- data/lib/active_matrix/memory/agent_memory.rb +7 -21
- data/lib/active_matrix/memory/conversation_memory.rb +4 -20
- data/lib/active_matrix/memory/global_memory.rb +15 -30
- data/lib/active_matrix/message_dispatcher.rb +197 -0
- data/lib/active_matrix/metrics.rb +424 -0
- data/lib/active_matrix/presence_manager.rb +181 -0
- data/lib/active_matrix/railtie.rb +8 -0
- data/lib/active_matrix/telemetry.rb +134 -0
- data/lib/active_matrix/version.rb +1 -1
- data/lib/active_matrix.rb +18 -11
- data/lib/generators/active_matrix/install/install_generator.rb +3 -22
- data/lib/generators/active_matrix/install/templates/README +5 -2
- metadata +191 -31
- data/lib/generators/active_matrix/install/templates/agent_memory.rb +0 -47
- data/lib/generators/active_matrix/install/templates/conversation_context.rb +0 -72
- data/lib/generators/active_matrix/install/templates/create_agent_memories.rb +0 -17
- data/lib/generators/active_matrix/install/templates/create_conversation_contexts.rb +0 -21
- data/lib/generators/active_matrix/install/templates/create_global_memories.rb +0 -20
- data/lib/generators/active_matrix/install/templates/create_matrix_agents.rb +0 -26
- data/lib/generators/active_matrix/install/templates/global_memory.rb +0 -70
- data/lib/generators/active_matrix/install/templates/matrix_agent.rb +0 -127
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'singleton'
|
|
4
|
-
require '
|
|
4
|
+
require 'async'
|
|
5
|
+
require 'async/queue'
|
|
5
6
|
|
|
6
7
|
module ActiveMatrix
|
|
7
|
-
# Routes Matrix events to appropriate agents
|
|
8
|
+
# Routes Matrix events to appropriate agents using async fibers
|
|
8
9
|
class EventRouter
|
|
9
10
|
include Singleton
|
|
10
11
|
include ActiveMatrix::Logging
|
|
11
12
|
|
|
12
13
|
def initialize
|
|
13
|
-
@routes =
|
|
14
|
-
@
|
|
14
|
+
@routes = []
|
|
15
|
+
@mutex = Mutex.new
|
|
16
|
+
@event_queue = nil
|
|
15
17
|
@processing = false
|
|
16
|
-
@
|
|
18
|
+
@worker_task = nil
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
# Register an event route
|
|
@@ -28,8 +30,10 @@ module ActiveMatrix
|
|
|
28
30
|
handler: block
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
@
|
|
32
|
-
|
|
33
|
+
@mutex.synchronize do
|
|
34
|
+
@routes << route
|
|
35
|
+
@routes.sort_by! { |r| -r[:priority] } # Higher priority first
|
|
36
|
+
end
|
|
33
37
|
|
|
34
38
|
logger.debug "Registered route: #{route.except(:handler).inspect}"
|
|
35
39
|
route[:id]
|
|
@@ -37,29 +41,35 @@ module ActiveMatrix
|
|
|
37
41
|
|
|
38
42
|
# Unregister a route
|
|
39
43
|
def unregister_route(route_id)
|
|
40
|
-
@
|
|
44
|
+
@mutex.synchronize do
|
|
45
|
+
@routes.delete_if { |route| route[:id] == route_id }
|
|
46
|
+
end
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
# Clear all routes for an agent
|
|
44
50
|
def clear_agent_routes(agent_id)
|
|
45
|
-
@
|
|
51
|
+
@mutex.synchronize do
|
|
52
|
+
@routes.delete_if { |route| route[:agent_id] == agent_id }
|
|
53
|
+
end
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
# Route an event to appropriate agents
|
|
49
57
|
def route_event(event)
|
|
50
|
-
return unless @processing
|
|
58
|
+
return unless @processing && @event_queue
|
|
51
59
|
|
|
52
60
|
# Queue the event for processing
|
|
53
|
-
@event_queue
|
|
61
|
+
@event_queue.enqueue(event)
|
|
54
62
|
end
|
|
55
63
|
|
|
56
|
-
# Start the event router
|
|
64
|
+
# Start the event router (call from within async context)
|
|
57
65
|
def start
|
|
58
66
|
return if @processing
|
|
59
67
|
|
|
60
68
|
@processing = true
|
|
61
|
-
@
|
|
62
|
-
|
|
69
|
+
@event_queue = Async::Queue.new
|
|
70
|
+
|
|
71
|
+
@worker_task = Async(transient: true) do |task|
|
|
72
|
+
task.annotate 'event-router'
|
|
63
73
|
process_events
|
|
64
74
|
end
|
|
65
75
|
|
|
@@ -69,20 +79,22 @@ module ActiveMatrix
|
|
|
69
79
|
# Stop the event router
|
|
70
80
|
def stop
|
|
71
81
|
@processing = false
|
|
72
|
-
@
|
|
73
|
-
@event_queue
|
|
82
|
+
@worker_task&.stop
|
|
83
|
+
@event_queue = nil
|
|
74
84
|
|
|
75
85
|
logger.info 'Event router stopped'
|
|
76
86
|
end
|
|
77
87
|
|
|
78
88
|
# Check if router is running
|
|
79
89
|
def running?
|
|
80
|
-
@processing && @
|
|
90
|
+
@processing && @worker_task&.alive?
|
|
81
91
|
end
|
|
82
92
|
|
|
83
93
|
# Get routes for debugging
|
|
84
94
|
def routes_summary
|
|
85
|
-
@
|
|
95
|
+
@mutex.synchronize do
|
|
96
|
+
@routes.map { |r| r.except(:handler) }
|
|
97
|
+
end
|
|
86
98
|
end
|
|
87
99
|
|
|
88
100
|
# Broadcast an event to all agents
|
|
@@ -99,9 +111,7 @@ module ActiveMatrix
|
|
|
99
111
|
def process_events
|
|
100
112
|
while @processing
|
|
101
113
|
begin
|
|
102
|
-
|
|
103
|
-
event = nil
|
|
104
|
-
Timeout.timeout(1) { event = @event_queue.pop }
|
|
114
|
+
event = @event_queue.dequeue
|
|
105
115
|
|
|
106
116
|
next unless event
|
|
107
117
|
|
|
@@ -113,36 +123,40 @@ module ActiveMatrix
|
|
|
113
123
|
next
|
|
114
124
|
end
|
|
115
125
|
|
|
116
|
-
# Process routes in priority order
|
|
126
|
+
# Process routes in priority order (each in its own fiber)
|
|
117
127
|
matching_routes.each do |route|
|
|
118
|
-
|
|
128
|
+
Async do
|
|
129
|
+
process_route(route, event)
|
|
130
|
+
end
|
|
119
131
|
end
|
|
120
|
-
rescue
|
|
121
|
-
|
|
132
|
+
rescue Async::Stop
|
|
133
|
+
break
|
|
122
134
|
rescue StandardError => e
|
|
123
135
|
logger.error "Event router error: #{e.message}"
|
|
124
|
-
logger.error e.backtrace.join("\n")
|
|
136
|
+
logger.error e.backtrace.first(10).join("\n")
|
|
125
137
|
end
|
|
126
138
|
end
|
|
127
139
|
end
|
|
128
140
|
|
|
129
141
|
def find_matching_routes(event)
|
|
130
|
-
@
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
@mutex.synchronize do
|
|
143
|
+
@routes.select do |route|
|
|
144
|
+
# Check room match
|
|
145
|
+
next false if route[:room_id] && route[:room_id] != event[:room_id]
|
|
133
146
|
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
# Check event type match
|
|
148
|
+
next false if route[:event_type] && route[:event_type] != event[:type]
|
|
136
149
|
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
# Check user match
|
|
151
|
+
next false if route[:user_id] && route[:user_id] != event[:sender]
|
|
139
152
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
153
|
+
# Check if agent is running
|
|
154
|
+
registry = AgentRegistry.instance
|
|
155
|
+
agent_entry = registry.get(route[:agent_id])
|
|
156
|
+
next false unless agent_entry
|
|
144
157
|
|
|
145
|
-
|
|
158
|
+
true
|
|
159
|
+
end
|
|
146
160
|
end
|
|
147
161
|
end
|
|
148
162
|
|
|
@@ -154,18 +168,16 @@ module ActiveMatrix
|
|
|
154
168
|
|
|
155
169
|
bot = agent_entry[:instance]
|
|
156
170
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
bot._handle_event(event)
|
|
164
|
-
end
|
|
165
|
-
rescue StandardError => e
|
|
166
|
-
logger.error "Error processing route for agent #{agent_entry[:record].name}: #{e.message}"
|
|
167
|
-
logger.error e.backtrace.first(5).join("\n")
|
|
171
|
+
if route[:handler]
|
|
172
|
+
# Custom handler
|
|
173
|
+
route[:handler].call(bot, event)
|
|
174
|
+
elsif bot.respond_to?(:_handle_event)
|
|
175
|
+
# Default handling
|
|
176
|
+
bot._handle_event(event)
|
|
168
177
|
end
|
|
178
|
+
rescue StandardError => e
|
|
179
|
+
logger.error "Error processing route for agent #{agent_entry[:record].name}: #{e.message}"
|
|
180
|
+
logger.error e.backtrace.first(5).join("\n")
|
|
169
181
|
end
|
|
170
182
|
end
|
|
171
183
|
|
data/lib/active_matrix/events.rb
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/concern'
|
|
4
|
+
require 'active_support/notifications'
|
|
5
|
+
require 'timeout'
|
|
6
|
+
require 'socket'
|
|
7
|
+
require 'json'
|
|
8
|
+
|
|
9
|
+
module ActiveMatrix
|
|
10
|
+
# Instrumentation module for Matrix bot operations
|
|
11
|
+
# Provides ActiveSupport::Notifications events and structured logging
|
|
12
|
+
#
|
|
13
|
+
# @example Include in a class
|
|
14
|
+
# class MyService
|
|
15
|
+
# include ActiveMatrix::Instrumentation
|
|
16
|
+
#
|
|
17
|
+
# def perform
|
|
18
|
+
# instrument_operation(:my_operation, room_id: '!abc:matrix.org') do
|
|
19
|
+
# # ... operation code
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
module Instrumentation
|
|
25
|
+
extend ActiveSupport::Concern
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# Instrument a Matrix bot operation with timing and result tracking
|
|
30
|
+
#
|
|
31
|
+
# @param operation [Symbol, String] Operation name (e.g., :send_message, :sync)
|
|
32
|
+
# @param metadata [Hash] Additional context to include in the event
|
|
33
|
+
# @yield Block to execute and instrument
|
|
34
|
+
# @return [Object] Result of the block
|
|
35
|
+
# @raise [StandardError] Re-raises any exception after logging
|
|
36
|
+
def instrument_operation(operation, **metadata)
|
|
37
|
+
event_data = metadata.merge(
|
|
38
|
+
operation: operation,
|
|
39
|
+
agent_id: respond_to?(:agent_id) ? agent_id : nil,
|
|
40
|
+
component: self.class.name&.demodulize || 'Unknown'
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
ActiveSupport::Notifications.instrument(
|
|
44
|
+
"activematrix.#{operation}",
|
|
45
|
+
event_data
|
|
46
|
+
) do |payload|
|
|
47
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
48
|
+
|
|
49
|
+
begin
|
|
50
|
+
result = yield
|
|
51
|
+
|
|
52
|
+
payload[:status] = 'success'
|
|
53
|
+
payload[:duration_ms] = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
54
|
+
payload[:result] = summarize_result(result)
|
|
55
|
+
|
|
56
|
+
log_operation_result(operation, 'SUCCESS', payload)
|
|
57
|
+
|
|
58
|
+
result
|
|
59
|
+
rescue StandardError => e
|
|
60
|
+
payload[:status] = 'error'
|
|
61
|
+
payload[:duration_ms] = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
62
|
+
payload[:error_class] = e.class.name
|
|
63
|
+
payload[:error_message] = e.message
|
|
64
|
+
payload[:error_category] = classify_error(e)
|
|
65
|
+
|
|
66
|
+
log_operation_result(operation, 'ERROR', payload)
|
|
67
|
+
|
|
68
|
+
raise
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Classify errors for better monitoring and alerting
|
|
74
|
+
#
|
|
75
|
+
# @param error [StandardError] The error to classify
|
|
76
|
+
# @return [String] Error category
|
|
77
|
+
def classify_error(error)
|
|
78
|
+
case error
|
|
79
|
+
when Timeout::Error
|
|
80
|
+
'timeout'
|
|
81
|
+
when SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH
|
|
82
|
+
'network'
|
|
83
|
+
when JSON::ParserError
|
|
84
|
+
'parse'
|
|
85
|
+
when OpenSSL::SSL::SSLError
|
|
86
|
+
'ssl'
|
|
87
|
+
else
|
|
88
|
+
case error.class.name
|
|
89
|
+
when /ActiveMatrix::Errors::MatrixConnectionError/
|
|
90
|
+
'matrix_connection'
|
|
91
|
+
when /ActiveMatrix::Errors::MatrixRequestError/
|
|
92
|
+
'matrix_request'
|
|
93
|
+
when /ActiveMatrix::Errors::MatrixNotAuthorizedError/
|
|
94
|
+
'matrix_auth'
|
|
95
|
+
when /ActiveMatrix::Errors::MatrixForbiddenError/
|
|
96
|
+
'matrix_forbidden'
|
|
97
|
+
when /ActiveMatrix::Errors::MatrixNotFoundError/
|
|
98
|
+
'matrix_not_found'
|
|
99
|
+
when /PG::/
|
|
100
|
+
'database'
|
|
101
|
+
else
|
|
102
|
+
'application'
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Log operation result with structured data
|
|
108
|
+
#
|
|
109
|
+
# @param operation [Symbol, String] Operation name
|
|
110
|
+
# @param status [String] 'SUCCESS' or 'ERROR'
|
|
111
|
+
# @param data [Hash] Event payload data
|
|
112
|
+
def log_operation_result(operation, status, data)
|
|
113
|
+
component = data[:component] || 'Unknown'
|
|
114
|
+
agent_id = data[:agent_id] || 'unknown'
|
|
115
|
+
|
|
116
|
+
message = "#{operation} - #{status}"
|
|
117
|
+
message += " (#{data[:duration_ms]}ms)" if data[:duration_ms]
|
|
118
|
+
message = "[#{component}][agent:#{agent_id}] #{message}"
|
|
119
|
+
|
|
120
|
+
if status == 'ERROR'
|
|
121
|
+
ActiveMatrix.logger.error("#{message}: #{data[:error_class]} - #{data[:error_message]}")
|
|
122
|
+
else
|
|
123
|
+
ActiveMatrix.logger.debug(message)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Summarize result for logging without exposing sensitive data
|
|
128
|
+
#
|
|
129
|
+
# @param result [Object] The result to summarize
|
|
130
|
+
# @return [String] Human-readable summary
|
|
131
|
+
def summarize_result(result)
|
|
132
|
+
case result
|
|
133
|
+
when String
|
|
134
|
+
result.length > 100 ? "#{result[0...97]}..." : result
|
|
135
|
+
when Numeric, true, false
|
|
136
|
+
result.to_s
|
|
137
|
+
when nil
|
|
138
|
+
'nil'
|
|
139
|
+
when Hash
|
|
140
|
+
"Hash(#{result.keys.size} keys)"
|
|
141
|
+
when Array
|
|
142
|
+
"Array(#{result.size} items)"
|
|
143
|
+
else
|
|
144
|
+
result.class.name
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -14,19 +14,15 @@ module ActiveMatrix
|
|
|
14
14
|
# Get a value from agent memory
|
|
15
15
|
def get(key)
|
|
16
16
|
fetch_with_cache(key) do
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
memory = @agent.agent_memories.active.find_by(key: key)
|
|
17
|
+
memory = @agent.agent_stores.active.find_by(key: key)
|
|
20
18
|
memory&.value
|
|
21
19
|
end
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
# Set a value in agent memory
|
|
25
23
|
def set(key, value, expires_in: nil)
|
|
26
|
-
return false unless defined?(::AgentMemory)
|
|
27
|
-
|
|
28
24
|
write_through(key, value, expires_in: expires_in) do
|
|
29
|
-
memory = @agent.
|
|
25
|
+
memory = @agent.agent_stores.find_or_initialize_by(key: key)
|
|
30
26
|
memory.value = value
|
|
31
27
|
memory.expires_at = expires_in.present? ? Time.current + expires_in : nil
|
|
32
28
|
memory.save!
|
|
@@ -35,43 +31,33 @@ module ActiveMatrix
|
|
|
35
31
|
|
|
36
32
|
# Check if a key exists
|
|
37
33
|
def exists?(key)
|
|
38
|
-
return false unless defined?(::AgentMemory)
|
|
39
|
-
|
|
40
34
|
if @cache_enabled && Rails.cache.exist?(cache_key(key))
|
|
41
35
|
true
|
|
42
36
|
else
|
|
43
|
-
@agent.
|
|
37
|
+
@agent.agent_stores.active.exists?(key: key)
|
|
44
38
|
end
|
|
45
39
|
end
|
|
46
40
|
|
|
47
41
|
# Delete a key
|
|
48
42
|
def delete(key)
|
|
49
|
-
return false unless defined?(::AgentMemory)
|
|
50
|
-
|
|
51
43
|
delete_through(key) do
|
|
52
|
-
@agent.
|
|
44
|
+
@agent.agent_stores.where(key: key).destroy_all.any?
|
|
53
45
|
end
|
|
54
46
|
end
|
|
55
47
|
|
|
56
48
|
# Get all keys
|
|
57
49
|
def keys
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@agent.agent_memories.active.pluck(:key)
|
|
50
|
+
AsyncQuery.async_pluck(@agent.agent_stores.active, :key)
|
|
61
51
|
end
|
|
62
52
|
|
|
63
53
|
# Get all memory as hash
|
|
64
54
|
def all
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@agent.agent_memories.active.pluck(:key, :value).to_h
|
|
55
|
+
AsyncQuery.async_pluck(@agent.agent_stores.active, :key, :value).to_h
|
|
68
56
|
end
|
|
69
57
|
|
|
70
58
|
# Clear all agent memory
|
|
71
59
|
def clear!
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@agent.agent_memories.destroy_all
|
|
60
|
+
@agent.agent_stores.destroy_all
|
|
75
61
|
|
|
76
62
|
# Clear cache entries
|
|
77
63
|
keys.each { |key| Rails.cache.delete(cache_key(key)) } if @cache_enabled
|
|
@@ -16,8 +16,6 @@ module ActiveMatrix
|
|
|
16
16
|
# Get conversation context
|
|
17
17
|
def context
|
|
18
18
|
fetch_with_cache('context', expires_in: 1.hour) do
|
|
19
|
-
return {} unless defined?(::ConversationContext)
|
|
20
|
-
|
|
21
19
|
record = find_or_create_record
|
|
22
20
|
record.context
|
|
23
21
|
end
|
|
@@ -25,8 +23,6 @@ module ActiveMatrix
|
|
|
25
23
|
|
|
26
24
|
# Update conversation context
|
|
27
25
|
def update_context(data)
|
|
28
|
-
return false unless defined?(::ConversationContext)
|
|
29
|
-
|
|
30
26
|
record = find_or_create_record
|
|
31
27
|
record.context = record.context.merge(data)
|
|
32
28
|
record.save!
|
|
@@ -38,8 +34,6 @@ module ActiveMatrix
|
|
|
38
34
|
|
|
39
35
|
# Add a message to history
|
|
40
36
|
def add_message(event)
|
|
41
|
-
return false unless defined?(::ConversationContext)
|
|
42
|
-
|
|
43
37
|
record = find_or_create_record
|
|
44
38
|
record.add_message({
|
|
45
39
|
event_id: event[:event_id],
|
|
@@ -60,8 +54,6 @@ module ActiveMatrix
|
|
|
60
54
|
# Get recent messages
|
|
61
55
|
def recent_messages(limit = 10)
|
|
62
56
|
fetch_with_cache('recent_messages', expires_in: 5.minutes) do
|
|
63
|
-
return [] unless defined?(::ConversationContext)
|
|
64
|
-
|
|
65
57
|
record = find_or_create_record
|
|
66
58
|
record.recent_messages(limit)
|
|
67
59
|
end
|
|
@@ -69,8 +61,6 @@ module ActiveMatrix
|
|
|
69
61
|
|
|
70
62
|
# Get last message timestamp
|
|
71
63
|
def last_message_at
|
|
72
|
-
return nil unless defined?(::ConversationContext)
|
|
73
|
-
|
|
74
64
|
record = conversation_record
|
|
75
65
|
record&.last_message_at
|
|
76
66
|
end
|
|
@@ -83,8 +73,6 @@ module ActiveMatrix
|
|
|
83
73
|
|
|
84
74
|
# Clear conversation history but keep context
|
|
85
75
|
def clear_history!
|
|
86
|
-
return false unless defined?(::ConversationContext)
|
|
87
|
-
|
|
88
76
|
record = conversation_record
|
|
89
77
|
return false unless record
|
|
90
78
|
|
|
@@ -131,20 +119,16 @@ module ActiveMatrix
|
|
|
131
119
|
end
|
|
132
120
|
|
|
133
121
|
def conversation_record
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
::ConversationContext.find_by(
|
|
137
|
-
matrix_agent: @agent,
|
|
122
|
+
ActiveMatrix::ChatSession.find_by(
|
|
123
|
+
agent: @agent,
|
|
138
124
|
user_id: @user_id,
|
|
139
125
|
room_id: @room_id
|
|
140
126
|
)
|
|
141
127
|
end
|
|
142
128
|
|
|
143
129
|
def find_or_create_record
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
::ConversationContext.find_or_create_by!(
|
|
147
|
-
matrix_agent: @agent,
|
|
130
|
+
ActiveMatrix::ChatSession.find_or_create_by!(
|
|
131
|
+
agent: @agent,
|
|
148
132
|
user_id: @user_id,
|
|
149
133
|
room_id: @room_id
|
|
150
134
|
)
|
|
@@ -11,74 +11,59 @@ module ActiveMatrix
|
|
|
11
11
|
# Get a value from global memory
|
|
12
12
|
def get(key)
|
|
13
13
|
fetch_with_cache(key) do
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
::GlobalMemory.get(key)
|
|
14
|
+
ActiveMatrix::KnowledgeBase.get(key)
|
|
17
15
|
end
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
# Set a value in global memory
|
|
21
19
|
def set(key, value, category: nil, expires_in: nil, public_read: true, public_write: false)
|
|
22
|
-
return false unless defined?(::GlobalMemory)
|
|
23
|
-
|
|
24
20
|
write_through(key, value, expires_in: expires_in) do
|
|
25
|
-
::
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
ActiveMatrix::KnowledgeBase.set(key, value,
|
|
22
|
+
category: category,
|
|
23
|
+
expires_in: expires_in,
|
|
24
|
+
public_read: public_read,
|
|
25
|
+
public_write: public_write)
|
|
30
26
|
end
|
|
31
27
|
end
|
|
32
28
|
|
|
33
29
|
# Check if a key exists
|
|
34
30
|
def exists?(key)
|
|
35
|
-
return false unless defined?(::GlobalMemory)
|
|
36
|
-
|
|
37
31
|
if @cache_enabled && Rails.cache.exist?(cache_key(key))
|
|
38
32
|
true
|
|
39
33
|
else
|
|
40
|
-
::
|
|
34
|
+
ActiveMatrix::KnowledgeBase.active.exists?(key: key)
|
|
41
35
|
end
|
|
42
36
|
end
|
|
43
37
|
|
|
44
38
|
# Delete a key
|
|
45
39
|
def delete(key)
|
|
46
|
-
return false unless defined?(::GlobalMemory)
|
|
47
|
-
|
|
48
40
|
delete_through(key) do
|
|
49
|
-
::
|
|
41
|
+
ActiveMatrix::KnowledgeBase.where(key: key).destroy_all.any?
|
|
50
42
|
end
|
|
51
43
|
end
|
|
52
44
|
|
|
53
45
|
# Get all keys in a category
|
|
54
46
|
def keys(category: nil)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
scope = ::GlobalMemory.active
|
|
47
|
+
scope = ActiveMatrix::KnowledgeBase.active
|
|
58
48
|
scope = scope.by_category(category) if category
|
|
59
|
-
|
|
49
|
+
AsyncQuery.async_pluck(scope, :key)
|
|
60
50
|
end
|
|
61
51
|
|
|
62
52
|
# Get all values in a category
|
|
63
53
|
def by_category(category)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
::GlobalMemory.active.by_category(category).pluck(:key, :value).to_h
|
|
54
|
+
scope = ActiveMatrix::KnowledgeBase.active.by_category(category)
|
|
55
|
+
AsyncQuery.async_pluck(scope, :key, :value).to_h
|
|
67
56
|
end
|
|
68
57
|
|
|
69
58
|
# Check if readable by agent
|
|
70
59
|
def readable?(key, agent = nil)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
memory = ::GlobalMemory.find_by(key: key)
|
|
60
|
+
memory = ActiveMatrix::KnowledgeBase.find_by(key: key)
|
|
74
61
|
memory&.readable_by?(agent)
|
|
75
62
|
end
|
|
76
63
|
|
|
77
64
|
# Check if writable by agent
|
|
78
65
|
def writable?(key, agent = nil)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
memory = ::GlobalMemory.find_by(key: key)
|
|
66
|
+
memory = ActiveMatrix::KnowledgeBase.find_by(key: key)
|
|
82
67
|
memory&.writable_by?(agent)
|
|
83
68
|
end
|
|
84
69
|
|
|
@@ -92,7 +77,7 @@ module ActiveMatrix
|
|
|
92
77
|
# Set with permission check
|
|
93
78
|
def set_for_agent(key, value, agent, **)
|
|
94
79
|
# Allow creating new keys or updating writable ones
|
|
95
|
-
memory = ::
|
|
80
|
+
memory = ActiveMatrix::KnowledgeBase.find_by(key: key)
|
|
96
81
|
return false if memory && !memory.writable_by?(agent)
|
|
97
82
|
|
|
98
83
|
set(key, value, **)
|