agent99 0.0.4 → 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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/A2A_SPEC-dev.md +1829 -0
  3. data/CHANGELOG.md +31 -0
  4. data/COMMITS.md +196 -0
  5. data/DOCS.md +96 -0
  6. data/README.md +200 -78
  7. data/Rakefile +62 -0
  8. data/docs/AI/htm.md +215 -0
  9. data/docs/AI/htm.rb +141 -0
  10. data/docs/AI/htm_demo.db +0 -0
  11. data/docs/AI/notes_on_htm_implementation.md +1319 -0
  12. data/docs/AI/some_code.rb +692 -0
  13. data/docs/advanced-topics/a2a-protocol.md +13 -0
  14. data/docs/{control_actions.md → advanced-topics/control-actions.md} +2 -0
  15. data/docs/advanced-topics/model-context-protocol.md +4 -0
  16. data/docs/advanced-topics/multi-agent-processing.md +674 -0
  17. data/docs/agent-development/request-response-handling.md +512 -0
  18. data/docs/api-reference/agent99-base.md +463 -0
  19. data/docs/api-reference/message-clients.md +495 -0
  20. data/docs/api-reference/registry-client.md +470 -0
  21. data/docs/api-reference/schemas.md +518 -0
  22. data/docs/assets/css/custom.css +27 -0
  23. data/docs/assets/images/agent-lifecycle.svg +73 -0
  24. data/docs/assets/images/agent-registry-process.svg +86 -0
  25. data/docs/assets/images/agent-registry-processes.svg +114 -0
  26. data/docs/assets/images/agent-types-overview.svg +51 -0
  27. data/docs/assets/images/agent99-architecture.svg +85 -0
  28. data/docs/assets/images/agent99_logo.png +0 -0
  29. data/docs/assets/images/control-actions-state.svg +83 -0
  30. data/docs/assets/images/knowledge-graph.svg +77 -0
  31. data/docs/assets/images/message-processing-flow.svg +148 -0
  32. data/docs/assets/images/multi-agent-system.svg +66 -0
  33. data/docs/assets/images/proxy-pattern-sequence.svg +48 -0
  34. data/docs/assets/images/request-flow.svg +97 -0
  35. data/docs/assets/images/request-processing-lifecycle.svg +50 -0
  36. data/docs/assets/images/request-response-sequence.svg +39 -0
  37. data/docs/{agent_lifecycle.md → core-concepts/agent-lifecycle.md} +2 -0
  38. data/docs/core-concepts/agent-types.md +255 -0
  39. data/docs/{architecture.md → core-concepts/architecture.md} +5 -5
  40. data/docs/{what_is_an_agent.md → core-concepts/what-is-an-agent.md} +1 -1
  41. data/docs/diagrams/message-flow-sequence.svg +198 -0
  42. data/docs/diagrams/p2p-network-topology.svg +181 -0
  43. data/docs/diagrams/smart-transport-routing.svg +165 -0
  44. data/docs/diagrams/three-layer-architecture.svg +77 -0
  45. data/docs/diagrams/transport-extension-api.svg +309 -0
  46. data/docs/diagrams/transport-extension-architecture.svg +234 -0
  47. data/docs/diagrams/transport-selection-flowchart.svg +264 -0
  48. data/docs/examples/advanced-examples.md +951 -0
  49. data/docs/examples/basic-examples.md +268 -0
  50. data/docs/{agent_registry_processes.md → framework-components/agent-registry.md} +1 -1
  51. data/docs/{message_processing.md → framework-components/message-processing.md} +3 -1
  52. data/docs/getting-started/basic-example.md +306 -0
  53. data/docs/getting-started/installation.md +160 -0
  54. data/docs/getting-started/overview.md +64 -0
  55. data/docs/getting-started/quick-start.md +179 -0
  56. data/docs/index.md +97 -0
  57. data/examples/DEMO.md +148 -0
  58. data/examples/README.md +50 -0
  59. data/examples/bad_agent.rb +32 -0
  60. data/examples/registry.rb +0 -8
  61. data/examples/run_demo.rb +433 -0
  62. data/lib/agent99/amqp_message_client.rb +2 -2
  63. data/lib/agent99/base.rb +1 -1
  64. data/lib/agent99/message_processing.rb +6 -12
  65. data/lib/agent99/registry_client.rb +4 -1
  66. data/lib/agent99/version.rb +1 -1
  67. data/lib/agent99.rb +1 -1
  68. data/mkdocs.yml +195 -0
  69. data/p2p_plan.md +533 -0
  70. data/p2p_roadmap.md +299 -0
  71. data/registry_plan.md +1818 -0
  72. metadata +89 -32
  73. data/docs/README.md +0 -57
  74. data/docs/diagrams/agent_registry_processes.dot +0 -42
  75. data/docs/diagrams/agent_registry_processes.png +0 -0
  76. data/docs/diagrams/high_level_architecture.dot +0 -26
  77. data/docs/diagrams/high_level_architecture.png +0 -0
  78. data/docs/diagrams/request_flow.dot +0 -42
  79. data/docs/diagrams/request_flow.png +0 -0
  80. /data/docs/{advanced_features.md → advanced-topics/advanced-features.md} +0 -0
  81. /data/docs/{extending_the_framework.md → advanced-topics/extending-the-framework.md} +0 -0
  82. /data/docs/{custom_agent_implementation.md → agent-development/custom-agent-implementation.md} +0 -0
  83. /data/docs/{error_handling_and_logging.md → agent-development/error-handling-and-logging.md} +0 -0
  84. /data/docs/{schema_definition.md → agent-development/schema-definition.md} +0 -0
  85. /data/docs/{api_reference.md → api-reference/overview.md} +0 -0
  86. /data/docs/{agent_discovery.md → framework-components/agent-discovery.md} +0 -0
  87. /data/docs/{messaging_system.md → framework-components/messaging-system.md} +0 -0
  88. /data/docs/{breaking_change_v0.0.4.md → operations/breaking-changes.md} +0 -0
  89. /data/docs/{configuration.md → operations/configuration.md} +0 -0
  90. /data/docs/{preformance_considerations.md → operations/performance-considerations.md} +0 -0
  91. /data/docs/{security.md → operations/security.md} +0 -0
  92. /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
+ ![Request Processing Lifecycle](../assets/images/request-processing-lifecycle.svg)
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