agent99 0.0.1 → 0.0.3

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 (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
data/lib/agent99/base.rb CHANGED
@@ -36,14 +36,6 @@ class Agent99::Base
36
36
  include Agent99::MessageProcessing
37
37
 
38
38
  MESSAGE_TYPES = %w[request response control]
39
-
40
- CONTROL_HANDLERS = {
41
- 'shutdown' => :handle_shutdown,
42
- 'pause' => :handle_pause,
43
- 'resume' => :handle_resume,
44
- 'update_config' => :handle_update_config,
45
- 'status' => :handle_status_request
46
- }
47
39
 
48
40
  attr_reader :id, :capabilities, :name, :payload, :header, :logger, :queue
49
41
  attr_accessor :registry_client, :message_client
@@ -56,6 +48,12 @@ class Agent99::Base
56
48
  logger.error "#{message}: #{error.message}"
57
49
  logger.debug error.backtrace.join("\n")
58
50
  end
51
+
52
+
53
+ # the final rescue block
54
+ rescue StandardError => e
55
+ handle_error("Unhandled error in Agent99::Base", e)
56
+ exit(2)
59
57
  end
60
58
 
61
59
 
@@ -1,56 +1,89 @@
1
1
  # lib/agent99/control_actions.rb
2
2
 
3
3
 
4
- module Agent99::ControlActions
5
-
6
-
7
- ################################################
8
- private
9
-
10
- # Handles the shutdown control message.
11
- #
12
- def handle_shutdown
13
- logger.info "Received shutdown command. Initiating graceful shutdown..."
14
- send_control_response("Shutting down")
15
- fini
16
- exit(0)
17
- end
4
+ module Agent99
5
+ CONTROL_HANDLERS = {
6
+ 'shutdown' => :handle_shutdown,
7
+ 'pause' => :handle_pause,
8
+ 'resume' => :handle_resume,
9
+ 'update_config' => :handle_update_config,
10
+ 'status' => :handle_status_request,
11
+ 'response' => :handle_control_response,
12
+ }
18
13
 
19
- # Handles the pause control message.
20
- #
21
- def handle_pause
22
- @paused = true
23
- logger.info "Agent paused"
24
- send_control_response("Paused")
25
- end
14
+ module ControlActions
26
15
 
27
- # Handles the resume control message.
28
- #
29
- def handle_resume
30
- @paused = false
31
- logger.info "Agent resumed"
32
- send_control_response("Resumed")
33
- end
34
16
 
35
- # Handles the update_config control message.
36
- #
37
- def handle_update_config
38
- new_config = payload[:config]
39
- @config = new_config
40
- logger.info "Configuration updated: #{@config}"
41
- send_control_response("Configuration updated")
42
- end
17
+ ################################################
18
+ private
19
+
20
+ def handle_control_response
21
+ logger.info "Received control response: #{payload}"
22
+ response_type = payload.dig(:data, :type)
23
+ response_data = payload[:data]
24
+
25
+ case response_type
26
+ when 'status'
27
+ logger.info "Status update from agent: #{response_data}"
28
+ when 'error'
29
+ logger.error "Error from agent: #{response_data[:error]}"
30
+ else
31
+ logger.info "Generic control response: #{payload[:message]}"
32
+ end
33
+ end
34
+
35
+
36
+ # Handles the shutdown control message.
37
+ #
38
+ def handle_shutdown
39
+ logger.info "Received shutdown command. Initiating graceful shutdown..."
40
+ send_control_response("Shutting down")
41
+ fini
42
+ exit(0)
43
+ end
44
+
43
45
 
44
- # Handles the status request control message.
45
- #
46
- def handle_status_request
47
- status = {
48
- id: @id,
49
- name: @name,
50
- paused: @paused,
51
- config: @config,
52
- uptime: (Time.now - @start_time).to_i
53
- }
54
- send_control_response("Status", status)
46
+ # Handles the pause control message.
47
+ #
48
+ def handle_pause
49
+ @paused = true
50
+ logger.info "Agent paused"
51
+ send_control_response("Paused")
52
+ end
53
+
54
+
55
+ # Handles the resume control message.
56
+ #
57
+ def handle_resume
58
+ @paused = false
59
+ logger.info "Agent resumed"
60
+ send_control_response("Resumed")
61
+ end
62
+
63
+
64
+ # Handles the update_config control message.
65
+ #
66
+ def handle_update_config
67
+ new_config = payload[:config]
68
+ @config = new_config
69
+ logger.info "Configuration updated: #{@config}"
70
+ send_control_response("Configuration updated")
71
+ end
72
+
73
+
74
+ # Handles the status request control message.
75
+ #
76
+ def handle_status_request
77
+ status = {
78
+ type: 'status',
79
+ id: @id,
80
+ name: @name,
81
+ paused: @paused,
82
+ config: @config,
83
+ uptime: (Time.now - @start_time).to_i
84
+ }
85
+ send_control_response(status)
86
+ end
55
87
  end
56
88
  end
89
+
@@ -4,7 +4,7 @@ module Agent99::HeaderManagement
4
4
 
5
5
 
6
6
  ################################################
7
- private
7
+ # private
8
8
 
9
9
  def header = @payload[:header]
10
10
  def to_uuid = header[:to_uuid]
@@ -14,9 +14,11 @@ module Agent99::HeaderManagement
14
14
  def type = header[:type]
15
15
 
16
16
  def return_address
17
- header.merge(
18
- to_uuid: from_uuid,
19
- from_uuid: to_uuid,
17
+ return_address = payload[:header].dup
18
+
19
+ return_address.merge(
20
+ to_uuid: return_address[:from_uuid],
21
+ from_uuid: return_address[:to_uuid],
20
22
  timestamp: Agent99::Timestamp.new.to_i,
21
23
  type: 'response'
22
24
  )
@@ -13,3 +13,8 @@ class Agent99::HeaderSchema < SimpleJsonSchemaBuilder::Base
13
13
  integer :timestamp, required: true, examples: [Agent99::Timestamp.new.to_i]
14
14
  end
15
15
  end
16
+
17
+ __END__
18
+
19
+ string :type, required: true, enum: %w[request response control]
20
+
@@ -16,14 +16,15 @@ module Agent99::MessageProcessing
16
16
  #
17
17
  def dispatcher
18
18
  @start_time = Time.now
19
- @paused = false
20
- @config = {}
19
+ @paused = false
20
+ @config = {}
21
+
21
22
 
22
23
  message_client.listen_for_messages(
23
24
  queue,
24
- request_handler: ->(message) { process_request(message) unless paused? },
25
+ request_handler: ->(message) { process_request(message) unless paused? },
25
26
  response_handler: ->(message) { process_response(message) unless paused? },
26
- control_handler: ->(message) { process_control(message) }
27
+ control_handler: ->(message) { process_control(message) }
27
28
  )
28
29
  end
29
30
 
@@ -33,8 +34,8 @@ module Agent99::MessageProcessing
33
34
  # @param message [Hash] The incoming message
34
35
  #
35
36
  def process_request(message)
36
- @payload = message
37
- @header = payload[:header]
37
+ @payload = message
38
+ @header = payload[:header]
38
39
  return unless validate_schema.empty?
39
40
  receive_request
40
41
  end
@@ -54,11 +55,14 @@ module Agent99::MessageProcessing
54
55
  #
55
56
  def process_control(message)
56
57
  @payload = message
57
- receive_control
58
+ if payload[:action] == 'response'
59
+ receive_response
60
+ else
61
+ receive_control
62
+ end
58
63
  end
59
64
 
60
65
 
61
-
62
66
  # Handles incoming request messages (to be overridden by subclasses).
63
67
  #
64
68
  def receive_request
@@ -78,8 +82,9 @@ module Agent99::MessageProcessing
78
82
  # @raise [StandardError] If there's an error processing the control message
79
83
  #
80
84
  def receive_control
81
- action = payload[:action]
82
- handler = CONTROL_HANDLERS[action]
85
+
86
+ action = payload[:action]
87
+ handler = Agent99::CONTROL_HANDLERS[action]
83
88
 
84
89
  if handler
85
90
  send(handler)
@@ -89,7 +94,7 @@ module Agent99::MessageProcessing
89
94
 
90
95
  rescue StandardError => e
91
96
  logger.error "Error processing control message: #{e.message}"
92
- send_control_response("Error", { error: e.message })
97
+ send_control_response({ error: e.message })
93
98
  end
94
99
 
95
100
 
@@ -108,15 +113,16 @@ module Agent99::MessageProcessing
108
113
  # @param message [String] The response message
109
114
  # @param data [Hash, nil] Additional data to include in the response
110
115
  #
111
- def send_control_response(message, data = nil)
116
+ def send_control_response(data)
112
117
  response = {
113
118
  header: return_address.merge(type: 'control'),
114
- message: message,
119
+ action: 'response',
115
120
  data: data
116
121
  }
117
122
  @message_client.publish(response)
118
123
  end
119
124
 
125
+
120
126
  # Validates the incoming message against the defined schema.
121
127
  #
122
128
  # @return [Array] An array of validation errors, empty if validation succeeds
@@ -135,6 +141,7 @@ module Agent99::MessageProcessing
135
141
  e.messages
136
142
  end
137
143
 
144
+
138
145
  # Retrieves a field from the payload or returns a default value.
139
146
  #
140
147
  # @param field [Symbol] The field to retrieve
@@ -144,6 +151,7 @@ module Agent99::MessageProcessing
144
151
  payload[field] || default(field)
145
152
  end
146
153
 
154
+
147
155
  # Returns the default value for a field from the schema.
148
156
  #
149
157
  # @param field [Symbol] The field to get the default for
@@ -35,6 +35,12 @@ class Agent99::RegistryClient
35
35
  send_request(request)
36
36
  end
37
37
 
38
+
39
+ def fetch_all_agents
40
+ request = create_request(:get, "/")
41
+ response = send_request(request)
42
+ end
43
+
38
44
  ################################################
39
45
  private
40
46
 
@@ -48,9 +54,11 @@ class Agent99::RegistryClient
48
54
  response = @http_client.request(request)
49
55
 
50
56
  handle_response(response)
57
+
51
58
  rescue JSON::ParserError => e
52
59
  logger.error "JSON parsing error: #{e.message}"
53
60
  nil
61
+
54
62
  rescue StandardError => e
55
63
  logger.error "Request error: #{e.message}"
56
64
  nil
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Agent99
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.3"
5
5
 
6
6
  def self.version = VERSION
7
7
  end
data/lib/agent99.rb CHANGED
@@ -3,6 +3,10 @@
3
3
  require 'debug_me'
4
4
  include DebugMe
5
5
 
6
+ require 'json'
7
+ require 'json_schema'
8
+ require 'securerandom'
9
+
6
10
  module Agent99; end # Establish a namespace
7
11
 
8
12
  require_relative 'agent99/base'
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: agent99
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-05 00:00:00.000000000 Z
11
+ date: 2024-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: ai_client
14
+ name: bunny
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bunny
28
+ name: nats-pure
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: nats-pure
42
+ name: simple_json_schema_builder
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: simple_json_schema_builder
56
+ name: sinatra
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -152,13 +152,43 @@ files:
152
152
  - LICENSE
153
153
  - README.md
154
154
  - Rakefile
155
- - docs/todo.md
155
+ - docs/README.md
156
+ - docs/advanced_features.md
157
+ - docs/agent_discovery.md
158
+ - docs/agent_lifecycle.md
159
+ - docs/agent_registry_processes.md
160
+ - docs/api_reference.md
161
+ - docs/architecture.md
162
+ - docs/configuration.md
163
+ - docs/control_actions.md
164
+ - docs/custom_agent_implementation.md
165
+ - docs/diagrams/agent_registry_processes.dot
166
+ - docs/diagrams/agent_registry_processes.png
167
+ - docs/diagrams/high_level_architecture.dot
168
+ - docs/diagrams/high_level_architecture.png
169
+ - docs/diagrams/request_flow.dot
170
+ - docs/diagrams/request_flow.png
171
+ - docs/error_handling_and_logging.md
172
+ - docs/extending_the_framework.md
173
+ - docs/message_processing.md
174
+ - docs/messaging_system.md
175
+ - docs/preformance_considerations.md
176
+ - docs/schema_definition.md
177
+ - docs/security.md
178
+ - docs/troubleshooting.md
156
179
  - examples/README.md
157
- - examples/hello_world.rb
158
- - examples/hello_world_client.rb
159
- - examples/hello_world_request.rb
180
+ - examples/agent_watcher.rb
181
+ - examples/agents/.keep
182
+ - examples/chief_agent.rb
183
+ - examples/control.rb
184
+ - examples/diagram.dot
185
+ - examples/diagram.png
186
+ - examples/example_agent.rb
187
+ - examples/kaos_spy.rb
188
+ - examples/maxwell_agent86.rb
189
+ - examples/maxwell_request.rb
160
190
  - examples/registry.rb
161
- - examples/start_agents.sh
191
+ - examples/start_rabbitmq_and_registry.sh
162
192
  - lib/agent99.rb
163
193
  - lib/agent99/.irbrc
164
194
  - lib/agent99/agent_discovery.rb
data/docs/todo.md DELETED
@@ -1,66 +0,0 @@
1
- # Documentation
2
-
3
- TODO: Lets get some more detailed documentation underway that can be linked to from the main README.md file.
4
-
5
- Here are some key areas that should be covered in comprehensive documentation files:
6
-
7
- 1. Architecture Overview:
8
- - Explain the overall structure of the AiAgent framework
9
- - Describe the roles of different components (Base, MessageClient, RegistryClient, etc.)
10
- - Illustrate how agents communicate and interact within the system
11
-
12
- 2. Agent Lifecycle:
13
- - Detail the process of creating, initializing, running, and shutting down an agent
14
- - Explain the registration and withdrawal process with the registry service
15
-
16
- 3. Message Processing:
17
- - Describe the different types of messages (request, response, control)
18
- - Explain how messages are routed and processed
19
- - Detail the schema validation process for incoming messages
20
-
21
- 4. Agent Discovery:
22
- - Explain how agents can discover other agents based on capabilities
23
- - Describe the process of querying the registry for available agents
24
-
25
- 5. Control Actions:
26
- - List and explain all available control actions (shutdown, pause, resume, etc.)
27
- - Describe how to implement custom control actions
28
-
29
- 6. Configuration:
30
- - Detail all configuration options available for agents
31
- - Explain how to use environment variables for configuration
32
-
33
- 7. Error Handling and Logging:
34
- - Describe the error handling mechanisms in place
35
- - Explain how to configure and use the logging system effectively
36
-
37
- 8. Messaging Systems:
38
- - Provide details on both AMQP and NATS messaging systems
39
- - Explain how to switch between different messaging backends
40
-
41
- 9. Custom Agent Implementation:
42
- - Provide a step-by-step guide on creating a custom agent
43
- - Explain how to define capabilities, handle requests, and send responses
44
-
45
- 10. Schema Definition:
46
- - Explain how to define and use request and response schemas
47
- - Provide examples of complex schema definitions
48
-
49
- 11. Performance Considerations:
50
- - Discuss any performance optimizations in the framework
51
- - Provide guidelines for writing efficient agents
52
-
53
- 12. Security:
54
- - Explain any security measures in place (if any)
55
- - Provide best practices for securing agent communications
56
-
57
- 13. Extending the Framework:
58
- - Describe how to add new features or modify existing functionality
59
- - Explain the plugin system (if one exists)
60
-
61
- 14. Troubleshooting:
62
- - Provide a list of common issues and their solutions
63
- - Explain how to debug agents effectively
64
-
65
- 15. API Reference:
66
- - Provide a comprehensive API reference for all public methods and classes
@@ -1,70 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # examples/hello_world_client.rb
3
-
4
- require 'json'
5
- require 'json_schema'
6
- require 'securerandom'
7
- require_relative '../lib/agent99'
8
-
9
- class HelloWorldClient < Agent99::Base
10
- def init
11
- send_request
12
- end
13
-
14
- def send_request
15
- to_uuid = discover_agent(capability: 'greeter', how_many: 1).first[:uuid]
16
-
17
- request = build_request(
18
- to_uuid:,
19
- greeting: 'Hey',
20
- name: 'MadBomber'
21
- )
22
-
23
- result = @message_client.publish(request)
24
- logger.info "Sent request: #{request.inspect}; status? #{result.inspect}"
25
- end
26
-
27
- def build_request(
28
- to_uuid:,
29
- greeting: 'Hello',
30
- name: 'World'
31
- )
32
-
33
- {
34
- header: {
35
- type: 'request',
36
- from_uuid: @id,
37
- to_uuid:,
38
- event_uuid: SecureRandom.uuid,
39
- timestamp: Agent99::Timestamp.new.to_i
40
- },
41
- greeting:,
42
- name:
43
- }
44
- end
45
-
46
-
47
- def receive_response
48
- logger.info "Received response: #{payload.inspect}"
49
- result = payload[:result]
50
-
51
- puts
52
- puts `echo "#{result}" | boxes -d info`
53
- puts
54
-
55
- exit(0)
56
- end
57
-
58
- #####################################################
59
- private
60
-
61
- def capabilities
62
- ['hello_world_client']
63
- end
64
- end
65
-
66
-
67
- # Example usage
68
- client = HelloWorldClient.new
69
- client.run
70
-
@@ -1,20 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Start the registry server
4
- echo "Starting registry server..."
5
- # Assuming the registry server is a separate process, replace with actual command
6
- # e.g., ruby registry_server.rb &
7
- # Replace 'registry_server_command' with the actual command to start your registry server
8
- # Example: ruby registry_server.rb &
9
- ./registry.rb &
10
-
11
- # Start the HelloWorld agent
12
- echo "Starting HelloWorld agent..."
13
- ./hello_world.rb &
14
-
15
- # Start the HelloWorldClient agent
16
- echo "Starting HelloWorldClient agent..."
17
- ./hello_world_client.rb &
18
-
19
- # Wait for all background processes to finish
20
- wait