agent99 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/README.md +42 -0
- data/examples/README.md +22 -35
- data/examples/chief_agent.rb +96 -0
- data/examples/control.rb +136 -0
- data/examples/diagram.dot +22 -0
- data/examples/diagram.png +0 -0
- data/examples/{hello_world.rb → maxwell_agent86.rb} +38 -18
- data/examples/{hello_world_request.rb → maxwell_request.rb} +2 -2
- data/examples/registry.rb +0 -1
- data/examples/start_rabbitmq_and_registry.sh +29 -0
- data/lib/agent99/agent_lifecycle.rb +14 -15
- data/lib/agent99/amqp_message_client.rb +41 -10
- data/lib/agent99/base.rb +6 -8
- data/lib/agent99/control_actions.rb +80 -47
- data/lib/agent99/header_management.rb +6 -4
- data/lib/agent99/message_processing.rb +21 -13
- data/lib/agent99/registry_client.rb +8 -0
- data/lib/agent99/version.rb +1 -1
- data/lib/agent99.rb +4 -0
- metadata +9 -20
- data/examples/hello_world_client.rb +0 -70
- data/examples/start_agents.sh +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eeb28a9ea8730c5ff33d5722f4dad0d0bcec6a54d58784203d1c147b64b17223
|
4
|
+
data.tar.gz: c27cb24f7ee720f758b94a32bd5d33e1ea5a92dc490022122cc40eaaa736620e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc796885c649aa669f77954eb25c5a9e8667d5aff6958054b53ca589fd3e620bb03ba3fce5a4b707bc6d2bd67ee2672291d1e4a0e0ff32b5d1d06912766492b0
|
7
|
+
data.tar.gz: f01c4e6d5a3353c722f87c965faaa6abede17ed57861a9b4fc82829e0c5a33d4bb483eb47420b9cef22bd697fb17d2e7e437bf9a2e5314fc07ab208bfb9d374d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
|
3
|
+
|
4
|
+
## Released
|
5
|
+
|
6
|
+
### [0.0.2] - 2024-12-07
|
7
|
+
|
8
|
+
- Added examples/control.rb
|
9
|
+
- request status from all agents
|
10
|
+
- other control messages not worked on yet
|
11
|
+
|
12
|
+
### [0.0.1] - 2024-11-28
|
4
13
|
|
5
14
|
- Initial release
|
data/README.md
CHANGED
@@ -4,6 +4,48 @@
|
|
4
4
|
|
5
5
|
Agent99 is a Ruby-based framework for building and managing AI agents in a distributed system. It provides a robust foundation for creating intelligent agents that can communicate, discover each other, and perform various tasks.
|
6
6
|
|
7
|
+
## Hype!
|
8
|
+
|
9
|
+
🌟 **Introducing Agent99**: Your Friendly Ruby Sidekick for Building Intelligent Software Agents! 🌟
|
10
|
+
|
11
|
+
Do you ever miss the charm and wit of Barbara Feldon's iconic character from *Get Smart*? Just as Agent 99 was always ready to tackle mischief and save the day, **Agent99** is here to help you create your very own software agents that can communicate, collaborate, and conquer tasks in a smart and efficient way!
|
12
|
+
|
13
|
+
### Why Choose Agent99?
|
14
|
+
|
15
|
+
🔍 **Independent Agents, Unified Communication**: Say goodbye to complex service orchestration! Agent99 empowers you to build micro-services that communicate seamlessly through a robust peer-to-peer messaging network. Each agent operates independently while working together like a well-oiled machine!
|
16
|
+
|
17
|
+
🤖 **Smart Automation for the Modern Age**: In the era of AI, why settle for less? With Agent99, you can develop software agents that respond intelligently to incoming requests—just like a secret agent reacting to their next mission! Deploy your own digital spies to gather data, complete tasks, and more.
|
18
|
+
|
19
|
+
📦 **Quick Setup & Easy Integration**: Just like Agent 99’s quick thinking, our library is designed for rapid development. Get up and running in no time, whether you’re building a small project or an enterprise-level solution!
|
20
|
+
|
21
|
+
|
22
|
+
### Features that Make Agent99 a Must-Have:
|
23
|
+
|
24
|
+
- 🌐 **Peer-to-Peer Messaging**: Ensure that your agents can communicate effortlessly, just like Agent 99 and Max.
|
25
|
+
- 🚀 **Agile Development**: Design and deploy agents at lightning speed, giving you the freedom to innovate.
|
26
|
+
- 🔒 **Secure Communication**: Built with security in mind, because even our agents deserve to keep their secrets safe.
|
27
|
+
- 📊 **Scalability**: Expand your network of agents as your needs grow—no mission is too big!
|
28
|
+
|
29
|
+
### Get Smart! Join the Revolution!
|
30
|
+
|
31
|
+
Whether you’re a seasoned Ruby developer or just getting started, Agent99 provides the tools you need to build your very own quirky, intelligent agents. Just like Agent 99, your agents will be clever, adaptable, and ready to tackle any challenge!
|
32
|
+
|
33
|
+
### How to Get Started:
|
34
|
+
|
35
|
+
1. **Install**: Simply add Agent99 to your Gemfile and run `bundle install`.
|
36
|
+
2. **Create an Agent**: Use our simple API to define and deploy your first agent.
|
37
|
+
3. **Watch Them Work**: Sit back and relax as your agents spring into action, communicating with one another to accomplish tasks that would impress even the chief of CONTROL!
|
38
|
+
|
39
|
+
### Spread the Word!
|
40
|
+
|
41
|
+
**Join the Agent99 community** on GitHub and share your experiences, tips, and feedback. Let’s build a world of smart agents together! And remember, just like Agent 99 always had Max’s back, we’ve got yours during your development journey.
|
42
|
+
|
43
|
+
🕵️♂️ **Become an Agent!** Dive into the world of Agent99 today: [GitHub Repository](#) 📖
|
44
|
+
|
45
|
+
**Agent99** – Because when it comes to building software agents, it's all about being smart!
|
46
|
+
|
47
|
+
... end of the Hype; now back to normal.
|
48
|
+
|
7
49
|
## Features
|
8
50
|
|
9
51
|
- Agent Lifecycle Management: Easy setup and teardown of agents
|
data/examples/README.md
CHANGED
@@ -1,41 +1,36 @@
|
|
1
|
-
# Agent99 Framework Examples
|
2
|
-
|
3
|
-
- TODO
|
4
|
-
- Review and edit
|
5
|
-
- Add instructions for brew install rabbitmq nats-server boxes
|
6
|
-
- review example agents to see code can be tightened
|
1
|
+
# Agent99 Framework Examples - 86 and the Chief
|
7
2
|
|
8
3
|
This folder contains example implementations using the Agent99 framework. The framework provides a foundation for building AI agents that can communicate with each other through a message-based system.
|
9
4
|
|
10
5
|
## Files
|
11
6
|
|
12
|
-
### 1.
|
7
|
+
### 1. maxwell_agent86.rb
|
13
8
|
|
14
|
-
This file demonstrates a basic
|
9
|
+
This file demonstrates a basic agent implementation using the Agent99 framework.
|
15
10
|
|
16
|
-
- Class: `
|
11
|
+
- Class: `MaxwellAgent86 < Agent99::Base`
|
17
12
|
- Functionality: Responds to "hello world" requests
|
18
13
|
- Key methods:
|
19
14
|
- `receive_request`: Handles incoming requests
|
20
15
|
- `validate_request`: Validates the request against a schema
|
21
16
|
- `process`: Generates the response
|
22
17
|
|
23
|
-
### 2.
|
18
|
+
### 2. chief_agent.rb
|
24
19
|
|
25
|
-
This file shows how to create a client that interacts with the
|
20
|
+
This file shows how to create a client that interacts with the MaxwellAgent86 agent.
|
26
21
|
|
27
|
-
- Class: `
|
28
|
-
- Functionality: Sends a request to a
|
22
|
+
- Class: `ChiefAgent < Agent99::Base`
|
23
|
+
- Functionality: Sends a request to an agent who can be a greeter and processes the response
|
29
24
|
- Key methods:
|
30
25
|
- `init`: Initiates the request sending process
|
31
26
|
- `send_request`: Builds and sends the request
|
32
27
|
- `receive_response`: Handles the response from the HelloWorld agent
|
33
28
|
|
34
|
-
### 3.
|
29
|
+
### 3. mexwell_request.rb
|
35
30
|
|
36
|
-
This file defines the schema for
|
31
|
+
This file defines the schema for MaxwellAgent86 requests using SimpleJsonSchemaBuilder.
|
37
32
|
|
38
|
-
- Class: `
|
33
|
+
- Class: `MaxwellRequest < SimpleJsonSchemaBuilder::Base`
|
39
34
|
- Defines the structure of a valid HelloWorld request
|
40
35
|
|
41
36
|
### 4. registry.rb
|
@@ -52,28 +47,20 @@ This file implements a simple registry service for AI agents using Sinatra.
|
|
52
47
|
|
53
48
|
## Usage
|
54
49
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
From the examples directory you will need to start three different processes. You will want to keep them all in the forgound so it would be best to start them in different terminal windows.
|
51
|
+
|
52
|
+
Start the sample registry first: `./registry.rb`
|
53
|
+
|
54
|
+
Then start the service agent: `./maxwell_agent86.rb`
|
55
|
+
Maxwell will will register itself, get its UUID and setup a message queue to which it will listen for its service requests.
|
59
56
|
|
60
|
-
|
61
|
-
|
62
|
-
ruby hello_world.rb
|
63
|
-
```
|
57
|
+
Finally start the chief agent in charge: `./chief_agent.rb`
|
58
|
+
The Chief also registers itself but no other agent can give the Chief missions. That's his job. The chief gets his mission UUID, sets up a message queue to listen for the reports from the field after he sends out his request (aka order)
|
64
59
|
|
65
|
-
|
66
|
-
```
|
67
|
-
ruby hello_world_client.rb
|
68
|
-
```
|
60
|
+
But first the Chief asks the registry for the UUIDs of all agents who can handle a "greeter" request. The Chief selects one of those agents and sends the agent a greet request. The Chief then waits for a response to the request. When it comes in, the chiefs displays the response and terminates.
|
69
61
|
|
70
|
-
|
62
|
+
Run the chief a few times in a roll. Some times the agent to whom the Chief issues his requests does not always respond the you would expect.
|
71
63
|
|
72
|
-
|
73
|
-
- Gems: json, json_schema, sinatra, bunny, securerandom
|
64
|
+

|
74
65
|
|
75
|
-
## Notes
|
76
66
|
|
77
|
-
- The framework uses modern Ruby 3.3 syntax, especially for hashes and method signatures with named parameters.
|
78
|
-
- The examples demonstrate basic usage of the Agent99 framework, including request/response handling, validation, and agent discovery.
|
79
|
-
- The registry service uses an in-memory store (AGENT_REGISTRY) for simplicity, but it's recommended to use a more robust solution like SQLite for production use.
|
@@ -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
|
+
|
data/examples/control.rb
ADDED
@@ -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
|
@@ -1,22 +1,31 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# examples/
|
2
|
+
# examples/maxwell_agent86.rb
|
3
3
|
|
4
|
-
|
5
|
-
|
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 '
|
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
|
-
|
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
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
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
|
-
#
|
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 =
|
116
|
+
agent = MaxwellAgent86.new
|
97
117
|
agent.run # Starts listening for messages
|
@@ -1,8 +1,8 @@
|
|
1
|
-
# examples/
|
1
|
+
# examples/maxwell_request.rb
|
2
2
|
|
3
3
|
require_relative '../lib/agent99/header_schema'
|
4
4
|
|
5
|
-
class
|
5
|
+
class MaxwellRequest < SimpleJsonSchemaBuilder::Base
|
6
6
|
object do
|
7
7
|
object :header, schema: Agent99::HeaderSchema
|
8
8
|
|
data/examples/registry.rb
CHANGED
@@ -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(
|
33
|
+
def initialize(
|
34
|
+
config: CONFIG,
|
35
|
+
logger: Logger.new($stdout))
|
36
|
+
@config = config
|
21
37
|
@connection = create_amqp_connection
|
22
|
-
@channel
|
23
|
-
@exchange
|
24
|
-
@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 "
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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
|
-
{
|
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."
|
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
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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.
|
18
|
-
|
19
|
-
|
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
|
)
|
@@ -16,14 +16,15 @@ module Agent99::MessageProcessing
|
|
16
16
|
#
|
17
17
|
def dispatcher
|
18
18
|
@start_time = Time.now
|
19
|
-
@paused
|
20
|
-
@config
|
19
|
+
@paused = false
|
20
|
+
@config = {}
|
21
|
+
|
21
22
|
|
22
23
|
message_client.listen_for_messages(
|
23
24
|
queue,
|
24
|
-
request_handler:
|
25
|
+
request_handler: ->(message) { process_request(message) unless paused? },
|
25
26
|
response_handler: ->(message) { process_response(message) unless paused? },
|
26
|
-
control_handler:
|
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
|
37
|
-
@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
|
-
|
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
|
-
|
82
|
-
|
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(
|
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(
|
116
|
+
def send_control_response(data)
|
112
117
|
response = {
|
113
118
|
header: return_address.merge(type: 'control'),
|
114
|
-
|
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
|
data/lib/agent99/version.rb
CHANGED
data/lib/agent99.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: agent99
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
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-
|
11
|
+
date: 2024-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: ai_client
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: bunny
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,11 +140,14 @@ files:
|
|
154
140
|
- Rakefile
|
155
141
|
- docs/todo.md
|
156
142
|
- examples/README.md
|
157
|
-
- examples/
|
158
|
-
- examples/
|
159
|
-
- examples/
|
143
|
+
- examples/chief_agent.rb
|
144
|
+
- examples/control.rb
|
145
|
+
- examples/diagram.dot
|
146
|
+
- examples/diagram.png
|
147
|
+
- examples/maxwell_agent86.rb
|
148
|
+
- examples/maxwell_request.rb
|
160
149
|
- examples/registry.rb
|
161
|
-
- examples/
|
150
|
+
- examples/start_rabbitmq_and_registry.sh
|
162
151
|
- lib/agent99.rb
|
163
152
|
- lib/agent99/.irbrc
|
164
153
|
- lib/agent99/agent_discovery.rb
|
@@ -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
|
-
|
data/examples/start_agents.sh
DELETED
@@ -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
|