agent99 0.0.3 → 0.0.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +30 -24
- data/docs/advanced_features.md +9 -4
- data/docs/agent99_framework/central_registry.md +94 -0
- data/docs/agent99_framework/message_client.md +120 -0
- data/docs/agent99_framework/registry_client.md +119 -0
- data/docs/agent_discovery.md +9 -5
- data/docs/agent_registry_processes.md +8 -2
- data/docs/api_reference.md +14 -4
- data/docs/breaking_change_v0.0.4.md +26 -0
- data/docs/what_is_an_agent.md +293 -0
- data/examples/agent_watcher.rb +5 -1
- data/examples/chief_agent.rb +17 -6
- data/examples/control.rb +16 -7
- data/examples/example_agent.rb +16 -3
- data/examples/maxwell_agent86.rb +15 -26
- data/examples/registry.rb +18 -9
- data/lib/agent99/agent_discovery.rb +4 -0
- data/lib/agent99/agent_lifecycle.rb +34 -10
- data/lib/agent99/base.rb +5 -1
- data/lib/agent99/message_processing.rb +3 -1
- data/lib/agent99/registry_client.rb +12 -11
- data/lib/agent99/tcp_message_client.rb +183 -0
- data/lib/agent99/version.rb +1 -1
- metadata +8 -2
@@ -0,0 +1,183 @@
|
|
1
|
+
# lib/agent99/tcp_message_client.rb
|
2
|
+
#
|
3
|
+
# NOTE: This is an early attempt at a true
|
4
|
+
# peer-to-peer messaging platform. Its not
|
5
|
+
# not ready for prime time.
|
6
|
+
|
7
|
+
require 'socket'
|
8
|
+
require 'json'
|
9
|
+
require 'logger'
|
10
|
+
|
11
|
+
class Agent99::TcpMessageClient
|
12
|
+
attr_accessor :agents
|
13
|
+
|
14
|
+
def initialize(
|
15
|
+
agents: {},
|
16
|
+
logger: Logger.new($stdout)
|
17
|
+
)
|
18
|
+
@agents = agents
|
19
|
+
@logger = logger
|
20
|
+
@server_socket = nil
|
21
|
+
@client_connections = {}
|
22
|
+
@handlers = {}
|
23
|
+
@running = false
|
24
|
+
end
|
25
|
+
|
26
|
+
def listen_for_messages(queue, request_handler:, response_handler:, control_handler:)
|
27
|
+
@handlers = {
|
28
|
+
request: request_handler,
|
29
|
+
response: response_handler,
|
30
|
+
control: control_handler
|
31
|
+
}
|
32
|
+
|
33
|
+
start_server(queue[:port])
|
34
|
+
end
|
35
|
+
|
36
|
+
def publish(message)
|
37
|
+
target = message.dig(:header, :to_uuid)
|
38
|
+
return unless target
|
39
|
+
|
40
|
+
agent_info = agents(target)
|
41
|
+
return unless agent_info
|
42
|
+
|
43
|
+
socket = connect_to_agent(agent_info[:ip], agent_info[:port])
|
44
|
+
return unless socket
|
45
|
+
|
46
|
+
begin
|
47
|
+
socket.puts(message.to_json)
|
48
|
+
true
|
49
|
+
|
50
|
+
rescue StandardError => e
|
51
|
+
@logger.error("Failed to send message: #{e.message}")
|
52
|
+
false
|
53
|
+
|
54
|
+
ensure
|
55
|
+
socket.close unless socket.closed?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def stop
|
60
|
+
@running = false
|
61
|
+
@server_socket&.close
|
62
|
+
@client_connections.each_value(&:close)
|
63
|
+
@client_connections.clear
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def start_server(port)
|
69
|
+
@server_socket = TCPServer.new(port)
|
70
|
+
@running = true
|
71
|
+
|
72
|
+
Thread.new do
|
73
|
+
while @running
|
74
|
+
begin
|
75
|
+
client = @server_socket.accept
|
76
|
+
handle_client(client)
|
77
|
+
rescue StandardError => e
|
78
|
+
@logger.error("Server error: #{e.message}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def handle_client(client)
|
85
|
+
Thread.new do
|
86
|
+
while @running
|
87
|
+
begin
|
88
|
+
message = client.gets
|
89
|
+
break if message.nil?
|
90
|
+
|
91
|
+
parsed_message = JSON.parse(message, symbolize_names: true)
|
92
|
+
route_message(parsed_message)
|
93
|
+
|
94
|
+
rescue JSON::ParserError => e
|
95
|
+
@logger.error("Invalid JSON received: #{e.message}")
|
96
|
+
|
97
|
+
rescue StandardError => e
|
98
|
+
@logger.error("Error handling client: #{e.message}")
|
99
|
+
break
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
client.close unless client.closed?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def route_message(message)
|
108
|
+
type = message.dig(:header, :type)&.to_sym
|
109
|
+
handler = @handlers[type]
|
110
|
+
|
111
|
+
if handler
|
112
|
+
handler.call(message)
|
113
|
+
else
|
114
|
+
@logger.warn("No handler for message type: #{type}")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def connect_to_agent(ip, port)
|
119
|
+
TCPSocket.new(ip, port)
|
120
|
+
|
121
|
+
rescue StandardError => e
|
122
|
+
@logger.error("Failed to connect to #{ip}:#{port}: #{e.message}")
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
__END__
|
129
|
+
|
130
|
+
Based on the provided code for the `Agent99::TcpMessageClient` class, here's an analysis of your questions:
|
131
|
+
|
132
|
+
1. Does this class queue up JSON messages while a previous message is being processed?
|
133
|
+
|
134
|
+
No, this class does not explicitly queue up JSON messages while a previous message is being processed. The class processes messages as they are received, without maintaining an internal queue. Each incoming message is handled in its own thread (see the `handle_client` method), which allows for concurrent processing of messages from multiple clients.
|
135
|
+
|
136
|
+
2. Does it present a complete JSON message at once or does it only provide part of one?
|
137
|
+
|
138
|
+
The class attempts to present complete JSON messages at once. Here's why:
|
139
|
+
|
140
|
+
- In the `handle_client` method, messages are read using `client.gets`, which typically reads a full line of input (up to a newline character).
|
141
|
+
- The received message is then parsed as JSON using `JSON.parse(message, symbolize_names: true)`.
|
142
|
+
- If the parsing is successful, the entire parsed message is passed to the `route_message` method.
|
143
|
+
|
144
|
+
However, there are a few potential issues to consider:
|
145
|
+
|
146
|
+
- If a JSON message spans multiple lines, `client.gets` might not capture the entire message in one read.
|
147
|
+
- There's no explicit handling for partial messages or message boundaries.
|
148
|
+
- Large messages might be split across multiple TCP packets, and the current implementation doesn't account for reassembling these.
|
149
|
+
|
150
|
+
To ensure complete message handling, you might want to consider implementing a more robust message framing protocol, such as using message length prefixes or delimiter-based framing.
|
151
|
+
|
152
|
+
For example, you could modify the `handle_client` method to use a delimiter-based approach:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
def handle_client(client)
|
156
|
+
Thread.new do
|
157
|
+
buffer = ""
|
158
|
+
while @running
|
159
|
+
begin
|
160
|
+
chunk = client.readpartial(1024)
|
161
|
+
buffer += chunk
|
162
|
+
while (message_end = buffer.index("\n"))
|
163
|
+
message = buffer[0...message_end]
|
164
|
+
buffer = buffer[(message_end + 1)..]
|
165
|
+
parsed_message = JSON.parse(message, symbolize_names: true)
|
166
|
+
route_message(parsed_message)
|
167
|
+
end
|
168
|
+
rescue EOFError
|
169
|
+
break
|
170
|
+
rescue JSON::ParserError => e
|
171
|
+
@logger.error("Invalid JSON received: #{e.message}")
|
172
|
+
rescue StandardError => e
|
173
|
+
@logger.error("Error handling client: #{e.message}")
|
174
|
+
break
|
175
|
+
end
|
176
|
+
end
|
177
|
+
client.close unless client.closed?
|
178
|
+
end
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
This modification would allow for handling of messages that might be split across multiple reads, ensuring that complete JSON messages are processed.
|
183
|
+
|
data/lib/agent99/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.4
|
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-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -154,11 +154,15 @@ files:
|
|
154
154
|
- Rakefile
|
155
155
|
- docs/README.md
|
156
156
|
- docs/advanced_features.md
|
157
|
+
- docs/agent99_framework/central_registry.md
|
158
|
+
- docs/agent99_framework/message_client.md
|
159
|
+
- docs/agent99_framework/registry_client.md
|
157
160
|
- docs/agent_discovery.md
|
158
161
|
- docs/agent_lifecycle.md
|
159
162
|
- docs/agent_registry_processes.md
|
160
163
|
- docs/api_reference.md
|
161
164
|
- docs/architecture.md
|
165
|
+
- docs/breaking_change_v0.0.4.md
|
162
166
|
- docs/configuration.md
|
163
167
|
- docs/control_actions.md
|
164
168
|
- docs/custom_agent_implementation.md
|
@@ -176,6 +180,7 @@ files:
|
|
176
180
|
- docs/schema_definition.md
|
177
181
|
- docs/security.md
|
178
182
|
- docs/troubleshooting.md
|
183
|
+
- docs/what_is_an_agent.md
|
179
184
|
- examples/README.md
|
180
185
|
- examples/agent_watcher.rb
|
181
186
|
- examples/agents/.keep
|
@@ -201,6 +206,7 @@ files:
|
|
201
206
|
- lib/agent99/message_processing.rb
|
202
207
|
- lib/agent99/nats_message_client.rb
|
203
208
|
- lib/agent99/registry_client.rb
|
209
|
+
- lib/agent99/tcp_message_client.rb
|
204
210
|
- lib/agent99/timestamp.rb
|
205
211
|
- lib/agent99/version.rb
|
206
212
|
- sig/ai_agent.rbs
|