activematrix 0.0.4 → 0.0.7
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/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 +131 -0
- data/app/models/active_matrix/agent_store.rb +51 -0
- data/app/models/active_matrix/application_record.rb +7 -0
- data/app/models/active_matrix/chat_session.rb +76 -0
- data/app/models/active_matrix/knowledge_base.rb +74 -0
- data/lib/active_matrix/engine.rb +14 -0
- data/lib/active_matrix/protocols/cs/message_relationships.rb +318 -0
- data/lib/active_matrix/railtie.rb +8 -0
- data/lib/active_matrix/version.rb +1 -1
- data/lib/active_matrix.rb +11 -9
- data/lib/generators/active_matrix/install/install_generator.rb +0 -7
- metadata +77 -13
- 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/global_memory.rb +0 -70
- data/lib/generators/active_matrix/install/templates/matrix_agent.rb +0 -127
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class ConversationContext < ApplicationRecord
|
4
|
-
belongs_to :matrix_agent
|
5
|
-
|
6
|
-
validates :user_id, presence: true
|
7
|
-
validates :room_id, presence: true
|
8
|
-
validates :user_id, uniqueness: { scope: %i[matrix_agent_id room_id] }
|
9
|
-
|
10
|
-
# Configuration
|
11
|
-
MAX_HISTORY_SIZE = 20
|
12
|
-
|
13
|
-
# Scopes
|
14
|
-
scope :recent, -> { order(last_message_at: :desc) }
|
15
|
-
scope :active, -> { where('last_message_at > ?', 1.hour.ago) }
|
16
|
-
scope :stale, -> { where('last_message_at < ?', 1.day.ago) }
|
17
|
-
|
18
|
-
# Add a message to the history
|
19
|
-
def add_message(message_data)
|
20
|
-
messages = message_history['messages'] || []
|
21
|
-
|
22
|
-
# Add new message
|
23
|
-
messages << {
|
24
|
-
'event_id' => message_data[:event_id],
|
25
|
-
'sender' => message_data[:sender],
|
26
|
-
'content' => message_data[:content],
|
27
|
-
'timestamp' => message_data[:timestamp] || Time.current.to_i
|
28
|
-
}
|
29
|
-
|
30
|
-
# Keep only recent messages
|
31
|
-
messages = messages.last(MAX_HISTORY_SIZE)
|
32
|
-
|
33
|
-
# Update record
|
34
|
-
self.message_history = { 'messages' => messages }
|
35
|
-
self.last_message_at = Time.current
|
36
|
-
self.message_count = messages.size
|
37
|
-
save!
|
38
|
-
|
39
|
-
# Update cache
|
40
|
-
write_to_cache
|
41
|
-
end
|
42
|
-
|
43
|
-
# Get recent messages
|
44
|
-
def recent_messages(limit = 10)
|
45
|
-
messages = message_history['messages'] || []
|
46
|
-
messages.last(limit)
|
47
|
-
end
|
48
|
-
|
49
|
-
# Clear old messages but keep context
|
50
|
-
def prune_history!
|
51
|
-
messages = message_history['messages'] || []
|
52
|
-
self.message_history = { 'messages' => messages.last(5) }
|
53
|
-
save!
|
54
|
-
end
|
55
|
-
|
56
|
-
# Cache integration
|
57
|
-
def cache_key
|
58
|
-
"conversation/#{matrix_agent_id}/#{user_id}/#{room_id}"
|
59
|
-
end
|
60
|
-
|
61
|
-
def write_to_cache
|
62
|
-
Rails.cache.write(cache_key, {
|
63
|
-
context: context,
|
64
|
-
recent_messages: recent_messages,
|
65
|
-
last_message_at: last_message_at
|
66
|
-
}, expires_in: 1.hour)
|
67
|
-
end
|
68
|
-
|
69
|
-
def self.cleanup_stale!
|
70
|
-
stale.destroy_all
|
71
|
-
end
|
72
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class GlobalMemory < ApplicationRecord
|
4
|
-
validates :key, presence: true, uniqueness: true
|
5
|
-
|
6
|
-
scope :active, -> { where('expires_at IS NULL OR expires_at > ?', Time.current) }
|
7
|
-
scope :expired, -> { where('expires_at <= ?', Time.current) }
|
8
|
-
scope :by_category, ->(category) { where(category: category) }
|
9
|
-
scope :readable, -> { where(public_read: true) }
|
10
|
-
scope :writable, -> { where(public_write: true) }
|
11
|
-
|
12
|
-
def expired?
|
13
|
-
expires_at.present? && expires_at <= Time.current
|
14
|
-
end
|
15
|
-
|
16
|
-
def readable_by?(agent)
|
17
|
-
public_read || (agent.is_a?(MatrixAgent) && agent.admin?)
|
18
|
-
end
|
19
|
-
|
20
|
-
def writable_by?(agent)
|
21
|
-
public_write || (agent.is_a?(MatrixAgent) && agent.admin?)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Cache integration
|
25
|
-
def cache_key
|
26
|
-
"global/#{key}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def write_to_cache
|
30
|
-
return unless active?
|
31
|
-
|
32
|
-
ttl = expires_at.present? ? expires_at - Time.current : nil
|
33
|
-
Rails.cache.write(cache_key, value, expires_in: ttl)
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.get(key)
|
37
|
-
# Try cache first
|
38
|
-
cached = Rails.cache.read("global/#{key}")
|
39
|
-
return cached if cached.present?
|
40
|
-
|
41
|
-
# Fallback to database
|
42
|
-
memory = find_by(key: key)
|
43
|
-
return unless memory&.active?
|
44
|
-
|
45
|
-
memory.write_to_cache
|
46
|
-
memory.value
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.set(key, value, category: nil, expires_in: nil, public_read: true, public_write: false)
|
50
|
-
memory = find_or_initialize_by(key: key)
|
51
|
-
memory.value = value
|
52
|
-
memory.category = category
|
53
|
-
memory.expires_at = expires_in.present? ? Time.current + expires_in : nil
|
54
|
-
memory.public_read = public_read
|
55
|
-
memory.public_write = public_write
|
56
|
-
memory.save!
|
57
|
-
memory.write_to_cache
|
58
|
-
memory
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.cleanup_expired!
|
62
|
-
expired.destroy_all
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def active?
|
68
|
-
!expired?
|
69
|
-
end
|
70
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class MatrixAgent < ApplicationRecord
|
4
|
-
# Associations
|
5
|
-
has_many :agent_memories, dependent: :destroy
|
6
|
-
has_many :conversation_contexts, dependent: :destroy
|
7
|
-
|
8
|
-
# Validations
|
9
|
-
validates :name, presence: true, uniqueness: true
|
10
|
-
validates :homeserver, presence: true
|
11
|
-
validates :username, presence: true
|
12
|
-
validates :bot_class, presence: true
|
13
|
-
validate :valid_bot_class?
|
14
|
-
|
15
|
-
# Scopes
|
16
|
-
scope :active, -> { where.not(state: %i[offline error]) }
|
17
|
-
scope :online, -> { where(state: %i[online_idle online_busy]) }
|
18
|
-
scope :offline, -> { where(state: :offline) }
|
19
|
-
|
20
|
-
# Encrypts password before saving
|
21
|
-
before_save :encrypt_password, if: :password_changed?
|
22
|
-
|
23
|
-
# State machine for agent lifecycle
|
24
|
-
state_machine :state, initial: :offline do
|
25
|
-
state :offline
|
26
|
-
state :connecting
|
27
|
-
state :online_idle
|
28
|
-
state :online_busy
|
29
|
-
state :error
|
30
|
-
state :paused
|
31
|
-
|
32
|
-
event :connect do
|
33
|
-
transition %i[offline error paused] => :connecting
|
34
|
-
end
|
35
|
-
|
36
|
-
event :connection_established do
|
37
|
-
transition connecting: :online_idle
|
38
|
-
end
|
39
|
-
|
40
|
-
after_transition to: :online_idle do |agent|
|
41
|
-
agent.update(last_active_at: Time.current)
|
42
|
-
end
|
43
|
-
|
44
|
-
event :start_processing do
|
45
|
-
transition online_idle: :online_busy
|
46
|
-
end
|
47
|
-
|
48
|
-
event :finish_processing do
|
49
|
-
transition online_busy: :online_idle
|
50
|
-
end
|
51
|
-
|
52
|
-
event :disconnect do
|
53
|
-
transition %i[connecting online_idle online_busy] => :offline
|
54
|
-
end
|
55
|
-
|
56
|
-
event :encounter_error do
|
57
|
-
transition any => :error
|
58
|
-
end
|
59
|
-
|
60
|
-
event :pause do
|
61
|
-
transition %i[online_idle online_busy] => :paused
|
62
|
-
end
|
63
|
-
|
64
|
-
event :resume do
|
65
|
-
transition paused: :connecting
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Instance methods
|
70
|
-
def bot_instance
|
71
|
-
@bot_instance ||= bot_class.constantize.new(client) if running?
|
72
|
-
end
|
73
|
-
|
74
|
-
def client
|
75
|
-
@client ||= if access_token.present?
|
76
|
-
ActiveMatrix::Client.new(homeserver, access_token: access_token)
|
77
|
-
else
|
78
|
-
ActiveMatrix::Client.new(homeserver)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def running?
|
83
|
-
%i[online_idle online_busy].include?(state.to_sym)
|
84
|
-
end
|
85
|
-
|
86
|
-
def memory
|
87
|
-
@memory ||= ActiveMatrix::Memory::AgentMemory.new(self)
|
88
|
-
end
|
89
|
-
|
90
|
-
def increment_messages_handled!
|
91
|
-
increment!(:messages_handled)
|
92
|
-
end
|
93
|
-
|
94
|
-
def update_activity!
|
95
|
-
update(last_active_at: Time.current)
|
96
|
-
end
|
97
|
-
|
98
|
-
# Password handling
|
99
|
-
attr_accessor :password
|
100
|
-
|
101
|
-
def authenticate(password)
|
102
|
-
return false unless encrypted_password.present?
|
103
|
-
|
104
|
-
BCrypt::Password.new(encrypted_password) == password
|
105
|
-
end
|
106
|
-
|
107
|
-
private
|
108
|
-
|
109
|
-
def password_changed?
|
110
|
-
password.present?
|
111
|
-
end
|
112
|
-
|
113
|
-
def encrypt_password
|
114
|
-
self.encrypted_password = BCrypt::Password.create(password) if password.present?
|
115
|
-
end
|
116
|
-
|
117
|
-
def valid_bot_class?
|
118
|
-
return false if bot_class.blank?
|
119
|
-
|
120
|
-
begin
|
121
|
-
klass = bot_class.constantize
|
122
|
-
errors.add(:bot_class, 'must inherit from ActiveMatrix::Bot::Base') unless klass < ActiveMatrix::Bot::Base
|
123
|
-
rescue NameError
|
124
|
-
errors.add(:bot_class, 'must be a valid class name')
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|