agent99 0.0.3 → 0.0.5
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/A2A_SPEC-dev.md +1829 -0
- data/CHANGELOG.md +38 -0
- data/COMMITS.md +196 -0
- data/DOCS.md +96 -0
- data/README.md +212 -84
- data/Rakefile +62 -0
- data/docs/AI/htm.md +215 -0
- data/docs/AI/htm.rb +141 -0
- data/docs/AI/htm_demo.db +0 -0
- data/docs/AI/notes_on_htm_implementation.md +1319 -0
- data/docs/AI/some_code.rb +692 -0
- data/docs/advanced-topics/a2a-protocol.md +13 -0
- data/docs/{advanced_features.md → advanced-topics/advanced-features.md} +9 -4
- data/docs/{control_actions.md → advanced-topics/control-actions.md} +2 -0
- data/docs/advanced-topics/model-context-protocol.md +4 -0
- data/docs/advanced-topics/multi-agent-processing.md +674 -0
- data/docs/agent-development/request-response-handling.md +512 -0
- 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/api-reference/agent99-base.md +463 -0
- data/docs/api-reference/message-clients.md +495 -0
- data/docs/{api_reference.md → api-reference/overview.md} +14 -4
- data/docs/api-reference/registry-client.md +470 -0
- data/docs/api-reference/schemas.md +518 -0
- data/docs/assets/css/custom.css +27 -0
- data/docs/assets/images/agent-lifecycle.svg +73 -0
- data/docs/assets/images/agent-registry-process.svg +86 -0
- data/docs/assets/images/agent-registry-processes.svg +114 -0
- data/docs/assets/images/agent-types-overview.svg +51 -0
- data/docs/assets/images/agent99-architecture.svg +85 -0
- data/docs/assets/images/agent99_logo.png +0 -0
- data/docs/assets/images/control-actions-state.svg +83 -0
- data/docs/assets/images/knowledge-graph.svg +77 -0
- data/docs/assets/images/message-processing-flow.svg +148 -0
- data/docs/assets/images/multi-agent-system.svg +66 -0
- data/docs/assets/images/proxy-pattern-sequence.svg +48 -0
- data/docs/assets/images/request-flow.svg +97 -0
- data/docs/assets/images/request-processing-lifecycle.svg +50 -0
- data/docs/assets/images/request-response-sequence.svg +39 -0
- data/docs/{agent_lifecycle.md → core-concepts/agent-lifecycle.md} +2 -0
- data/docs/core-concepts/agent-types.md +255 -0
- data/docs/{architecture.md → core-concepts/architecture.md} +5 -5
- data/docs/core-concepts/what-is-an-agent.md +293 -0
- data/docs/diagrams/message-flow-sequence.svg +198 -0
- data/docs/diagrams/p2p-network-topology.svg +181 -0
- data/docs/diagrams/smart-transport-routing.svg +165 -0
- data/docs/diagrams/three-layer-architecture.svg +77 -0
- data/docs/diagrams/transport-extension-api.svg +309 -0
- data/docs/diagrams/transport-extension-architecture.svg +234 -0
- data/docs/diagrams/transport-selection-flowchart.svg +264 -0
- data/docs/examples/advanced-examples.md +951 -0
- data/docs/examples/basic-examples.md +268 -0
- data/docs/{agent_discovery.md → framework-components/agent-discovery.md} +9 -5
- data/docs/{agent_registry_processes.md → framework-components/agent-registry.md} +9 -3
- data/docs/{message_processing.md → framework-components/message-processing.md} +3 -1
- data/docs/getting-started/basic-example.md +306 -0
- data/docs/getting-started/installation.md +160 -0
- data/docs/getting-started/overview.md +64 -0
- data/docs/getting-started/quick-start.md +179 -0
- data/docs/index.md +97 -0
- data/docs/operations/breaking-changes.md +26 -0
- data/examples/DEMO.md +148 -0
- data/examples/README.md +50 -0
- data/examples/agent_watcher.rb +5 -1
- data/examples/bad_agent.rb +32 -0
- 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 +10 -9
- data/examples/run_demo.rb +433 -0
- data/lib/agent99/agent_discovery.rb +4 -0
- data/lib/agent99/agent_lifecycle.rb +34 -10
- data/lib/agent99/amqp_message_client.rb +2 -2
- data/lib/agent99/base.rb +6 -2
- data/lib/agent99/message_processing.rb +6 -10
- data/lib/agent99/registry_client.rb +15 -11
- data/lib/agent99/tcp_message_client.rb +183 -0
- data/lib/agent99/version.rb +1 -1
- data/lib/agent99.rb +1 -1
- data/mkdocs.yml +195 -0
- data/p2p_plan.md +533 -0
- data/p2p_roadmap.md +299 -0
- data/registry_plan.md +1818 -0
- metadata +93 -30
- data/docs/README.md +0 -57
- data/docs/diagrams/agent_registry_processes.dot +0 -42
- data/docs/diagrams/agent_registry_processes.png +0 -0
- data/docs/diagrams/high_level_architecture.dot +0 -26
- data/docs/diagrams/high_level_architecture.png +0 -0
- data/docs/diagrams/request_flow.dot +0 -42
- data/docs/diagrams/request_flow.png +0 -0
- /data/docs/{extending_the_framework.md → advanced-topics/extending-the-framework.md} +0 -0
- /data/docs/{custom_agent_implementation.md → agent-development/custom-agent-implementation.md} +0 -0
- /data/docs/{error_handling_and_logging.md → agent-development/error-handling-and-logging.md} +0 -0
- /data/docs/{schema_definition.md → agent-development/schema-definition.md} +0 -0
- /data/docs/{messaging_system.md → framework-components/messaging-system.md} +0 -0
- /data/docs/{configuration.md → operations/configuration.md} +0 -0
- /data/docs/{preformance_considerations.md → operations/performance-considerations.md} +0 -0
- /data/docs/{security.md → operations/security.md} +0 -0
- /data/docs/{troubleshooting.md → operations/troubleshooting.md} +0 -0
@@ -0,0 +1,512 @@
|
|
1
|
+
# Request & Response Handling
|
2
|
+
|
3
|
+
Understanding how to properly handle requests and responses is fundamental to building robust Agent99 applications. This guide covers patterns, best practices, and advanced techniques.
|
4
|
+
|
5
|
+
## Request Processing Lifecycle
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
## Basic Request Handling
|
10
|
+
|
11
|
+
### Simple Request Processing
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
class SimpleAgent < Agent99::Base
|
15
|
+
def process_request(payload)
|
16
|
+
# Extract data from payload
|
17
|
+
user_name = payload.dig(:name) || "Anonymous"
|
18
|
+
|
19
|
+
# Perform business logic
|
20
|
+
greeting = generate_greeting(user_name)
|
21
|
+
|
22
|
+
# Send response
|
23
|
+
send_response(message: greeting)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def generate_greeting(name)
|
29
|
+
"Hello, #{name}! Welcome to Agent99."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
### With Input Validation
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class ValidatingAgent < Agent99::Base
|
38
|
+
def process_request(payload)
|
39
|
+
# Validate required fields
|
40
|
+
unless payload.key?(:user_id)
|
41
|
+
return send_error("Missing required field: user_id", "MISSING_FIELD")
|
42
|
+
end
|
43
|
+
|
44
|
+
unless payload[:user_id].is_a?(String) && !payload[:user_id].empty?
|
45
|
+
return send_error("Invalid user_id: must be non-empty string", "INVALID_FORMAT")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Process valid request
|
49
|
+
result = lookup_user_data(payload[:user_id])
|
50
|
+
send_response(result)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def lookup_user_data(user_id)
|
56
|
+
# Simulate database lookup
|
57
|
+
{
|
58
|
+
user_id: user_id,
|
59
|
+
name: "User #{user_id}",
|
60
|
+
status: "active",
|
61
|
+
last_seen: Time.now.iso8601
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
## Advanced Request Patterns
|
68
|
+
|
69
|
+
### Asynchronous Processing
|
70
|
+
|
71
|
+
For long-running operations, acknowledge receipt immediately and process asynchronously:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class AsyncProcessingAgent < Agent99::Base
|
75
|
+
def initialize
|
76
|
+
super
|
77
|
+
@job_queue = Queue.new
|
78
|
+
@worker_thread = Thread.new { process_jobs }
|
79
|
+
end
|
80
|
+
|
81
|
+
def process_request(payload)
|
82
|
+
job_id = SecureRandom.uuid
|
83
|
+
|
84
|
+
# Acknowledge receipt immediately
|
85
|
+
send_response(
|
86
|
+
status: "accepted",
|
87
|
+
job_id: job_id,
|
88
|
+
message: "Request queued for processing"
|
89
|
+
)
|
90
|
+
|
91
|
+
# Queue for background processing
|
92
|
+
@job_queue << {
|
93
|
+
id: job_id,
|
94
|
+
payload: payload,
|
95
|
+
requester: current_request_id
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def process_jobs
|
102
|
+
while job = @job_queue.pop
|
103
|
+
begin
|
104
|
+
result = perform_long_operation(job[:payload])
|
105
|
+
|
106
|
+
# Send completion notification (if supported)
|
107
|
+
notify_completion(job[:requester], job[:id], result)
|
108
|
+
rescue => e
|
109
|
+
logger.error "Job #{job[:id]} failed: #{e.message}"
|
110
|
+
notify_failure(job[:requester], job[:id], e.message)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def perform_long_operation(payload)
|
116
|
+
# Simulate long operation
|
117
|
+
sleep(5)
|
118
|
+
{ result: "Operation completed", data: payload }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
### Streaming Responses
|
124
|
+
|
125
|
+
For data that comes in chunks:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class StreamingAgent < Agent99::Base
|
129
|
+
def process_request(payload)
|
130
|
+
file_path = payload.dig(:file_path)
|
131
|
+
|
132
|
+
unless File.exist?(file_path)
|
133
|
+
return send_error("File not found: #{file_path}", "FILE_NOT_FOUND")
|
134
|
+
end
|
135
|
+
|
136
|
+
# Send initial response
|
137
|
+
send_response(
|
138
|
+
status: "streaming",
|
139
|
+
total_size: File.size(file_path),
|
140
|
+
chunk_size: 1024
|
141
|
+
)
|
142
|
+
|
143
|
+
# Stream file contents
|
144
|
+
File.open(file_path, 'rb') do |file|
|
145
|
+
chunk_number = 0
|
146
|
+
while chunk = file.read(1024)
|
147
|
+
send_chunk(chunk_number, chunk)
|
148
|
+
chunk_number += 1
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Send completion marker
|
153
|
+
send_completion()
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def send_chunk(number, data)
|
159
|
+
# Implementation depends on messaging system
|
160
|
+
# This is a conceptual example
|
161
|
+
publish_message({
|
162
|
+
type: "chunk",
|
163
|
+
sequence: number,
|
164
|
+
data: Base64.encode64(data)
|
165
|
+
})
|
166
|
+
end
|
167
|
+
|
168
|
+
def send_completion
|
169
|
+
publish_message({
|
170
|
+
type: "stream_complete"
|
171
|
+
})
|
172
|
+
end
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
## Response Patterns
|
177
|
+
|
178
|
+
### Success Responses
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
# Simple success
|
182
|
+
send_response(result: "Operation successful")
|
183
|
+
|
184
|
+
# Rich success response
|
185
|
+
send_response(
|
186
|
+
status: "success",
|
187
|
+
data: processed_data,
|
188
|
+
metadata: {
|
189
|
+
processing_time: elapsed_time,
|
190
|
+
version: "1.0.0"
|
191
|
+
}
|
192
|
+
)
|
193
|
+
|
194
|
+
# Collection response
|
195
|
+
send_response(
|
196
|
+
items: search_results,
|
197
|
+
total_count: total_count,
|
198
|
+
page: current_page,
|
199
|
+
has_more: has_more_pages
|
200
|
+
)
|
201
|
+
```
|
202
|
+
|
203
|
+
### Error Responses
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
# Simple error
|
207
|
+
send_error("Something went wrong")
|
208
|
+
|
209
|
+
# Structured error
|
210
|
+
send_error("Invalid input data", "VALIDATION_ERROR")
|
211
|
+
|
212
|
+
# Rich error response
|
213
|
+
send_error(
|
214
|
+
"Database connection failed",
|
215
|
+
"DATABASE_ERROR",
|
216
|
+
{
|
217
|
+
retry_after: 30,
|
218
|
+
support_id: "ERR-#{SecureRandom.hex(8)}",
|
219
|
+
details: {
|
220
|
+
attempted_host: db_host,
|
221
|
+
timeout: connection_timeout
|
222
|
+
}
|
223
|
+
}
|
224
|
+
)
|
225
|
+
```
|
226
|
+
|
227
|
+
## Request Context and Headers
|
228
|
+
|
229
|
+
### Accessing Request Headers
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
class HeaderAwareAgent < Agent99::Base
|
233
|
+
def process_request(payload)
|
234
|
+
# Access headers from the current request
|
235
|
+
request_id = header_value('request_id')
|
236
|
+
user_id = header_value('user_id')
|
237
|
+
correlation_id = header_value('correlation_id')
|
238
|
+
|
239
|
+
logger.info "Processing request #{request_id} for user #{user_id}"
|
240
|
+
|
241
|
+
# Use headers in processing
|
242
|
+
if authorized_user?(user_id)
|
243
|
+
result = perform_operation(payload)
|
244
|
+
send_response(result)
|
245
|
+
else
|
246
|
+
send_error("Unauthorized access", "UNAUTHORIZED")
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
private
|
251
|
+
|
252
|
+
def authorized_user?(user_id)
|
253
|
+
# Check authorization logic
|
254
|
+
user_id && user_id.start_with?('user_')
|
255
|
+
end
|
256
|
+
end
|
257
|
+
```
|
258
|
+
|
259
|
+
### Setting Response Headers
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
def process_request(payload)
|
263
|
+
result = process_data(payload)
|
264
|
+
|
265
|
+
# Set custom headers in response
|
266
|
+
set_header('processing_node', Socket.gethostname)
|
267
|
+
set_header('cache_status', 'miss')
|
268
|
+
set_header('processing_time', elapsed_time.to_s)
|
269
|
+
|
270
|
+
send_response(result)
|
271
|
+
end
|
272
|
+
```
|
273
|
+
|
274
|
+
## Error Handling Strategies
|
275
|
+
|
276
|
+
### Graceful Degradation
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
class ResilientAgent < Agent99::Base
|
280
|
+
def process_request(payload)
|
281
|
+
begin
|
282
|
+
# Try primary service
|
283
|
+
result = primary_service.process(payload)
|
284
|
+
send_response(result)
|
285
|
+
rescue PrimaryServiceError => e
|
286
|
+
logger.warn "Primary service failed: #{e.message}"
|
287
|
+
|
288
|
+
begin
|
289
|
+
# Fallback to secondary service
|
290
|
+
result = secondary_service.process(payload)
|
291
|
+
result[:fallback_used] = true
|
292
|
+
send_response(result)
|
293
|
+
rescue SecondaryServiceError => e2
|
294
|
+
logger.error "Both services failed: #{e2.message}"
|
295
|
+
|
296
|
+
# Return cached or default result
|
297
|
+
default_result = get_cached_result(payload) || default_response
|
298
|
+
default_result[:degraded_service] = true
|
299
|
+
send_response(default_result)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
private
|
305
|
+
|
306
|
+
def get_cached_result(payload)
|
307
|
+
# Check cache for previous result
|
308
|
+
cache_key = generate_cache_key(payload)
|
309
|
+
cached_data = cache.get(cache_key)
|
310
|
+
|
311
|
+
if cached_data && fresh_enough?(cached_data)
|
312
|
+
cached_data[:from_cache] = true
|
313
|
+
return cached_data
|
314
|
+
end
|
315
|
+
|
316
|
+
nil
|
317
|
+
end
|
318
|
+
|
319
|
+
def default_response
|
320
|
+
{
|
321
|
+
status: "service_unavailable",
|
322
|
+
message: "Service temporarily unavailable",
|
323
|
+
retry_after: 60
|
324
|
+
}
|
325
|
+
end
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
329
|
+
### Circuit Breaker Pattern
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
class CircuitBreakerAgent < Agent99::Base
|
333
|
+
def initialize
|
334
|
+
super
|
335
|
+
@circuit_breaker = CircuitBreaker.new(
|
336
|
+
failure_threshold: 5,
|
337
|
+
recovery_timeout: 30
|
338
|
+
)
|
339
|
+
end
|
340
|
+
|
341
|
+
def process_request(payload)
|
342
|
+
@circuit_breaker.call do
|
343
|
+
# Potentially failing operation
|
344
|
+
external_service.process(payload)
|
345
|
+
end.then do |result|
|
346
|
+
send_response(result)
|
347
|
+
end.rescue do |error|
|
348
|
+
case error
|
349
|
+
when CircuitBreaker::OpenError
|
350
|
+
send_error("Service temporarily unavailable", "CIRCUIT_OPEN")
|
351
|
+
else
|
352
|
+
send_error("Service error: #{error.message}", "SERVICE_ERROR")
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
```
|
358
|
+
|
359
|
+
## Request Routing and Delegation
|
360
|
+
|
361
|
+
### Multi-operation Agent
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
class MultiOperationAgent < Agent99::Base
|
365
|
+
def process_request(payload)
|
366
|
+
operation = payload.dig(:operation)
|
367
|
+
|
368
|
+
case operation
|
369
|
+
when 'create'
|
370
|
+
handle_create(payload)
|
371
|
+
when 'read'
|
372
|
+
handle_read(payload)
|
373
|
+
when 'update'
|
374
|
+
handle_update(payload)
|
375
|
+
when 'delete'
|
376
|
+
handle_delete(payload)
|
377
|
+
else
|
378
|
+
send_error("Unknown operation: #{operation}", "INVALID_OPERATION")
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
private
|
383
|
+
|
384
|
+
def handle_create(payload)
|
385
|
+
# Creation logic
|
386
|
+
entity = create_entity(payload[:data])
|
387
|
+
send_response(entity: entity, status: "created")
|
388
|
+
end
|
389
|
+
|
390
|
+
def handle_read(payload)
|
391
|
+
# Read logic
|
392
|
+
entity = find_entity(payload[:id])
|
393
|
+
if entity
|
394
|
+
send_response(entity: entity)
|
395
|
+
else
|
396
|
+
send_error("Entity not found", "NOT_FOUND")
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def handle_update(payload)
|
401
|
+
# Update logic
|
402
|
+
entity = update_entity(payload[:id], payload[:data])
|
403
|
+
send_response(entity: entity, status: "updated")
|
404
|
+
end
|
405
|
+
|
406
|
+
def handle_delete(payload)
|
407
|
+
# Delete logic
|
408
|
+
delete_entity(payload[:id])
|
409
|
+
send_response(status: "deleted")
|
410
|
+
end
|
411
|
+
end
|
412
|
+
```
|
413
|
+
|
414
|
+
## Testing Request Handling
|
415
|
+
|
416
|
+
### Unit Testing
|
417
|
+
|
418
|
+
```ruby
|
419
|
+
require 'minitest/autorun'
|
420
|
+
|
421
|
+
class TestCalculatorAgent < Minitest::Test
|
422
|
+
def setup
|
423
|
+
@agent = CalculatorAgent.new
|
424
|
+
end
|
425
|
+
|
426
|
+
def test_successful_addition
|
427
|
+
payload = { operation: 'add', a: 5, b: 3 }
|
428
|
+
|
429
|
+
# Mock the send_response method
|
430
|
+
response = nil
|
431
|
+
@agent.stub(:send_response, ->(data) { response = data }) do
|
432
|
+
@agent.process_request(payload)
|
433
|
+
end
|
434
|
+
|
435
|
+
assert_equal 8, response[:result]
|
436
|
+
assert_equal 'add', response[:operation]
|
437
|
+
end
|
438
|
+
|
439
|
+
def test_division_by_zero
|
440
|
+
payload = { operation: 'divide', a: 10, b: 0 }
|
441
|
+
|
442
|
+
error_response = nil
|
443
|
+
@agent.stub(:send_error, ->(msg, code) {
|
444
|
+
error_response = { message: msg, code: code }
|
445
|
+
}) do
|
446
|
+
@agent.process_request(payload)
|
447
|
+
end
|
448
|
+
|
449
|
+
assert_equal 'DIVISION_BY_ZERO', error_response[:code]
|
450
|
+
end
|
451
|
+
end
|
452
|
+
```
|
453
|
+
|
454
|
+
## Best Practices
|
455
|
+
|
456
|
+
### Do's ✅
|
457
|
+
|
458
|
+
- **Always respond**: Every request should get a response
|
459
|
+
- **Validate early**: Check inputs before processing
|
460
|
+
- **Use schemas**: Define and validate request/response structures
|
461
|
+
- **Log appropriately**: Track requests without logging sensitive data
|
462
|
+
- **Handle timeouts**: Set reasonable processing time limits
|
463
|
+
- **Provide context**: Include helpful error messages and codes
|
464
|
+
|
465
|
+
### Don'ts ❌
|
466
|
+
|
467
|
+
- **Don't block indefinitely**: Always have timeouts
|
468
|
+
- **Don't expose internals**: Keep error messages user-friendly
|
469
|
+
- **Don't ignore errors**: Handle and respond to all error conditions
|
470
|
+
- **Don't trust input**: Always validate and sanitize
|
471
|
+
- **Don't forget correlation**: Maintain request tracing across calls
|
472
|
+
|
473
|
+
## Performance Considerations
|
474
|
+
|
475
|
+
### Memory Management
|
476
|
+
|
477
|
+
```ruby
|
478
|
+
def process_request(payload)
|
479
|
+
# Stream large data instead of loading all at once
|
480
|
+
if payload[:data_size] > LARGE_DATA_THRESHOLD
|
481
|
+
process_data_streaming(payload)
|
482
|
+
else
|
483
|
+
process_data_in_memory(payload)
|
484
|
+
end
|
485
|
+
end
|
486
|
+
```
|
487
|
+
|
488
|
+
### Connection Pooling
|
489
|
+
|
490
|
+
```ruby
|
491
|
+
class DatabaseAgent < Agent99::Base
|
492
|
+
def initialize
|
493
|
+
super
|
494
|
+
@connection_pool = ConnectionPool.new(size: 10) do
|
495
|
+
Database.connect
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
def process_request(payload)
|
500
|
+
@connection_pool.with do |connection|
|
501
|
+
result = connection.query(payload[:sql])
|
502
|
+
send_response(rows: result.to_a)
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
```
|
507
|
+
|
508
|
+
## Next Steps
|
509
|
+
|
510
|
+
- **[Error Handling & Logging](error-handling-and-logging.md)** - Comprehensive error strategies
|
511
|
+
- **[Schema Definition](schema-definition.md)** - Advanced schema patterns
|
512
|
+
- **[Advanced Features](../advanced-topics/advanced-features.md)** - Complex patterns and techniques
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Central Registry
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
The Central Registry is a crucial component of the Agent99 Framework, serving as a centralized hub for agent registration, discovery, and management. Its primary purpose is to facilitate communication and coordination between various agents within the framework, allowing them to register their capabilities and discover other agents with specific skills.
|
6
|
+
|
7
|
+
The registry provides a RESTful API that can be implemented in any programming language or web framework that supports HTTPS endpoints. This document outlines the API specifications for implementing a compatible Central Registry.
|
8
|
+
|
9
|
+
## API Endpoints
|
10
|
+
|
11
|
+
### 1. Health Check
|
12
|
+
|
13
|
+
- **Endpoint**: GET /healthcheck
|
14
|
+
- **Purpose**: Provides a simple health check for the registry service.
|
15
|
+
- **Response**: JSON object containing the current count of registered agents.
|
16
|
+
- **Example Response**:
|
17
|
+
```json
|
18
|
+
{
|
19
|
+
"agent_count": 5
|
20
|
+
}
|
21
|
+
```
|
22
|
+
|
23
|
+
### 2. Register Agent
|
24
|
+
|
25
|
+
- **Endpoint**: POST /register
|
26
|
+
- **Purpose**: Allows an agent to register itself with the registry, providing its name and capabilities.
|
27
|
+
- **Request Body**: JSON object containing agent information.
|
28
|
+
- **Response**: JSON object with a newly generated UUID for the registered agent.
|
29
|
+
- **Example Request**:
|
30
|
+
```json
|
31
|
+
{
|
32
|
+
"name": "TextAnalyzer",
|
33
|
+
"capabilities": ["sentiment analysis", "named entity recognition"]
|
34
|
+
}
|
35
|
+
```
|
36
|
+
- **Example Response**:
|
37
|
+
```json
|
38
|
+
{
|
39
|
+
"uuid": "550e8400-e29b-41d4-a716-446655440000"
|
40
|
+
}
|
41
|
+
```
|
42
|
+
|
43
|
+
### 3. Discover Agents
|
44
|
+
|
45
|
+
- **Endpoint**: GET /discover
|
46
|
+
- **Purpose**: Allows discovery of agents based on a specific capability.
|
47
|
+
- **Query Parameter**: capability (string)
|
48
|
+
- **Response**: JSON array of matching agents with their full information.
|
49
|
+
- **Example Request**: GET /discover?capability=sentiment+analysis
|
50
|
+
- **Example Response**:
|
51
|
+
```json
|
52
|
+
[
|
53
|
+
{
|
54
|
+
"name": "TextAnalyzer",
|
55
|
+
"capabilities": ["sentiment analysis", "named entity recognition"],
|
56
|
+
"uuid": "550e8400-e29b-41d4-a716-446655440000"
|
57
|
+
}
|
58
|
+
]
|
59
|
+
```
|
60
|
+
|
61
|
+
### 4. Withdraw Agent
|
62
|
+
|
63
|
+
- **Endpoint**: DELETE /withdraw/:uuid
|
64
|
+
- **Purpose**: Removes an agent from the registry using its UUID.
|
65
|
+
- **Response**:
|
66
|
+
- 204 No Content if successful
|
67
|
+
- 404 Not Found if the agent UUID is not in the registry
|
68
|
+
- **Example Request**: DELETE /withdraw/550e8400-e29b-41d4-a716-446655440000
|
69
|
+
|
70
|
+
### 5. List All Agents
|
71
|
+
|
72
|
+
- **Endpoint**: GET /
|
73
|
+
- **Purpose**: Retrieves a list of all registered agents.
|
74
|
+
- **Response**: JSON array containing all registered agents' information.
|
75
|
+
|
76
|
+
## Implementation Notes
|
77
|
+
|
78
|
+
1. Agent capabilities should be stored and compared in lowercase to ensure case-insensitive matching.
|
79
|
+
2. The current implementation uses an in-memory array to store agent information. For production use, consider using a persistent database like SQLite or a more scalable solution.
|
80
|
+
3. The discovery process currently uses simple keyword matching. Future enhancements could include semantic matching for more accurate agent discovery.
|
81
|
+
|
82
|
+
## Potential Enhancements
|
83
|
+
|
84
|
+
1. **Persistent Storage**: Implement a database backend for storing agent information, ensuring data persistence across server restarts.
|
85
|
+
2. **Authentication and Authorization**: Add security measures to protect sensitive endpoints and ensure only authorized agents can register or withdraw.
|
86
|
+
3. **Semantic Matching**: Enhance the discovery process with natural language processing or vector search capabilities for more intelligent agent matching.
|
87
|
+
4. **Agent Health Monitoring**: Implement periodic health checks on registered agents to ensure they are still active and available.
|
88
|
+
5. **Versioning**: Add support for agent versioning to manage different versions of agents with similar capabilities.
|
89
|
+
6. **Pagination**: Implement pagination for the discovery and list all endpoints to handle large numbers of agents efficiently.
|
90
|
+
7. **Metrics and Logging**: Add comprehensive logging and metrics collection for better monitoring and debugging of the registry service.
|
91
|
+
8. **API Rate Limiting**: Implement rate limiting to prevent abuse and ensure fair usage of the registry service.
|
92
|
+
|
93
|
+
By implementing this API, developers can create a compatible Central Registry for the Agent99 Framework in their preferred language or framework, enabling seamless integration and communication between diverse agents in the ecosystem.
|
94
|
+
|