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.
@@ -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
+
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Agent99
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.4"
5
5
 
6
6
  def self.version = VERSION
7
7
  end
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.3
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-09 00:00:00.000000000 Z
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