agent99 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/README.md +46 -2
  4. data/docs/README.md +57 -0
  5. data/docs/advanced_features.md +110 -0
  6. data/docs/agent_discovery.md +62 -0
  7. data/docs/agent_lifecycle.md +137 -0
  8. data/docs/agent_registry_processes.md +102 -0
  9. data/docs/api_reference.md +136 -0
  10. data/docs/architecture.md +77 -0
  11. data/docs/configuration.md +17 -0
  12. data/docs/control_actions.md +179 -0
  13. data/docs/custom_agent_implementation.md +30 -0
  14. data/docs/diagrams/agent_registry_processes.dot +42 -0
  15. data/docs/diagrams/agent_registry_processes.png +0 -0
  16. data/docs/diagrams/high_level_architecture.dot +26 -0
  17. data/docs/diagrams/high_level_architecture.png +0 -0
  18. data/docs/diagrams/request_flow.dot +42 -0
  19. data/docs/diagrams/request_flow.png +0 -0
  20. data/docs/error_handling_and_logging.md +13 -0
  21. data/docs/extending_the_framework.md +11 -0
  22. data/docs/message_processing.md +165 -0
  23. data/docs/messaging_system.md +129 -0
  24. data/docs/preformance_considerations.md +9 -0
  25. data/docs/schema_definition.md +78 -0
  26. data/docs/security.md +9 -0
  27. data/docs/troubleshooting.md +11 -0
  28. data/examples/README.md +65 -35
  29. data/examples/agent_watcher.rb +102 -0
  30. data/examples/agents/.keep +0 -0
  31. data/examples/chief_agent.rb +96 -0
  32. data/examples/control.rb +136 -0
  33. data/examples/diagram.dot +22 -0
  34. data/examples/diagram.png +0 -0
  35. data/examples/example_agent.rb +26 -0
  36. data/examples/kaos_spy.rb +63 -0
  37. data/examples/{hello_world.rb → maxwell_agent86.rb} +38 -18
  38. data/examples/{hello_world_request.rb → maxwell_request.rb} +2 -2
  39. data/examples/registry.rb +8 -7
  40. data/examples/start_rabbitmq_and_registry.sh +29 -0
  41. data/lib/agent99/agent_lifecycle.rb +14 -15
  42. data/lib/agent99/amqp_message_client.rb +41 -10
  43. data/lib/agent99/base.rb +6 -8
  44. data/lib/agent99/control_actions.rb +80 -47
  45. data/lib/agent99/header_management.rb +6 -4
  46. data/lib/agent99/header_schema.rb +5 -0
  47. data/lib/agent99/message_processing.rb +21 -13
  48. data/lib/agent99/registry_client.rb +8 -0
  49. data/lib/agent99/version.rb +1 -1
  50. data/lib/agent99.rb +4 -0
  51. metadata +41 -11
  52. data/docs/todo.md +0 -66
  53. data/examples/hello_world_client.rb +0 -70
  54. data/examples/start_agents.sh +0 -20
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/chief_agent.rb
3
+ #
4
+ # This program is a type :client agent.
5
+ #
6
+ # This program calls a CLI program named boxes to highlight
7
+ # the response received to the request that it sent.
8
+ #
9
+ # brew install boxes
10
+ #
11
+ # Run this program several times to see if Maxwell Agent86
12
+ # messes up his mission which he is prone to do half the time.
13
+
14
+ require_relative '../lib/agent99'
15
+
16
+ class ChiefAgent < Agent99::Base
17
+ TYPE = :client
18
+
19
+ # init is called at the end of the initialization process.
20
+ # It may be only something that a :client type agent would do.
21
+ #
22
+ # For this client it sends out a request as its first order of
23
+ # business and expects to receive a response.
24
+ #
25
+ def init
26
+ action = 'greeter'
27
+ agent = discover_agent(
28
+ capability: action,
29
+ how_many: 1
30
+ ).first[:uuid]
31
+
32
+ send_request(agent:)
33
+
34
+ rescue Exception => e
35
+ logger.warn "No Agents are available as #{action}"
36
+ exit(1)
37
+ end
38
+
39
+
40
+ ##################################################
41
+ private
42
+
43
+ def send_request(agent:)
44
+ request = build_request(
45
+ to_uuid: agent,
46
+ greeting: 'Hey',
47
+ name: 'MadBomber'
48
+ )
49
+
50
+ result = @message_client.publish(request)
51
+ logger.info "Sent request: #{request.inspect}; status? #{result.inspect}"
52
+ end
53
+
54
+
55
+ def build_request(
56
+ to_uuid:,
57
+ greeting: 'Hello',
58
+ name: 'World'
59
+ )
60
+
61
+ {
62
+ header: {
63
+ type: 'request',
64
+ from_uuid: @id,
65
+ to_uuid: ,
66
+ event_uuid: SecureRandom.uuid,
67
+ timestamp: Agent99::Timestamp.new.to_i
68
+ },
69
+ greeting:,
70
+ name:
71
+ }
72
+ end
73
+
74
+
75
+ def receive_response
76
+ logger.info "Received response: #{payload.inspect}"
77
+ result = payload[:result]
78
+
79
+ puts
80
+ puts `echo "#{result}" | boxes -d info`
81
+ puts
82
+
83
+ exit(0)
84
+ end
85
+
86
+
87
+ def capabilities
88
+ ['Chief of Control']
89
+ end
90
+ end
91
+
92
+
93
+ # Example usage
94
+ client = ChiefAgent.new
95
+ client.run
96
+
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+ # control.rb
3
+
4
+ require_relative '../lib/agent99'
5
+
6
+ class Control < Agent99::Base
7
+ TYPE = :hybrid
8
+
9
+ attr_accessor :statuses
10
+
11
+ def init
12
+ @agents = @registry_client.fetch_all_agents
13
+ @statuses = {}
14
+ end
15
+
16
+
17
+ def capabilities
18
+ ['control', 'headquarters', 'secret underground base']
19
+ end
20
+
21
+
22
+ def send_control_message(message:, payload: {})
23
+ @agents.each do |agent|
24
+ response = @message_client.publish(
25
+ header: {
26
+ to_uuid: agent[:uuid],
27
+ from_uuid: @id,
28
+ event_uuid: SecureRandom.uuid,
29
+ type: 'control',
30
+ timestamp: Agent99::Timestamp.new.to_i
31
+ },
32
+ action: message,
33
+ payload: payload
34
+ )
35
+ puts "Sent #{message} to #{agent[:name]}: #{response[:success] ? 'Success' : 'Failed'}"
36
+ end
37
+ end
38
+
39
+
40
+ def pause_all
41
+ send_control_message(message: 'pause')
42
+ end
43
+
44
+
45
+ def resume_all
46
+ send_control_message(message: 'resume')
47
+ end
48
+
49
+
50
+ def stop_all
51
+ send_control_message(message: 'stop')
52
+ end
53
+
54
+ def get_all_status
55
+ @statuses.clear # Reset statuses before new request
56
+
57
+ @agents.each do |agent|
58
+ @message_client.publish(
59
+ header: {
60
+ to_uuid: agent[:uuid],
61
+ from_uuid: @id,
62
+ event_uuid: SecureRandom.uuid,
63
+ type: 'control',
64
+ timestamp: Agent99::Timestamp.new.to_i
65
+ },
66
+ action: 'status'
67
+ )
68
+ end
69
+
70
+ # Wait for responses (with timeout)
71
+ sleep 2 # Give agents time to respond
72
+ @statuses
73
+ end
74
+
75
+ def receive_response
76
+ if payload[:action] == 'response' && payload[:data][:type] == 'status'
77
+ agent_name = payload[:header][:from_uuid]
78
+ @statuses[agent_name] = payload[:data]
79
+ logger.info "Received status from #{agent_name}: #{payload[:data]}"
80
+ elsif payload[:action] == 'status' && payload[:header][:from_uuid] == @id
81
+ # Handle our own status request
82
+ handle_status_request
83
+ end
84
+ end
85
+
86
+
87
+ end
88
+
89
+
90
+ if __FILE__ == $PROGRAM_NAME
91
+ control = Control.new
92
+
93
+ # Start the message processing in a separate thread
94
+ dispatcher_thread = Thread.new do
95
+ control.run
96
+ end
97
+
98
+ # UI thread
99
+ begin
100
+ loop do
101
+ puts "\n1. Pause all agents"
102
+ puts "2. Resume all agents"
103
+ puts "3. Stop all agents"
104
+ puts "4. Get all agents status"
105
+ puts "5. Exit"
106
+ print "\nEnter your choice: "
107
+
108
+ choice = gets.chomp.to_i
109
+
110
+ case choice
111
+ when 1
112
+ control.pause_all
113
+ when 2
114
+ control.resume_all
115
+ when 3
116
+ control.stop_all
117
+ when 4
118
+ statuses = control.get_all_status
119
+ sleep 2 # Give time for responses to arrive
120
+ puts JSON.pretty_generate(control.statuses)
121
+ when 5
122
+ puts "Exiting..."
123
+ Thread.exit
124
+ break
125
+ else
126
+ puts "Invalid choice. Please try again."
127
+ end
128
+ end
129
+ rescue Interrupt
130
+ puts "\nShutting down..."
131
+ ensure
132
+ dispatcher_thread.exit
133
+ control.fini
134
+ end
135
+ end
136
+
@@ -0,0 +1,22 @@
1
+ digraph agent_interaction {
2
+ rankdir=LR; // Left to right layout
3
+
4
+ // Define nodes for each component
5
+ node [shape=box];
6
+ ChiefAgent [label="ChiefAgent"];
7
+ MaxwellAgent86 [label="MaxwellAgent86"];
8
+ Registry [label="Registry Service"];
9
+
10
+ // Define edges for interactions
11
+ ChiefAgent -> Registry [label="GET /discover (greeter)"];
12
+ Registry -> ChiefAgent [label="Agent UUIDs"];
13
+
14
+ ChiefAgent -> MaxwellAgent86 [label="Send greet request"];
15
+ MaxwellAgent86 -> ChiefAgent [label="Response (Hello World)"];
16
+
17
+ MaxwellAgent86 -> Registry [label="POST /register"];
18
+ Registry -> MaxwellAgent86 [label="ACK (registration)"];
19
+
20
+ ChiefAgent -> Registry [label="POST /register"];
21
+ Registry -> ChiefAgent [label="ACK (registration)"];
22
+ }
Binary file
@@ -0,0 +1,26 @@
1
+ # examples/example_agent.rb
2
+ #
3
+ # NOTE: This agent is meant to be loaded
4
+ # by the agent_watcher.rb be file.
5
+ # To do that first have AgentWatcher running
6
+ # then `cp example_agent.rb agents`
7
+ # AgentWatcher will see the new file arrive
8
+ # in the `agents` folder, will determine that the
9
+ # new file contains an Agent99 subclass, will
10
+ # load it, create a new instance of the class and
11
+ # finally run the new instance within its own
12
+ # thread as part of the AgentWatcher process.
13
+ #
14
+
15
+ require_relative '../../lib/agent99'
16
+
17
+ class ExampleAgent < Agent99::Base
18
+ TYPE = :server
19
+
20
+ def capabilities = %w[ rubber_stamp yes_man example ]
21
+
22
+ def receive_request
23
+ logger.info "Example agent received request: #{payload}"
24
+ send_response(status: 'success')
25
+ end
26
+ end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/kaos_spy.rb
3
+ #
4
+ # KAOS stood for "Kreatively Akting Out Simultaneously."
5
+
6
+ require 'agent99'
7
+
8
+ # Kaos captured Agent99 and forced her to reveal the
9
+ # secrets of the centralized registry and the communication
10
+ # network used by Control. Max was not there to save her.
11
+
12
+ require 'tty-table'
13
+
14
+ class KaosSpy
15
+ # TODO: spread some choas!
16
+
17
+ attr_reader :registry, :comms, :agents
18
+
19
+ def initialize
20
+ @registry = Agent99::RegistryClient.new
21
+ @agents = registry.fetch_all_agents
22
+ dox_control_agents
23
+
24
+ @comms = Agent99::AmqpMessageClient.new
25
+ take_out_communications
26
+ end
27
+
28
+ def dox_control_agents
29
+ if agents.empty?
30
+ puts "\nKAOS won! There are no Control agents in the field."
31
+ else
32
+ report = [ %w[Name Address Capabilities] ]
33
+
34
+ agents.each{|agent|
35
+ report << [
36
+ agent[:name],
37
+ agent[:uuid],
38
+ agent[:capabilities].join(', ')
39
+ ]
40
+ }
41
+
42
+ table = TTY::Table.new(report[0], report[1..])
43
+ puts table.render(:unicode)
44
+ end
45
+ end
46
+
47
+ def take_out_communications
48
+ puts
49
+ puts "Destroy Control's Comms Network ..."
50
+ puts
51
+
52
+ agents.each do |agent|
53
+ comms.delete_queue(agent[:uuid])
54
+ puts " Agent #{agent[:name]} cannot make or receive calls@"
55
+ end
56
+ end
57
+ end
58
+
59
+ KaosSpy.new
60
+ puts
61
+ puts "That's all it takes - Get Smart; get security!"
62
+ puts
63
+
@@ -1,22 +1,31 @@
1
1
  #!/usr/bin/env ruby
2
- # examples/hello_world.rb
2
+ # examples/maxwell_agent86.rb
3
3
 
4
- require 'json'
5
- require 'json_schema'
4
+ # There are three types of agents: Server, Client and Hybrid.
5
+ # A Server receives a requests and _may_ send a response.
6
+ # A Client sends a request and _may_ expect a response.
7
+ # A Hybrid _may_ act like a Server or a Client
6
8
 
7
9
  require_relative '../lib/agent99'
8
- require_relative 'hello_world_request'
10
+ require_relative 'maxwell_request'
11
+
12
+ class MaxwellAgent86 < Agent99::Base
13
+ REQUEST_SCHEMA = MaxwellRequest.schema
14
+ TYPE = :server
9
15
 
10
- class HelloWorld < Agent99::Base
11
- REQUEST_SCHEMA = HelloWorldRequest.schema
12
16
  # RESPONSE_SCHEMA = Agent99::RESPONSE.schema
13
17
  # ERROR_SCHEMA = Agent99::ERROR.schema
14
18
 
19
+
20
+ #######################################
21
+ private
22
+
15
23
  # The request is in @payload
16
24
  def receive_request
17
25
  send_response( validate_request || process )
18
26
  end
19
27
 
28
+
20
29
  # This method validates the incoming request and returns any errors found
21
30
  # or nil if there are no errors.
22
31
  # It allows for returning an array of errors.
@@ -44,6 +53,7 @@ class HelloWorld < Agent99::Base
44
53
  }
45
54
  end
46
55
 
56
+
47
57
  # Validate the incoming request body against the schema
48
58
  validation_errors = validate_schema
49
59
  unless validation_errors.empty?
@@ -57,6 +67,7 @@ class HelloWorld < Agent99::Base
57
67
  responses.empty? ? nil : responses
58
68
  end
59
69
 
70
+
60
71
  # Returns the response value
61
72
  # All response message have the same schema in that
62
73
  # they have a header (all messages have headers) and
@@ -64,9 +75,13 @@ class HelloWorld < Agent99::Base
64
75
  # a JSON string, sure but then we would need a
65
76
  # RESPONSE_SCHEMA constant for the class.
66
77
  def process
67
- {
68
- result: get(:greeting) + ' ' + get(:name)
69
- }
78
+ result = if 50 <= rand(100)
79
+ get(:greeting) + ' ' + get(:name)
80
+ else
81
+ 'Missed it by that >< much.'
82
+ end
83
+
84
+ { result: result }
70
85
  end
71
86
 
72
87
 
@@ -76,16 +91,21 @@ class HelloWorld < Agent99::Base
76
91
  loger.warn("Unexpected response type message: response.inspect")
77
92
  end
78
93
 
79
- private
80
94
 
81
- # NOTE: what I'm thinking about here is similar to the
82
- # prompt tool (aka function) callback facility
83
- # where descriptive text is used to describe
84
- # what the tool does.
95
+ # Phase One Implementation is to do a search
96
+ # using the String#include? and the Array#include?
97
+ # methods. If you want discrete word-based selection
98
+ # then use an Array of Strings to define the different
99
+ # things this agent can do.
100
+ #
101
+ # If you want to match on sub-strings then define the
102
+ # the capabilities as a String.
103
+ #
104
+ # Subsequent implementations may use a semantic search
105
+ # to find the agents to use in which case capabilities may
106
+ # be constrained to be a String.
85
107
  #
86
- # TODO: scale this idea back to just keywords
87
- # until the registry program gets more
88
- # stuff added to its discovery process.
108
+ # For now, lets just go with the Array of Strings.
89
109
  #
90
110
  def capabilities
91
111
  %w[ greeter hello_world hello-world hello]
@@ -93,5 +113,5 @@ class HelloWorld < Agent99::Base
93
113
  end
94
114
 
95
115
  # Example usage
96
- agent = HelloWorld.new
116
+ agent = MaxwellAgent86.new
97
117
  agent.run # Starts listening for messages
@@ -1,8 +1,8 @@
1
- # examples/hello_world_request.rb
1
+ # examples/maxwell_request.rb
2
2
 
3
3
  require_relative '../lib/agent99/header_schema'
4
4
 
5
- class HelloWorldRequest < SimpleJsonSchemaBuilder::Base
5
+ class MaxwellRequest < SimpleJsonSchemaBuilder::Base
6
6
  object do
7
7
  object :header, schema: Agent99::HeaderSchema
8
8
 
data/examples/registry.rb CHANGED
@@ -6,11 +6,13 @@ include DebugMe
6
6
 
7
7
  require 'sinatra'
8
8
  require 'json'
9
- require 'bunny'
10
9
  require 'securerandom'
11
10
 
12
- # In-memory registry to store agent capabilities
13
- # Array(Hash)
11
+ # In-memory registry to store agent Array(Hash)
12
+ #
13
+ # Agent capabilities are save as lower case. The
14
+ # discovery process also compares content as lower case.
15
+ #
14
16
  # TODO: change this data store to a sqlite database
15
17
  # maybe with a vector search capability.
16
18
  #
@@ -26,12 +28,11 @@ end
26
28
  post '/register' do
27
29
  request.body.rewind
28
30
  agent_info = JSON.parse(request.body.read, symbolize_names: true)
29
-
30
31
  agent_name = agent_info[:name]
31
- capabilities = agent_info[:capabilities]
32
-
33
32
  agent_uuid = SecureRandom.uuid
34
33
 
34
+ agent_info[:capabilities].map!{|c| c.downcase}
35
+
35
36
  AGENT_REGISTRY << agent_info.merge({uuid: agent_uuid})
36
37
 
37
38
  status 201
@@ -43,7 +44,7 @@ end
43
44
  # TODO: This is a simple keyword matcher. Looking
44
45
  # => for a semantic match process.
45
46
  get '/discover' do
46
- capability = params['capability']
47
+ capability = params['capability'].downcase
47
48
 
48
49
  matching_agents = AGENT_REGISTRY.select do |agent|
49
50
  agent[:capabilities].include?(capability)
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ # examples/start_rabbitmq_and_registry.sh
3
+ #
4
+ # brew install rabbitmq-server
5
+ #
6
+ # Start up the AMQP message broker (RabbitM@) in the background
7
+
8
+ echo "Starting rabbitmq-server in background ..."
9
+ rabbitmq-server &
10
+ sleep 2
11
+ open http://localhost:15672/#/queues
12
+
13
+ echo
14
+ echo
15
+ echo "Starting example registry in forground ..."
16
+ echo "http://localhost:4567"
17
+ echo
18
+ ruby registry.rb # blocks until control-C
19
+
20
+ # You may have to do control-c twice to stop both
21
+
22
+ echo "#"
23
+ echo "##"
24
+ echo "###"
25
+ echo "####"
26
+ echo "#####"
27
+ echo "registry is stopped"
28
+ echo "stopping the rabbitmq server ...."
29
+ rabbitmqctl stop
@@ -48,9 +48,21 @@ module Agent99::AgentLifecycle
48
48
  end
49
49
 
50
50
 
51
+ # Performs cleanup operations when the agent is shutting down.
52
+ #
53
+ def fini
54
+ if id
55
+ queue_name = id
56
+ withdraw
57
+ @message_client&.delete_queue(queue_name)
58
+ else
59
+ logger.warn('fini called with a nil id')
60
+ end
61
+ end
62
+
51
63
  ################################################
52
64
  private
53
-
65
+
54
66
  # Checks if the agent is currently paused.
55
67
  #
56
68
  # @return [Boolean] True if the agent is paused, false otherwise
@@ -71,17 +83,4 @@ module Agent99::AgentLifecycle
71
83
  end
72
84
  end
73
85
  end
74
-
75
-
76
- # Performs cleanup operations when the agent is shutting down.
77
- #
78
- def fini
79
- if id
80
- queue_name = id
81
- withdraw
82
- @message_client&.delete_queue(queue_name)
83
- else
84
- logger.warn('fini called with a nil id')
85
- end
86
- end
87
- end
86
+ end
@@ -6,6 +6,19 @@ require 'json_schema'
6
6
  require 'logger'
7
7
 
8
8
  class Agent99::AmqpMessageClient
9
+ CONFIG = {
10
+ host: "127.0.0.1",
11
+ port: 5672,
12
+ ssl: false,
13
+ vhost: "/",
14
+ user: "guest",
15
+ pass: "guest",
16
+ heartbeat: :server, # will use RabbitMQ setting
17
+ frame_max: 131072,
18
+ auth_mechanism: "PLAIN"
19
+ }
20
+
21
+
9
22
  QUEUE_TTL = 60_000 # 60 seconds TTL
10
23
  @instance = nil
11
24
 
@@ -17,16 +30,21 @@ class Agent99::AmqpMessageClient
17
30
 
18
31
  attr_accessor :logger, :channel, :exchange
19
32
 
20
- def initialize(logger: Logger.new($stdout))
33
+ def initialize(
34
+ config: CONFIG,
35
+ logger: Logger.new($stdout))
36
+ @config = config
21
37
  @connection = create_amqp_connection
22
- @channel = @connection.create_channel
23
- @exchange = @channel.default_exchange
24
- @logger = logger
38
+ @channel = @connection.create_channel
39
+ @exchange = @channel.default_exchange
40
+ @logger = logger
25
41
  end
26
42
 
27
43
  def setup(agent_id:, logger:)
28
44
  queue = create_queue(agent_id)
29
45
 
46
+ logger.info "Created queue for agent_id: #{agent_id}"
47
+
30
48
  # Returning the queue to be used in the Base class
31
49
  queue
32
50
  end
@@ -66,26 +84,39 @@ class Agent99::AmqpMessageClient
66
84
  queue_name = message.dig(:header, :to_uuid)
67
85
 
68
86
  begin
87
+ # FIXME: message.to_json
69
88
  json_payload = JSON.generate(message)
70
89
 
71
90
  exchange.publish(json_payload, routing_key: queue_name)
72
91
 
73
- logger.info "Message published successfully to queue: #{queue_name}"
92
+ logger.info "#{message.dig(:header,:type).to_s.upcase} message published successfully to queue: #{queue_name}"
74
93
 
75
94
  # Return a success status
76
- { success: true, message: "Message published successfully" }
95
+ {
96
+ success: true,
97
+ message: "Message published successfully"
98
+ }
77
99
 
78
100
  rescue JSON::GeneratorError => e
79
101
  logger.error "Failed to convert payload to JSON: #{e.message}"
80
- { success: false, error: "JSON conversion error: #{e.message}" }
102
+ {
103
+ success: false,
104
+ error: "JSON conversion error: #{e.message}"
105
+ }
81
106
 
82
107
  rescue Bunny::ConnectionClosedError, Bunny::ChannelAlreadyClosed => e
83
108
  logger.error "Failed to publish message: #{e.message}"
84
- { success: false, error: "Publishing error: #{e.message}" }
109
+ {
110
+ success: false,
111
+ error: "Publishing error: #{e.message}"
112
+ }
85
113
 
86
114
  rescue StandardError => e
87
115
  logger.error "Unexpected error while publishing message: #{e.message}"
88
- { success: false, error: "Unexpected error: #{e.message}" }
116
+ {
117
+ success: false,
118
+ error: "Unexpected error: #{e.message}"
119
+ }
89
120
  end
90
121
  end
91
122
 
@@ -109,7 +140,7 @@ class Agent99::AmqpMessageClient
109
140
  private
110
141
 
111
142
  def create_amqp_connection
112
- Bunny.new.tap(&:start)
143
+ Bunny.new(@config).tap(&:start)
113
144
  rescue Bunny::TCPConnectionFailed, StandardError => e
114
145
  logger.error "Failed to connect to AMQP: #{e.message}"
115
146
  raise "AMQP Connection Error: #{e.message}. Please check your AMQP server and try again."