activematrix 0.0.1 → 0.0.2

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +218 -51
  3. data/lib/active_matrix/agent_manager.rb +275 -0
  4. data/lib/active_matrix/agent_registry.rb +154 -0
  5. data/lib/active_matrix/bot/multi_instance_base.rb +189 -0
  6. data/lib/active_matrix/client.rb +5 -15
  7. data/lib/active_matrix/client_pool.rb +194 -0
  8. data/lib/active_matrix/event_router.rb +215 -0
  9. data/lib/active_matrix/memory/agent_memory.rb +128 -0
  10. data/lib/active_matrix/memory/base.rb +101 -0
  11. data/lib/active_matrix/memory/conversation_memory.rb +161 -0
  12. data/lib/active_matrix/memory/global_memory.rb +153 -0
  13. data/lib/active_matrix/memory.rb +28 -0
  14. data/lib/active_matrix/room.rb +131 -51
  15. data/lib/active_matrix/rooms/space.rb +1 -5
  16. data/lib/active_matrix/user.rb +10 -0
  17. data/lib/active_matrix/util/account_data_cache.rb +62 -24
  18. data/lib/active_matrix/util/cacheable.rb +73 -0
  19. data/lib/active_matrix/util/extensions.rb +4 -0
  20. data/lib/active_matrix/util/state_event_cache.rb +106 -31
  21. data/lib/active_matrix/version.rb +1 -1
  22. data/lib/active_matrix.rb +51 -3
  23. data/lib/generators/active_matrix/bot/bot_generator.rb +38 -0
  24. data/lib/generators/active_matrix/bot/templates/bot.rb.erb +111 -0
  25. data/lib/generators/active_matrix/bot/templates/bot_spec.rb.erb +68 -0
  26. data/lib/generators/active_matrix/install/install_generator.rb +44 -0
  27. data/lib/generators/active_matrix/install/templates/README +30 -0
  28. data/lib/generators/active_matrix/install/templates/active_matrix.rb +33 -0
  29. data/lib/generators/active_matrix/install/templates/agent_memory.rb +47 -0
  30. data/lib/generators/active_matrix/install/templates/conversation_context.rb +72 -0
  31. data/lib/generators/active_matrix/install/templates/create_agent_memories.rb +17 -0
  32. data/lib/generators/active_matrix/install/templates/create_conversation_contexts.rb +21 -0
  33. data/lib/generators/active_matrix/install/templates/create_global_memories.rb +20 -0
  34. data/lib/generators/active_matrix/install/templates/create_matrix_agents.rb +26 -0
  35. data/lib/generators/active_matrix/install/templates/global_memory.rb +70 -0
  36. data/lib/generators/active_matrix/install/templates/matrix_agent.rb +127 -0
  37. metadata +110 -4
  38. data/lib/active_matrix/util/rails_cache_adapter.rb +0 -37
  39. data/lib/active_matrix/util/tinycache.rb +0 -145
  40. data/lib/active_matrix/util/tinycache_adapter.rb +0 -87
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveMatrix
4
+ module Util
5
+ # Provides caching functionality for Matrix objects
6
+ # Handles serialization/deserialization to work with Rails.cache
7
+ module Cacheable
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ attr_accessor :cached_at
12
+ end
13
+
14
+ class_methods do
15
+ # Reconstruct object from cached data
16
+ def from_cache(client, data)
17
+ return nil unless data.is_a?(Hash) && data[:_cache_class] == name
18
+
19
+ # Remove cache metadata
20
+ attrs = data.except(:_cache_class, :_cached_at)
21
+
22
+ # Reconstruct based on class type
23
+ case name
24
+ when 'ActiveMatrix::User'
25
+ new(client, attrs[:id], attrs.except(:id))
26
+ when 'ActiveMatrix::Room'
27
+ new(client, attrs[:id], attrs.except(:id))
28
+ else
29
+ new(client, attrs)
30
+ end
31
+ end
32
+ end
33
+
34
+ # Convert object to cacheable hash
35
+ def to_cache
36
+ data = cache_attributes.merge(
37
+ _cache_class: self.class.name,
38
+ _cached_at: Time.current
39
+ )
40
+
41
+ # Ensure we only cache serializable data
42
+ data.deep_stringify_keys
43
+ end
44
+
45
+ # Override in each class to specify what to cache
46
+ def cache_attributes
47
+ if respond_to?(:attributes)
48
+ attributes
49
+ elsif respond_to?(:to_h)
50
+ to_h
51
+ else
52
+ {}
53
+ end
54
+ end
55
+
56
+ # Generate a cache key for this object
57
+ def cache_key(*suffixes)
58
+ base_key = "#{self.class.name.underscore}:#{cache_id}"
59
+ suffixes.any? ? "#{base_key}:#{suffixes.join(':')}" : base_key
60
+ end
61
+
62
+ # Override in each class if ID method is different
63
+ def cache_id
64
+ respond_to?(:id) ? id : object_id
65
+ end
66
+
67
+ # Check if this object was loaded from cache
68
+ def from_cache?
69
+ @cached_at.present?
70
+ end
71
+ end
72
+ end
73
+ end
@@ -8,6 +8,10 @@ unless Object.respond_to? :yield_self
8
8
  end
9
9
  end
10
10
 
11
+ # Time.current is provided by ActiveSupport
12
+
13
+ # Time duration helpers are provided by ActiveSupport
14
+
11
15
  module ActiveMatrix
12
16
  module Extensions
13
17
  def events(*symbols)
@@ -3,14 +3,13 @@
3
3
  module ActiveMatrix::Util
4
4
  class StateEventCache
5
5
  extend ActiveMatrix::Extensions
6
- extend ActiveMatrix::Util::Tinycache
7
6
  include Enumerable
8
7
 
9
8
  attr_reader :room
10
9
 
11
10
  attr_accessor :cache_time
12
11
 
13
- ignore_inspect :client, :room, :tinycache_adapter
12
+ ignore_inspect :client, :room
14
13
 
15
14
  def initialize(room, cache_time: 30 * 60, **_params)
16
15
  raise ArgumentError, 'Must be given a Room instance' unless room.is_a? ActiveMatrix::Room
@@ -24,21 +23,18 @@ module ActiveMatrix::Util
24
23
  end
25
24
 
26
25
  def reload!
27
- tinycache_adapter.clear
26
+ # Clear all cache entries for this room's state
27
+ cache.delete_matched("activematrix:room:#{room.id}:state:*") if cache_available?
28
28
  end
29
29
 
30
30
  def keys
31
- tinycache_adapter.send(:cache).keys.map do |type|
32
- real_type = type.split('|').first
33
- state_key = type.split('|').last
34
- state_key = nil if state_key == real_type
35
-
36
- [real_type, state_key]
37
- end
31
+ # State is not enumerable when using Rails.cache
32
+ # This would require keeping track of keys separately
33
+ []
38
34
  end
39
35
 
40
36
  def values
41
- keys.map { |key| tinycache_adapter.read(key) }
37
+ []
42
38
  end
43
39
 
44
40
  def size
@@ -46,47 +42,126 @@ module ActiveMatrix::Util
46
42
  end
47
43
 
48
44
  def key?(type, key = nil)
49
- keys.key?("#{type}#{"|#{key}" if key}")
45
+ cache_available? && cache.exist?(cache_key(type, key))
50
46
  end
51
47
 
52
48
  def expire(type, key = nil)
53
- tinycache_adapter.expire("#{type}#{"|#{key}" if key}")
49
+ cache.delete(cache_key(type, key)) if cache_available?
54
50
  end
55
51
 
56
52
  def each(live: false)
57
- return to_enum(__method__, live: live) { keys.count } unless block_given?
58
-
59
- keys.each do |type|
60
- real_type = type.split('|').first
61
- state_key = type.split('|').last
62
- state_key = nil if state_key == real_type
63
-
64
- v = live ? self[real_type, key: state_key] : tinycache_adapter.read(type)
65
- # hash = v.hash
66
- yield [real_type, state_key], v
67
- # self[key] = v if hash != v.hash
68
- end
53
+ to_enum(__method__, live: live) { 0 } unless block_given?
54
+ # Not enumerable with Rails.cache
69
55
  end
70
56
 
71
57
  def delete(type, key = nil)
72
58
  type = type.to_s unless type.is_a? String
73
59
  client.api.set_room_state(room.id, type, {}, **{ state_key: key }.compact)
74
- tinycache_adapter.delete("#{type}#{"|#{key}" if key}")
60
+ cache.delete(cache_key(type, key)) if cache_available?
75
61
  end
76
62
 
77
63
  def [](type, key = nil)
78
64
  type = type.to_s unless type.is_a? String
79
- tinycache_adapter.fetch("#{type}#{"|#{key}" if key}", expires_in: @cache_time) do
80
- client.api.get_room_state(room.id, type, **{ key: key }.compact)
81
- rescue ActiveMatrix::MatrixNotFoundError
82
- {}
65
+ return fetch_state(type, key) if !cache_available? || client.cache == :none
66
+
67
+ begin
68
+ cached_value = cache.fetch(cache_key(type, key), expires_in: @cache_time) do
69
+ result = fetch_state(type, key)
70
+
71
+ # Convert Response objects to plain hashes for caching
72
+ # Response objects extend Hash but contain an @api instance variable that can't be serialized
73
+ if result.is_a?(Hash)
74
+ # Create a clean hash with just the data, no instance variables or extended modules
75
+ # Deep convert to ensure no mock objects are included
76
+ clean_hash = {}
77
+ result.each do |key, value|
78
+ clean_hash[key] = case value
79
+ when Hash then value.to_h
80
+ when Array then value.map { |v| v.is_a?(Hash) ? v.to_h : v }
81
+ else value
82
+ end
83
+ end
84
+ clean_hash
85
+ else
86
+ result
87
+ end
88
+ end
89
+
90
+ # If it's a hash and we have an API client, convert it back to a Response
91
+ if cached_value.is_a?(Hash) && !cached_value.empty? && client.respond_to?(:api)
92
+ ActiveMatrix::Response.new(client.api, cached_value)
93
+ else
94
+ cached_value
95
+ end
96
+ rescue StandardError
97
+ # If caching fails, return the direct result
98
+ fetch_state(type, key)
83
99
  end
84
100
  end
85
101
 
102
+ def fetch_state(type, key = nil)
103
+ client.api.get_room_state(room.id, type, **{ key: key }.compact)
104
+ rescue ActiveMatrix::MatrixNotFoundError
105
+ {}
106
+ end
107
+
86
108
  def []=(type, key = nil, value) # rubocop:disable Style/OptionalArguments Not possible to put optional last
87
109
  type = type.to_s unless type.is_a? String
88
110
  client.api.set_room_state(room.id, type, value, **{ state_key: key }.compact)
89
- tinycache_adapter.write("#{type}#{"|#{key}" if key}", value)
111
+
112
+ return unless cache_available?
113
+
114
+ # Convert to plain hash for caching to avoid serialization issues with Mocha
115
+ cacheable_value = if value.is_a?(Hash)
116
+ clean_hash = {}
117
+ value.each do |k, v|
118
+ clean_hash[k] = case v
119
+ when Hash then v.to_h
120
+ when Array then v.map { |item| item.is_a?(Hash) ? item.to_h : item }
121
+ else v
122
+ end
123
+ end
124
+ clean_hash
125
+ else
126
+ value
127
+ end
128
+ cache.write(cache_key(type, key), cacheable_value, expires_in: @cache_time)
129
+ end
130
+
131
+ # Alias for writing without API call
132
+ def write(type, value, key = nil)
133
+ type = type.to_s unless type.is_a? String
134
+ return unless cache_available?
135
+
136
+ # Convert to plain hash for caching to avoid serialization issues
137
+ cacheable_value = if value.is_a?(Hash)
138
+ clean_hash = {}
139
+ value.each do |k, v|
140
+ clean_hash[k] = case v
141
+ when Hash then v.to_h
142
+ when Array then v.map { |item| item.is_a?(Hash) ? item.to_h : item }
143
+ else v
144
+ end
145
+ end
146
+ clean_hash
147
+ else
148
+ value
149
+ end
150
+ cache.write(cache_key(type, key), cacheable_value, expires_in: @cache_time)
151
+ end
152
+
153
+ private
154
+
155
+ def cache_key(type, key = nil)
156
+ "activematrix:room:#{room.id}:state:#{type}#{"|#{key}" if key}"
157
+ end
158
+
159
+ def cache_available?
160
+ defined?(::Rails) && ::Rails.respond_to?(:cache) && ::Rails.cache
161
+ end
162
+
163
+ def cache
164
+ ::Rails.cache
90
165
  end
91
166
  end
92
167
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveMatrix
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.2'
5
5
  end
data/lib/active_matrix.rb CHANGED
@@ -9,25 +9,73 @@ require_relative 'active_matrix/errors'
9
9
 
10
10
  require 'json'
11
11
  require 'zeitwerk'
12
+ require 'active_support'
13
+ require 'active_support/core_ext/integer/time'
14
+ require 'active_support/core_ext/time/calculations'
15
+ require 'active_support/core_ext/hash/keys'
16
+ require 'active_support/core_ext/object/blank'
12
17
 
13
18
  module ActiveMatrix
19
+ # Configuration
20
+ class << self
21
+ attr_accessor :config
22
+
23
+ def configure
24
+ @config ||= Configuration.new
25
+ yield @config if block_given?
26
+ @config
27
+ end
28
+ end
29
+
30
+ # Configuration class
31
+ class Configuration
32
+ attr_accessor :agent_startup_delay, :max_agents_per_process,
33
+ :agent_health_check_interval, :conversation_history_limit,
34
+ :conversation_stale_after, :memory_cleanup_interval,
35
+ :event_queue_size, :event_processing_timeout,
36
+ :max_clients_per_homeserver, :client_idle_timeout,
37
+ :agent_log_level, :log_agent_events
38
+
39
+ def initialize
40
+ # Set defaults
41
+ @agent_startup_delay = 2
42
+ @max_agents_per_process = 10
43
+ @agent_health_check_interval = 30
44
+ @conversation_history_limit = 20
45
+ @conversation_stale_after = 86_400 # 1 day
46
+ @memory_cleanup_interval = 3600 # 1 hour
47
+ @event_queue_size = 1000
48
+ @event_processing_timeout = 30
49
+ @max_clients_per_homeserver = 5
50
+ @client_idle_timeout = 300 # 5 minutes
51
+ @agent_log_level = :info
52
+ @log_agent_events = false
53
+ end
54
+ end
55
+
14
56
  # Set up Zeitwerk loader
15
57
  Loader = Zeitwerk::Loader.for_gem
16
-
58
+
59
+ # Ignore directories that shouldn't be autoloaded
60
+ Loader.ignore("#{__dir__}/generators")
61
+
17
62
  # Configure inflections for special cases
18
63
  Loader.inflector.inflect(
19
64
  'mxid' => 'MXID',
20
65
  'uri' => 'URI',
21
66
  'as' => 'AS',
22
67
  'cs' => 'CS',
23
- 'is' => 'IS',
68
+ 'is' => 'IS',
24
69
  'ss' => 'SS',
25
70
  'msc' => 'MSC'
26
71
  )
27
72
 
28
73
  # Setup Zeitwerk autoloading
29
74
  Loader.setup
30
-
75
+
76
+ # Eager load all classes if in Rails eager loading mode
77
+ Loader.eager_load if defined?(Rails) && Rails.respond_to?(:application) && Rails.application&.config&.eager_load
78
+
31
79
  # Load Railtie for Rails integration
32
80
  require 'active_matrix/railtie' if defined?(Rails::Railtie)
33
81
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+
5
+ module ActiveMatrix
6
+ module Generators
7
+ class BotGenerator < Rails::Generators::NamedBase
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ desc 'Creates a new ActiveMatrix bot'
11
+
12
+ argument :commands, type: :array, default: [], banner: 'command1 command2'
13
+
14
+ def create_bot_file
15
+ template 'bot.rb.erb', "app/bots/#{file_name}_bot.rb"
16
+ end
17
+
18
+ def create_bot_spec
19
+ template 'bot_spec.rb.erb', "spec/bots/#{file_name}_bot_spec.rb"
20
+ end
21
+
22
+ def display_usage
23
+ say "\nBot created! To use your bot:\n\n"
24
+ say '1. Create an agent in Rails console:'
25
+ say ' agent = MatrixAgent.create!('
26
+ say " name: '#{file_name}',"
27
+ say " homeserver: 'https://matrix.org',"
28
+ say " username: 'your_bot_username',"
29
+ say " password: 'your_bot_password',"
30
+ say " bot_class: '#{class_name}Bot'"
31
+ say ' )'
32
+ say "\n2. Start the agent:"
33
+ say ' ActiveMatrix::AgentManager.instance.start_agent(agent)'
34
+ say "\n"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %>Bot < ActiveMatrix::Bot::MultiInstanceBase
4
+ # Bot configuration
5
+ set :accept_invites, true
6
+ set :command_prefix, '!'
7
+
8
+ # Set a custom help preamble
9
+ set :help_preamble, "I am <%= class_name %>, ready to assist!"
10
+
11
+ <% if commands.any? %>
12
+ # Generated commands
13
+ <% commands.each do |command| %>
14
+ command :<%= command %>,
15
+ desc: 'TODO: Add description for <%= command %>',
16
+ args: 'TODO: Add arguments' do |*args|
17
+ # TODO: Implement <%= command %> command
18
+ room.send_notice("Command <%= command %> called with args: #{args.inspect}")
19
+
20
+ # Example of using agent memory
21
+ count = memory.increment("<%= command %>_count")
22
+ room.send_notice("This command has been used #{count} times")
23
+
24
+ # Example of using conversation context
25
+ context = conversation_context
26
+ update_context(last_command: '<%= command %>')
27
+ end
28
+
29
+ <% end %>
30
+ <% else %>
31
+ # Example command
32
+ command :hello,
33
+ desc: 'Say hello',
34
+ args: '[name]' do |name = nil|
35
+ greeting = name ? "Hello, #{name}!" : "Hello there!"
36
+ room.send_notice(greeting)
37
+
38
+ # Remember who we greeted in conversation memory
39
+ if name
40
+ names_greeted = conversation_memory.remember(:names_greeted) { [] }
41
+ unless names_greeted.include?(name)
42
+ names_greeted << name
43
+ conversation_memory[:names_greeted] = names_greeted
44
+ end
45
+ end
46
+ end
47
+
48
+ # Example command with agent communication
49
+ command :broadcast,
50
+ desc: 'Broadcast a message to all agents',
51
+ args: 'message' do |*message|
52
+ msg = message.join(' ')
53
+
54
+ # Broadcast to all online agents
55
+ broadcast_to_agents(:online, {
56
+ type: 'announcement',
57
+ message: msg,
58
+ from_room: room.id
59
+ })
60
+
61
+ room.send_notice("Broadcast sent: #{msg}")
62
+ end
63
+ <% end %>
64
+
65
+ # Example event handler
66
+ event 'm.room.member' do
67
+ if event[:content][:membership] == 'join' && event[:state_key] != client.mxid
68
+ # Someone joined the room
69
+ user = client.get_user(event[:state_key])
70
+ room.send_notice("Welcome, #{user.display_name || user.id}!")
71
+
72
+ # Track room members in agent memory
73
+ members = memory.get(:room_members) || {}
74
+ members[room.id] ||= []
75
+ members[room.id] << event[:state_key] unless members[room.id].include?(event[:state_key])
76
+ memory.set(:room_members, members)
77
+ end
78
+ end
79
+
80
+ # Handle inter-agent messages
81
+ def receive_message(data, from:)
82
+ case data[:type]
83
+ when 'ping'
84
+ # Respond to ping
85
+ send_to_agent(from.agent_name, { type: 'pong', timestamp: Time.current })
86
+ else
87
+ logger.info "Received message from #{from.agent_name}: #{data.inspect}"
88
+ end
89
+ end
90
+
91
+ # Handle broadcasts
92
+ def receive_broadcast(data, from:)
93
+ case data[:type]
94
+ when 'announcement'
95
+ # Could relay announcements to specific rooms
96
+ logger.info "Received announcement from #{from.agent_name}: #{data[:message]}"
97
+ end
98
+ end
99
+
100
+ # Custom helper methods
101
+ private
102
+
103
+ def greeting_for_time
104
+ hour = Time.current.hour
105
+ case hour
106
+ when 0..11 then "Good morning"
107
+ when 12..17 then "Good afternoon"
108
+ when 18..23 then "Good evening"
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe <%= class_name %>Bot do
6
+ let(:agent) do
7
+ MatrixAgent.create!(
8
+ name: '<%= file_name %>_test',
9
+ homeserver: 'https://matrix.example.com',
10
+ username: 'test_bot',
11
+ password: 'password',
12
+ bot_class: '<%= class_name %>Bot'
13
+ )
14
+ end
15
+
16
+ let(:client) { instance_double(ActiveMatrix::Client) }
17
+ let(:room) { instance_double(ActiveMatrix::Room) }
18
+ let(:bot) { described_class.new(agent) }
19
+
20
+ before do
21
+ allow(agent).to receive(:client).and_return(client)
22
+ allow(client).to receive(:mxid).and_return('@test_bot:example.com')
23
+ allow(bot).to receive(:room).and_return(room)
24
+ end
25
+
26
+ <% if commands.any? %>
27
+ <% commands.each do |command| %>
28
+ describe '#<%= command %>' do
29
+ it 'responds to the <%= command %> command' do
30
+ expect(room).to receive(:send_notice).twice
31
+ bot.send(:<%= command %>)
32
+ end
33
+ end
34
+
35
+ <% end %>
36
+ <% else %>
37
+ describe '#hello' do
38
+ it 'responds with a greeting' do
39
+ expect(room).to receive(:send_notice).with('Hello there!')
40
+ bot.hello
41
+ end
42
+
43
+ it 'greets by name when provided' do
44
+ expect(room).to receive(:send_notice).with('Hello, Alice!')
45
+ bot.hello('Alice')
46
+ end
47
+ end
48
+ <% end %>
49
+
50
+ describe 'inter-agent communication' do
51
+ let(:other_bot) { instance_double(ActiveMatrix::Bot::MultiInstanceBase, agent_name: 'other_bot') }
52
+
53
+ it 'responds to ping messages' do
54
+ expect(bot).to receive(:send_to_agent).with('other_bot', hash_including(type: 'pong'))
55
+ bot.receive_message({ type: 'ping' }, from: other_bot)
56
+ end
57
+ end
58
+
59
+ describe 'memory access' do
60
+ it 'has access to agent memory' do
61
+ expect(bot.memory).to be_a(ActiveMatrix::Memory::AgentMemory)
62
+ end
63
+
64
+ it 'has access to global memory' do
65
+ expect(bot.global_memory).to be_a(ActiveMatrix::Memory::GlobalMemory)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+
6
+ module ActiveMatrix
7
+ module Generators
8
+ class InstallGenerator < Rails::Generators::Base
9
+ include ActiveRecord::Generators::Migration
10
+
11
+ source_root File.expand_path('templates', __dir__)
12
+
13
+ desc 'Creates ActiveMatrix migrations and initializers'
14
+
15
+ def create_migrations
16
+ migration_template 'create_matrix_agents.rb', 'db/migrate/create_matrix_agents.rb'
17
+ migration_template 'create_agent_memories.rb', 'db/migrate/create_agent_memories.rb'
18
+ migration_template 'create_conversation_contexts.rb', 'db/migrate/create_conversation_contexts.rb'
19
+ migration_template 'create_global_memories.rb', 'db/migrate/create_global_memories.rb'
20
+ end
21
+
22
+ def create_initializer
23
+ template 'active_matrix.rb', 'config/initializers/active_matrix.rb'
24
+ end
25
+
26
+ def create_models
27
+ template 'matrix_agent.rb', 'app/models/matrix_agent.rb'
28
+ template 'agent_memory.rb', 'app/models/agent_memory.rb'
29
+ template 'conversation_context.rb', 'app/models/conversation_context.rb'
30
+ template 'global_memory.rb', 'app/models/global_memory.rb'
31
+ end
32
+
33
+ def display_post_install
34
+ readme 'README' if behavior == :invoke
35
+ end
36
+
37
+ private
38
+
39
+ def migration_version
40
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ ===============================================================================
2
+
3
+ ActiveMatrix Multi-Agent System has been installed!
4
+
5
+ Next steps:
6
+
7
+ 1. Run migrations:
8
+ rails db:migrate
9
+
10
+ 2. Create your first bot:
11
+ rails generate active_matrix:bot MyFirstBot
12
+
13
+ 3. Configure your agents in the Rails console:
14
+ agent = MatrixAgent.create!(
15
+ name: 'captain',
16
+ homeserver: 'https://matrix.org',
17
+ username: 'captain_bot',
18
+ password: 'secure_password',
19
+ bot_class: 'MyFirstBot'
20
+ )
21
+
22
+ 4. Start the agent system:
23
+ ActiveMatrix::AgentManager.instance.start_all
24
+
25
+ Or start individual agents:
26
+ ActiveMatrix::AgentManager.instance.start_agent(agent)
27
+
28
+ For more information, visit: https://github.com/seuros/agent_smith
29
+
30
+ ===============================================================================
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ActiveMatrix configuration
4
+ ActiveMatrix.configure do |config|
5
+ # Agent configuration
6
+ config.agent_startup_delay = 2.seconds # Delay between starting each agent
7
+ config.max_agents_per_process = 10 # Maximum agents in a single process
8
+ config.agent_health_check_interval = 30.seconds
9
+
10
+ # Memory configuration
11
+ config.conversation_history_limit = 20
12
+ config.conversation_stale_after = 1.day
13
+ config.memory_cleanup_interval = 1.hour
14
+
15
+ # Event routing configuration
16
+ config.event_queue_size = 1000
17
+ config.event_processing_timeout = 30.seconds
18
+
19
+ # Client pool configuration
20
+ config.max_clients_per_homeserver = 5
21
+ config.client_idle_timeout = 5.minutes
22
+
23
+ # Logging
24
+ config.agent_log_level = :info
25
+ config.log_agent_events = Rails.env.development?
26
+ end
27
+
28
+ # Start agent manager on Rails boot (can be disabled in environments)
29
+ if Rails.env.production? || ENV['START_AGENTS'].present?
30
+ Rails.application.config.after_initialize do
31
+ ActiveMatrix::AgentManager.instance.start_all
32
+ end
33
+ end