activematrix 0.0.0 → 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 (60) 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/{matrix_sdk → active_matrix}/api.rb +18 -22
  6. data/lib/{matrix_sdk → active_matrix}/bot/base.rb +42 -39
  7. data/lib/{matrix_sdk → active_matrix}/bot/main.rb +4 -5
  8. data/lib/active_matrix/bot/multi_instance_base.rb +189 -0
  9. data/lib/active_matrix/bot.rb +7 -0
  10. data/lib/{matrix_sdk → active_matrix}/client.rb +21 -34
  11. data/lib/active_matrix/client_pool.rb +194 -0
  12. data/lib/{matrix_sdk → active_matrix}/errors.rb +4 -4
  13. data/lib/active_matrix/event_router.rb +215 -0
  14. data/lib/active_matrix/logging.rb +56 -0
  15. data/lib/active_matrix/memory/agent_memory.rb +128 -0
  16. data/lib/active_matrix/memory/base.rb +101 -0
  17. data/lib/active_matrix/memory/conversation_memory.rb +161 -0
  18. data/lib/active_matrix/memory/global_memory.rb +153 -0
  19. data/lib/active_matrix/memory.rb +28 -0
  20. data/lib/{matrix_sdk → active_matrix}/mxid.rb +2 -2
  21. data/lib/{matrix_sdk → active_matrix}/protocols/as.rb +1 -1
  22. data/lib/{matrix_sdk → active_matrix}/protocols/cs.rb +6 -8
  23. data/lib/{matrix_sdk → active_matrix}/protocols/is.rb +1 -1
  24. data/lib/{matrix_sdk → active_matrix}/protocols/msc.rb +6 -8
  25. data/lib/{matrix_sdk → active_matrix}/protocols/ss.rb +2 -2
  26. data/lib/active_matrix/railtie.rb +18 -0
  27. data/lib/{matrix_sdk → active_matrix}/response.rb +2 -2
  28. data/lib/{matrix_sdk → active_matrix}/room.rb +148 -72
  29. data/lib/{matrix_sdk → active_matrix}/rooms/space.rb +3 -7
  30. data/lib/{matrix_sdk → active_matrix}/user.rb +23 -15
  31. data/lib/active_matrix/util/account_data_cache.rb +129 -0
  32. data/lib/active_matrix/util/cacheable.rb +73 -0
  33. data/lib/{matrix_sdk → active_matrix}/util/events.rb +8 -8
  34. data/lib/{matrix_sdk → active_matrix}/util/extensions.rb +6 -15
  35. data/lib/active_matrix/util/state_event_cache.rb +167 -0
  36. data/lib/{matrix_sdk → active_matrix}/util/uri.rb +4 -4
  37. data/lib/active_matrix/version.rb +5 -0
  38. data/lib/active_matrix.rb +81 -0
  39. data/lib/generators/active_matrix/bot/bot_generator.rb +38 -0
  40. data/lib/generators/active_matrix/bot/templates/bot.rb.erb +111 -0
  41. data/lib/generators/active_matrix/bot/templates/bot_spec.rb.erb +68 -0
  42. data/lib/generators/active_matrix/install/install_generator.rb +44 -0
  43. data/lib/generators/active_matrix/install/templates/README +30 -0
  44. data/lib/generators/active_matrix/install/templates/active_matrix.rb +33 -0
  45. data/lib/generators/active_matrix/install/templates/agent_memory.rb +47 -0
  46. data/lib/generators/active_matrix/install/templates/conversation_context.rb +72 -0
  47. data/lib/generators/active_matrix/install/templates/create_agent_memories.rb +17 -0
  48. data/lib/generators/active_matrix/install/templates/create_conversation_contexts.rb +21 -0
  49. data/lib/generators/active_matrix/install/templates/create_global_memories.rb +20 -0
  50. data/lib/generators/active_matrix/install/templates/create_matrix_agents.rb +26 -0
  51. data/lib/generators/active_matrix/install/templates/global_memory.rb +70 -0
  52. data/lib/generators/active_matrix/install/templates/matrix_agent.rb +127 -0
  53. metadata +168 -30
  54. data/lib/matrix_sdk/bot.rb +0 -4
  55. data/lib/matrix_sdk/util/account_data_cache.rb +0 -91
  56. data/lib/matrix_sdk/util/state_event_cache.rb +0 -92
  57. data/lib/matrix_sdk/util/tinycache.rb +0 -140
  58. data/lib/matrix_sdk/util/tinycache_adapter.rb +0 -87
  59. data/lib/matrix_sdk/version.rb +0 -5
  60. data/lib/matrix_sdk.rb +0 -75
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveMatrix::Bot
4
+ # Base class for multi-instance bot support
5
+ class MultiInstanceBase < Base
6
+ attr_reader :agent_record
7
+
8
+ def initialize(client_or_agent, **params)
9
+ # Handle both client and agent record initialization
10
+ if client_or_agent.respond_to?(:homeserver) && !client_or_agent.respond_to?(:client) # It's a client
11
+ super
12
+ @agent_record = params[:agent_record]
13
+ else # It's an agent record
14
+ @agent_record = client_or_agent
15
+ super(@agent_record.client, **params)
16
+ end
17
+
18
+ setup_agent_context if @agent_record
19
+ end
20
+
21
+ # Access agent-specific memory
22
+ def memory
23
+ @memory ||= ActiveMatrix::Memory.for_agent(@agent_record)
24
+ end
25
+
26
+ # Access conversation memory
27
+ def conversation_memory(user_id = nil, room_id = nil)
28
+ user_id ||= event[:sender] if in_event?
29
+ room_id ||= event[:room_id] if in_event?
30
+
31
+ return nil unless user_id && room_id
32
+
33
+ @conversation_memories ||= {}
34
+ @conversation_memories["#{user_id}/#{room_id}"] ||=
35
+ ActiveMatrix::Memory.for_conversation(@agent_record, user_id, room_id)
36
+ end
37
+
38
+ # Access global memory
39
+ def global_memory
40
+ ActiveMatrix::Memory.global
41
+ end
42
+
43
+ # Get current conversation context
44
+ def conversation_context
45
+ conversation_memory&.context || {}
46
+ end
47
+
48
+ # Update conversation context
49
+ def update_context(data)
50
+ conversation_memory&.update_context(data)
51
+ end
52
+
53
+ # Remember something in conversation
54
+ def remember_in_conversation(key, &)
55
+ conversation_memory&.remember(key, &)
56
+ end
57
+
58
+ # Agent state helpers
59
+ def agent_name
60
+ @agent_record&.name || settings.bot_name
61
+ end
62
+
63
+ def agent_state
64
+ @agent_record&.state || 'unknown'
65
+ end
66
+
67
+ def mark_busy!
68
+ @agent_record&.start_processing! if @agent_record&.may_start_processing?
69
+ end
70
+
71
+ def mark_idle!
72
+ @agent_record&.finish_processing! if @agent_record&.may_finish_processing?
73
+ end
74
+
75
+ # Inter-agent communication
76
+ def broadcast_to_agents(selector, data)
77
+ return unless defined?(AgentRegistry)
78
+
79
+ registry = AgentRegistry.instance
80
+ registry.broadcast_to(selector) do |agent_bot|
81
+ agent_bot.receive_broadcast(data, from: self)
82
+ end
83
+ end
84
+
85
+ def send_to_agent(agent_name, data)
86
+ return unless defined?(AgentRegistry)
87
+
88
+ registry = AgentRegistry.instance
89
+ entry = registry.get_by_name(agent_name)
90
+
91
+ if entry
92
+ entry[:instance].receive_message(data, from: self)
93
+ true
94
+ else
95
+ logger.warn "Agent #{agent_name} not found or not running"
96
+ false
97
+ end
98
+ end
99
+
100
+ # Receive inter-agent messages (override in subclasses)
101
+ def receive_message(data, from:)
102
+ logger.debug "Received message from #{from.agent_name}: #{data.inspect}"
103
+ end
104
+
105
+ def receive_broadcast(data, from:)
106
+ logger.debug "Received broadcast from #{from.agent_name}: #{data.inspect}"
107
+ end
108
+
109
+ # Override event handling to track conversation
110
+ def _handle_message(event)
111
+ # Mark as busy while processing
112
+ mark_busy!
113
+
114
+ # Track conversation if we have an agent record
115
+ conversation_memory(event[:sender], event[:room_id])&.add_message(event) if @agent_record && event[:type] == 'm.room.message'
116
+
117
+ super
118
+ ensure
119
+ # Mark as idle when done
120
+ mark_idle!
121
+ end
122
+
123
+ def _handle_event(event)
124
+ mark_busy!
125
+ super
126
+ ensure
127
+ mark_idle!
128
+ end
129
+
130
+ # Enhanced settings with agent-specific overrides
131
+ def settings
132
+ return self.class unless @agent_record
133
+
134
+ # Merge agent-specific settings with class settings
135
+ @settings ||= begin
136
+ base_settings = self.class.settings
137
+ agent_settings = @agent_record.settings || {}
138
+
139
+ # Create a settings proxy that checks agent settings first
140
+ SettingsProxy.new(base_settings, agent_settings)
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ def setup_agent_context
147
+ # Set up agent-specific logging
148
+ if @agent_record
149
+ @logger = ActiveMatrix.logger.dup
150
+ @logger.progname = "[#{@agent_record.name}]"
151
+ end
152
+
153
+ # Load any agent-specific configuration
154
+ return unless @agent_record.settings['commands_disabled']
155
+
156
+ @agent_record.settings['commands_disabled'].each do |cmd|
157
+ # Temporarily disable commands for this instance
158
+ @disabled_commands ||= []
159
+ @disabled_commands << cmd
160
+ end
161
+ end
162
+
163
+ # Settings proxy to merge class and instance settings
164
+ class SettingsProxy
165
+ def initialize(base_settings, agent_settings)
166
+ @base_settings = base_settings
167
+ @agent_settings = agent_settings
168
+ end
169
+
170
+ def method_missing(method, *, &)
171
+ method_name = method.to_s.gsub(/\?$/, '')
172
+
173
+ # Check agent settings first
174
+ if @agent_settings.key?(method_name)
175
+ value = @agent_settings[method_name]
176
+ return method.to_s.end_with?('?') ? !value.nil? : value
177
+ end
178
+
179
+ # Fall back to base settings
180
+ @base_settings.send(method, *, &)
181
+ end
182
+
183
+ def respond_to_missing?(method, include_private = false)
184
+ method_name = method.to_s.gsub(/\?$/, '')
185
+ @agent_settings.key?(method_name) || @base_settings.respond_to?(method, include_private)
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveMatrix
4
+ # Namespace module for Bot framework
5
+ module Bot
6
+ end
7
+ end
@@ -1,15 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'matrix_sdk'
4
- require 'matrix_sdk/util/events'
5
-
6
3
  require 'English'
7
4
  require 'forwardable'
8
5
 
9
- module MatrixSdk
6
+ module ActiveMatrix
10
7
  class Client
11
- extend MatrixSdk::Extensions
12
- include MatrixSdk::Logging
8
+ extend ActiveMatrix::Extensions
9
+ include ActiveMatrix::Logging
13
10
  extend Forwardable
14
11
 
15
12
  # @!attribute api [r] The underlying API connection
@@ -44,11 +41,11 @@ module MatrixSdk
44
41
  # @see Api.new_for_domain
45
42
  # @see #initialize
46
43
  def self.new_for_domain(domain, **params)
47
- api = MatrixSdk::Api.new_for_domain(domain, keep_wellknown: true)
44
+ api = ActiveMatrix::Api.new_for_domain(domain, keep_wellknown: true)
48
45
  return new(api, **params) unless api.well_known&.key?('m.identity_server')
49
46
 
50
- identity_server = MatrixSdk::Api.new(api.well_known['m.identity_server']['base_url'], protocols: %i[IS])
51
- new(api, **params.merge(identity_server: identity_server))
47
+ identity_server = ActiveMatrix::Api.new(api.well_known['m.identity_server']['base_url'], protocols: %i[IS])
48
+ new(api, **params, identity_server: identity_server)
52
49
  end
53
50
 
54
51
  # @param hs_url [String,URI,Api] The URL to the Matrix homeserver, without the /_matrix/ part, or an existing Api instance
@@ -56,7 +53,7 @@ module MatrixSdk
56
53
  # @param params [Hash] Additional parameters on creation
57
54
  # @option params [String,MXID] :user_id The user ID of the logged-in user
58
55
  # @option params [Integer] :sync_filter_limit (20) Limit of timeline entries in syncs
59
- # @see MatrixSdk::Api.new for additional usable params
56
+ # @see ActiveMatrix::Api.new for additional usable params
60
57
  def initialize(hs_url, client_cache: :all, **params)
61
58
  event_initialize
62
59
 
@@ -153,7 +150,7 @@ module MatrixSdk
153
150
 
154
151
  break if data[:next_batch].nil?
155
152
 
156
- since = data.next_batch
153
+ since = data[:next_batch]
157
154
  end
158
155
 
159
156
  rooms
@@ -168,9 +165,9 @@ module MatrixSdk
168
165
 
169
166
  # Retrieve an account data helper
170
167
  def account_data
171
- return MatrixSdk::Util::AccountDataCache.new self if cache == :none
168
+ return ActiveMatrix::Util::AccountDataCache.new self if cache == :none
172
169
 
173
- @account_data ||= MatrixSdk::Util::AccountDataCache.new self
170
+ @account_data ||= ActiveMatrix::Util::AccountDataCache.new self
174
171
  end
175
172
 
176
173
  # Gets a direct message room for the given user if one exists
@@ -178,7 +175,7 @@ module MatrixSdk
178
175
  # @note Will return the oldest room if multiple exist
179
176
  # @return [Room,nil] A direct message room if one exists
180
177
  def direct_room(mxid)
181
- mxid = MatrixSdk::MXID.new mxid.to_s unless mxid.is_a? MatrixSdk::MXID
178
+ mxid = ActiveMatrix::MXID.new mxid.to_s unless mxid.is_a? ActiveMatrix::MXID
182
179
  raise ArgumentError, 'Must be a valid user ID' unless mxid.user?
183
180
 
184
181
  room_id = direct_rooms[mxid.to_s]&.first
@@ -365,7 +362,7 @@ module MatrixSdk
365
362
  end
366
363
 
367
364
  def inspect
368
- "#<MatrixSdk::Response 3pid=#{to_s.inspect} added_at=\"#{added_at}\"#{validated? ? " validated_at=\"#{validated_at}\"" : ''}>"
365
+ "#<ActiveMatrix::Response 3pid=#{to_s.inspect} added_at=\"#{added_at}\"#{" validated_at=\"#{validated_at}\"" if validated?}>"
369
366
  end
370
367
  end
371
368
  end
@@ -376,13 +373,13 @@ module MatrixSdk
376
373
  #
377
374
  # @example Creating a room with an alias
378
375
  # client.create_room('myroom')
379
- # #<MatrixSdk::Room ... >
376
+ # #<ActiveMatrix::Room ... >
380
377
  #
381
378
  # @param room_alias [String] A default alias to set on the room, should only be the localpart
382
379
  # @return [Room] The resulting room
383
380
  # @see Protocols::CS#create_room
384
381
  def create_room(room_alias = nil, **params)
385
- data = api.create_room(**params.merge(room_alias: room_alias))
382
+ data = api.create_room(**params, room_alias: room_alias)
386
383
  ensure_room(data.room_id)
387
384
  end
388
385
 
@@ -542,7 +539,7 @@ module MatrixSdk
542
539
  attempts = 0
543
540
  data = loop do
544
541
  break api.sync(**extra_params)
545
- rescue MatrixSdk::MatrixTimeoutError => e
542
+ rescue ActiveMatrix::MatrixTimeoutError => e
546
543
  raise e if (attempts += 1) >= params.fetch(:allow_sync_retry, 0)
547
544
  end
548
545
 
@@ -577,7 +574,7 @@ module MatrixSdk
577
574
  orig_bad_sync_timeout = bad_sync_timeout + 0
578
575
  while @should_listen
579
576
  begin
580
- sync(**params.merge(timeout: timeout))
577
+ sync(**params, timeout: timeout)
581
578
  return unless @should_listen
582
579
 
583
580
  bad_sync_timeout = orig_bad_sync_timeout
@@ -588,7 +585,7 @@ module MatrixSdk
588
585
  logger.warn("A #{e.class} occurred during sync")
589
586
  if e.httpstatus >= 500
590
587
  logger.warn("Serverside error, retrying in #{bad_sync_timeout} seconds...")
591
- sleep(bad_sync_timeout) if bad_sync_timeout.positive? # rubocop:disable Metrics/BlockNesting
588
+ sleep(bad_sync_timeout) if bad_sync_timeout.positive?
592
589
  bad_sync_timeout = [bad_sync_timeout * 2, @bad_sync_timeout_limit].min
593
590
  end
594
591
  end
@@ -620,10 +617,9 @@ module MatrixSdk
620
617
 
621
618
  def handle_sync_response(data)
622
619
  data.dig(:account_data, :events)&.each do |account_data|
623
- if cache != :none
624
- adapter = self.account_data.tinycache_adapter
625
- adapter.write(account_data[:type], account_data[:content], expires_in: self.account_data.cache_time)
626
- end
620
+ # Store the account data in cache
621
+ self.account_data.write(account_data[:type], account_data[:content]) if account_data[:type]
622
+
627
623
  fire_account_data(MatrixEvent.new(self, account_data))
628
624
  end
629
625
 
@@ -679,16 +675,7 @@ module MatrixSdk
679
675
  end
680
676
  end
681
677
 
682
- unless cache == :none
683
- account_data.tinycache_adapter.cleanup if instance_variable_defined?(:@account_data) && @account_data
684
- @rooms.each do |_id, room|
685
- # Clean up old cache data after every sync
686
- # TODO Run this in a thread?
687
- room.tinycache_adapter.cleanup
688
- room.account_data.tinycache_adapter.cleanup
689
- room.room_state.tinycache_adapter.cleanup
690
- end
691
- end
678
+ # Rails.cache handles its own cleanup/expiration
692
679
 
693
680
  nil
694
681
  end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'concurrent'
5
+
6
+ module ActiveMatrix
7
+ # Manages a pool of Matrix client connections for efficiency
8
+ class ClientPool
9
+ include Singleton
10
+ include ActiveMatrix::Logging
11
+
12
+ def initialize
13
+ @pools = Concurrent::Hash.new
14
+ @config = ActiveMatrix.config
15
+ @mutex = Mutex.new
16
+ end
17
+
18
+ # Get or create a client for a homeserver
19
+ def get_client(homeserver, **)
20
+ @mutex.synchronize do
21
+ pool = get_or_create_pool(homeserver)
22
+ pool.checkout(**)
23
+ end
24
+ end
25
+
26
+ # Return a client to the pool
27
+ def checkin(client)
28
+ homeserver = client.homeserver
29
+ pool = @pools[homeserver]
30
+ pool&.checkin(client)
31
+ end
32
+
33
+ # Get pool statistics
34
+ def stats
35
+ @pools.map do |homeserver, pool|
36
+ {
37
+ homeserver: homeserver,
38
+ size: pool.size,
39
+ available: pool.available_count,
40
+ in_use: pool.in_use_count
41
+ }
42
+ end
43
+ end
44
+
45
+ # Clear all pools
46
+ def clear!
47
+ @mutex.synchronize do
48
+ @pools.each_value(&:clear!)
49
+ @pools.clear
50
+ end
51
+ end
52
+
53
+ # Shutdown all pools
54
+ def shutdown
55
+ clear!
56
+ end
57
+
58
+ private
59
+
60
+ def get_or_create_pool(homeserver)
61
+ @pools[homeserver] ||= HomeserverPool.new(
62
+ homeserver,
63
+ max_size: @config&.max_clients_per_homeserver || 5,
64
+ timeout: @config&.client_idle_timeout || 5.minutes
65
+ )
66
+ end
67
+
68
+ # Pool for a specific homeserver
69
+ class HomeserverPool
70
+ include ActiveMatrix::Logging
71
+
72
+ attr_reader :homeserver, :max_size, :timeout
73
+
74
+ def initialize(homeserver, max_size:, timeout:)
75
+ @homeserver = homeserver
76
+ @max_size = max_size
77
+ @timeout = timeout
78
+ @available = []
79
+ @in_use = {}
80
+ @mutex = Mutex.new
81
+ @condition = ConditionVariable.new
82
+ end
83
+
84
+ def checkout(**)
85
+ @mutex.synchronize do
86
+ # Try to find an available client
87
+ client = find_available_client
88
+
89
+ # Create new client if needed and pool not full
90
+ client = create_client(**) if client.nil? && @in_use.size < @max_size
91
+
92
+ # Wait for a client if pool is full
93
+ while client.nil?
94
+ @condition.wait(@mutex, 1)
95
+ client = find_available_client
96
+ end
97
+
98
+ # Mark as in use
99
+ @available.delete(client)
100
+ @in_use[client.object_id] = {
101
+ client: client,
102
+ checked_out_at: Time.current
103
+ }
104
+
105
+ client
106
+ end
107
+ end
108
+
109
+ def checkin(client)
110
+ @mutex.synchronize do
111
+ entry = @in_use.delete(client.object_id)
112
+ return unless entry
113
+
114
+ # Add back to available pool if still valid
115
+ if client_valid?(client)
116
+ @available << client
117
+ else
118
+ # Client is no longer valid, don't return to pool
119
+ logger.debug "Discarding invalid client for #{@homeserver}"
120
+ end
121
+
122
+ # Signal waiting threads
123
+ @condition.signal
124
+ end
125
+ end
126
+
127
+ def size
128
+ @mutex.synchronize { @available.size + @in_use.size }
129
+ end
130
+
131
+ def available_count
132
+ @mutex.synchronize { @available.size }
133
+ end
134
+
135
+ def in_use_count
136
+ @mutex.synchronize { @in_use.size }
137
+ end
138
+
139
+ def clear!
140
+ @mutex.synchronize do
141
+ # Stop all clients
142
+ (@available + @in_use.values.map { |e| e[:client] }).each do |client|
143
+ client.stop_listener_thread if client.listening?
144
+ client.logout if client.logged_in?
145
+ rescue StandardError => e
146
+ logger.error "Error cleaning up client: #{e.message}"
147
+ end
148
+
149
+ @available.clear
150
+ @in_use.clear
151
+ end
152
+ end
153
+
154
+ private
155
+
156
+ def find_available_client
157
+ # Remove any expired clients
158
+ @available.select! { |client| client_valid?(client) }
159
+
160
+ # Return first available
161
+ @available.first
162
+ end
163
+
164
+ def create_client(**)
165
+ logger.debug "Creating new client for #{@homeserver}"
166
+
167
+ ActiveMatrix::Client.new(
168
+ @homeserver,
169
+ client_cache: :some,
170
+ sync_filter_limit: 20,
171
+ **
172
+ )
173
+ end
174
+
175
+ def client_valid?(client)
176
+ # Check if client is still connected and responsive
177
+ return false unless client
178
+
179
+ # Could add more validation here
180
+ true
181
+ rescue StandardError
182
+ false
183
+ end
184
+ end
185
+ end
186
+
187
+ # Monkey patch Client to support pooling
188
+ class Client
189
+ # Checkin this client back to the pool
190
+ def checkin_to_pool
191
+ ClientPool.instance.checkin(self) if defined?(ClientPool)
192
+ end
193
+ end
194
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module MatrixSdk
4
- # A generic error raised for issues in the MatrixSdk
3
+ module ActiveMatrix
4
+ # A generic error raised for issues in the ActiveMatrix
5
5
  class MatrixError < StandardError
6
6
  end
7
7
 
@@ -30,9 +30,9 @@ module MatrixSdk
30
30
  @code = error[:errcode]
31
31
  @httpstatus = status
32
32
  @message = error[:error]
33
- @data = error.reject { |k, _v| %i[errcode error].include? k }
33
+ @data = error.except(:errcode, :error)
34
34
 
35
- super error[:error]
35
+ super(error[:error])
36
36
  end
37
37
 
38
38
  def to_s