a2a-ruby 1.0.0
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +137 -0
- data/.simplecov +46 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +33 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/CONTRIBUTING.md +165 -0
- data/Gemfile +43 -0
- data/Guardfile +34 -0
- data/LICENSE.txt +21 -0
- data/PUBLISHING_CHECKLIST.md +214 -0
- data/README.md +171 -0
- data/Rakefile +165 -0
- data/docs/agent_execution.md +309 -0
- data/docs/api_reference.md +792 -0
- data/docs/configuration.md +780 -0
- data/docs/events.md +475 -0
- data/docs/getting_started.md +668 -0
- data/docs/integration.md +262 -0
- data/docs/server_apps.md +621 -0
- data/docs/troubleshooting.md +765 -0
- data/lib/a2a/client/api_methods.rb +263 -0
- data/lib/a2a/client/auth/api_key.rb +161 -0
- data/lib/a2a/client/auth/interceptor.rb +288 -0
- data/lib/a2a/client/auth/jwt.rb +189 -0
- data/lib/a2a/client/auth/oauth2.rb +146 -0
- data/lib/a2a/client/auth.rb +137 -0
- data/lib/a2a/client/base.rb +316 -0
- data/lib/a2a/client/config.rb +210 -0
- data/lib/a2a/client/connection_pool.rb +233 -0
- data/lib/a2a/client/http_client.rb +524 -0
- data/lib/a2a/client/json_rpc_handler.rb +136 -0
- data/lib/a2a/client/middleware/circuit_breaker_interceptor.rb +245 -0
- data/lib/a2a/client/middleware/logging_interceptor.rb +371 -0
- data/lib/a2a/client/middleware/rate_limit_interceptor.rb +142 -0
- data/lib/a2a/client/middleware/retry_interceptor.rb +161 -0
- data/lib/a2a/client/middleware.rb +116 -0
- data/lib/a2a/client/performance_tracker.rb +60 -0
- data/lib/a2a/configuration/defaults.rb +34 -0
- data/lib/a2a/configuration/environment_loader.rb +76 -0
- data/lib/a2a/configuration/file_loader.rb +115 -0
- data/lib/a2a/configuration/inheritance.rb +101 -0
- data/lib/a2a/configuration/validator.rb +180 -0
- data/lib/a2a/configuration.rb +201 -0
- data/lib/a2a/errors.rb +291 -0
- data/lib/a2a/modules.rb +50 -0
- data/lib/a2a/monitoring/alerting.rb +490 -0
- data/lib/a2a/monitoring/distributed_tracing.rb +398 -0
- data/lib/a2a/monitoring/health_endpoints.rb +204 -0
- data/lib/a2a/monitoring/metrics_collector.rb +438 -0
- data/lib/a2a/monitoring.rb +463 -0
- data/lib/a2a/plugin.rb +358 -0
- data/lib/a2a/plugin_manager.rb +159 -0
- data/lib/a2a/plugins/example_auth.rb +81 -0
- data/lib/a2a/plugins/example_middleware.rb +118 -0
- data/lib/a2a/plugins/example_transport.rb +76 -0
- data/lib/a2a/protocol/agent_card.rb +8 -0
- data/lib/a2a/protocol/agent_card_server.rb +584 -0
- data/lib/a2a/protocol/capability.rb +496 -0
- data/lib/a2a/protocol/json_rpc.rb +254 -0
- data/lib/a2a/protocol/message.rb +8 -0
- data/lib/a2a/protocol/task.rb +8 -0
- data/lib/a2a/rails/a2a_controller.rb +258 -0
- data/lib/a2a/rails/controller_helpers.rb +499 -0
- data/lib/a2a/rails/engine.rb +167 -0
- data/lib/a2a/rails/generators/agent_generator.rb +311 -0
- data/lib/a2a/rails/generators/install_generator.rb +209 -0
- data/lib/a2a/rails/generators/migration_generator.rb +232 -0
- data/lib/a2a/rails/generators/templates/add_a2a_indexes.rb +57 -0
- data/lib/a2a/rails/generators/templates/agent_controller.rb +122 -0
- data/lib/a2a/rails/generators/templates/agent_controller_spec.rb +160 -0
- data/lib/a2a/rails/generators/templates/agent_readme.md +200 -0
- data/lib/a2a/rails/generators/templates/create_a2a_push_notification_configs.rb +68 -0
- data/lib/a2a/rails/generators/templates/create_a2a_tasks.rb +83 -0
- data/lib/a2a/rails/generators/templates/example_agent_controller.rb +228 -0
- data/lib/a2a/rails/generators/templates/initializer.rb +108 -0
- data/lib/a2a/rails/generators/templates/push_notification_config_model.rb +228 -0
- data/lib/a2a/rails/generators/templates/task_model.rb +200 -0
- data/lib/a2a/rails/tasks/a2a.rake +228 -0
- data/lib/a2a/server/a2a_methods.rb +520 -0
- data/lib/a2a/server/agent.rb +537 -0
- data/lib/a2a/server/agent_execution/agent_executor.rb +279 -0
- data/lib/a2a/server/agent_execution/request_context.rb +219 -0
- data/lib/a2a/server/apps/rack_app.rb +311 -0
- data/lib/a2a/server/apps/sinatra_app.rb +261 -0
- data/lib/a2a/server/default_request_handler.rb +350 -0
- data/lib/a2a/server/events/event_consumer.rb +116 -0
- data/lib/a2a/server/events/event_queue.rb +226 -0
- data/lib/a2a/server/example_agent.rb +248 -0
- data/lib/a2a/server/handler.rb +281 -0
- data/lib/a2a/server/middleware/authentication_middleware.rb +212 -0
- data/lib/a2a/server/middleware/cors_middleware.rb +171 -0
- data/lib/a2a/server/middleware/logging_middleware.rb +362 -0
- data/lib/a2a/server/middleware/rate_limit_middleware.rb +382 -0
- data/lib/a2a/server/middleware.rb +213 -0
- data/lib/a2a/server/push_notification_manager.rb +327 -0
- data/lib/a2a/server/request_handler.rb +136 -0
- data/lib/a2a/server/storage/base.rb +141 -0
- data/lib/a2a/server/storage/database.rb +266 -0
- data/lib/a2a/server/storage/memory.rb +274 -0
- data/lib/a2a/server/storage/redis.rb +320 -0
- data/lib/a2a/server/storage.rb +38 -0
- data/lib/a2a/server/task_manager.rb +534 -0
- data/lib/a2a/transport/grpc.rb +481 -0
- data/lib/a2a/transport/http.rb +415 -0
- data/lib/a2a/transport/sse.rb +499 -0
- data/lib/a2a/types/agent_card.rb +540 -0
- data/lib/a2a/types/artifact.rb +99 -0
- data/lib/a2a/types/base_model.rb +223 -0
- data/lib/a2a/types/events.rb +117 -0
- data/lib/a2a/types/message.rb +106 -0
- data/lib/a2a/types/part.rb +288 -0
- data/lib/a2a/types/push_notification.rb +139 -0
- data/lib/a2a/types/security.rb +167 -0
- data/lib/a2a/types/task.rb +154 -0
- data/lib/a2a/types.rb +88 -0
- data/lib/a2a/utils/helpers.rb +245 -0
- data/lib/a2a/utils/message_buffer.rb +278 -0
- data/lib/a2a/utils/performance.rb +247 -0
- data/lib/a2a/utils/rails_detection.rb +97 -0
- data/lib/a2a/utils/structured_logger.rb +306 -0
- data/lib/a2a/utils/time_helpers.rb +167 -0
- data/lib/a2a/utils/validation.rb +8 -0
- data/lib/a2a/version.rb +6 -0
- data/lib/a2a-rails.rb +58 -0
- data/lib/a2a.rb +198 -0
- metadata +437 -0
data/docs/events.md
ADDED
@@ -0,0 +1,475 @@
|
|
1
|
+
# Event System
|
2
|
+
|
3
|
+
The A2A Ruby SDK includes a comprehensive event system that enables real-time, asynchronous processing of agent requests and responses. This event-driven architecture allows for streaming responses, task updates, and flexible agent implementations.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The event system consists of:
|
8
|
+
|
9
|
+
- **Event**: Represents a single event with type, data, and metadata
|
10
|
+
- **EventQueue**: Manages event publishing and subscription
|
11
|
+
- **EventConsumer**: Processes events with registered handlers
|
12
|
+
- **Event Types**: Predefined event types for different scenarios
|
13
|
+
|
14
|
+
## Event Types
|
15
|
+
|
16
|
+
### Core Event Types
|
17
|
+
|
18
|
+
- `task` - Complete task objects
|
19
|
+
- `message` - Message objects (user or agent messages)
|
20
|
+
- `task_status_update` - Task status change events
|
21
|
+
- `task_artifact_update` - Task artifact addition/modification events
|
22
|
+
|
23
|
+
### Event Structure
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
event = A2A::Server::Events::Event.new(
|
27
|
+
type: "task_status_update",
|
28
|
+
data: status_update_object,
|
29
|
+
id: "optional-custom-id" # Auto-generated if not provided
|
30
|
+
)
|
31
|
+
|
32
|
+
# Event properties
|
33
|
+
event.type # => "task_status_update"
|
34
|
+
event.data # => The event data object
|
35
|
+
event.timestamp # => Time when event was created
|
36
|
+
event.id # => Unique event identifier
|
37
|
+
event.task_id # => Task ID if applicable
|
38
|
+
event.context_id # => Context ID if applicable
|
39
|
+
```
|
40
|
+
|
41
|
+
## Event Queue
|
42
|
+
|
43
|
+
### In-Memory Event Queue
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
# Create event queue
|
47
|
+
queue = A2A::Server::Events::InMemoryEventQueue.new
|
48
|
+
|
49
|
+
# Publish events
|
50
|
+
event = A2A::Server::Events::Event.new(
|
51
|
+
type: "message",
|
52
|
+
data: message_object
|
53
|
+
)
|
54
|
+
queue.publish(event)
|
55
|
+
|
56
|
+
# Subscribe to events
|
57
|
+
queue.subscribe do |event|
|
58
|
+
puts "Received event: #{event.type}"
|
59
|
+
process_event(event)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Subscribe with filtering
|
63
|
+
queue.subscribe(->(event) { event.task_event? }) do |event|
|
64
|
+
puts "Task-related event: #{event.type}"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Clean up
|
68
|
+
queue.close
|
69
|
+
```
|
70
|
+
|
71
|
+
### Custom Event Queue Implementation
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class RedisEventQueue < A2A::Server::Events::EventQueue
|
75
|
+
def initialize(redis_client)
|
76
|
+
@redis = redis_client
|
77
|
+
@subscribers = []
|
78
|
+
end
|
79
|
+
|
80
|
+
def publish(event)
|
81
|
+
@redis.publish("a2a_events", event.to_h.to_json)
|
82
|
+
end
|
83
|
+
|
84
|
+
def subscribe(filter = nil)
|
85
|
+
# Implementation for Redis pub/sub
|
86
|
+
@redis.subscribe("a2a_events") do |on|
|
87
|
+
on.message do |channel, message|
|
88
|
+
event_data = JSON.parse(message)
|
89
|
+
event = A2A::Server::Events::Event.new(**event_data.symbolize_keys)
|
90
|
+
|
91
|
+
next if filter && !filter.call(event)
|
92
|
+
|
93
|
+
yield event if block_given?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def close
|
99
|
+
@redis.unsubscribe("a2a_events")
|
100
|
+
end
|
101
|
+
|
102
|
+
def closed?
|
103
|
+
!@redis.connected?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
## Event Consumer
|
109
|
+
|
110
|
+
The EventConsumer provides a higher-level interface for processing events:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
# Create consumer
|
114
|
+
consumer = A2A::Server::Events::EventConsumer.new(event_queue)
|
115
|
+
|
116
|
+
# Register handlers for specific event types
|
117
|
+
consumer.register_handler("task") do |event|
|
118
|
+
task = event.data
|
119
|
+
puts "Task received: #{task.id}"
|
120
|
+
|
121
|
+
# Save to database
|
122
|
+
TaskStore.save(task)
|
123
|
+
end
|
124
|
+
|
125
|
+
consumer.register_handler("task_status_update") do |event|
|
126
|
+
status_update = event.data
|
127
|
+
puts "Task #{status_update.task_id} status: #{status_update.status.state}"
|
128
|
+
|
129
|
+
# Update task in database
|
130
|
+
TaskStore.update_status(status_update.task_id, status_update.status)
|
131
|
+
|
132
|
+
# Send notifications
|
133
|
+
NotificationService.notify_status_change(status_update)
|
134
|
+
end
|
135
|
+
|
136
|
+
consumer.register_handler("message") do |event|
|
137
|
+
message = event.data
|
138
|
+
puts "Message: #{message.parts.first.text}"
|
139
|
+
|
140
|
+
# Process message
|
141
|
+
MessageProcessor.process(message)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Start consuming events
|
145
|
+
consumer.start
|
146
|
+
|
147
|
+
# Stop consuming
|
148
|
+
consumer.stop
|
149
|
+
```
|
150
|
+
|
151
|
+
## Event Filtering
|
152
|
+
|
153
|
+
### Basic Filtering
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
# Filter by event type
|
157
|
+
task_filter = ->(event) { event.task_event? }
|
158
|
+
message_filter = ->(event) { event.message_event? }
|
159
|
+
|
160
|
+
# Filter by task ID
|
161
|
+
task_id_filter = ->(event) { event.task_id == "specific-task-id" }
|
162
|
+
|
163
|
+
# Filter by context ID
|
164
|
+
context_filter = ->(event) { event.context_id == "specific-context-id" }
|
165
|
+
|
166
|
+
# Combine filters
|
167
|
+
combined_filter = ->(event) {
|
168
|
+
event.task_event? && event.task_id == "task-123"
|
169
|
+
}
|
170
|
+
|
171
|
+
queue.subscribe(combined_filter) do |event|
|
172
|
+
# Only receives task events for task-123
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
### Advanced Filtering
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
class EventFilter
|
180
|
+
def initialize(criteria)
|
181
|
+
@criteria = criteria
|
182
|
+
end
|
183
|
+
|
184
|
+
def call(event)
|
185
|
+
@criteria.all? { |key, value| matches?(event, key, value) }
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
def matches?(event, key, value)
|
191
|
+
case key
|
192
|
+
when :type
|
193
|
+
event.type == value
|
194
|
+
when :task_id
|
195
|
+
event.task_id == value
|
196
|
+
when :context_id
|
197
|
+
event.context_id == value
|
198
|
+
when :after
|
199
|
+
event.timestamp > value
|
200
|
+
when :user_id
|
201
|
+
event.data.respond_to?(:user_id) && event.data.user_id == value
|
202
|
+
else
|
203
|
+
false
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Usage
|
209
|
+
filter = EventFilter.new(
|
210
|
+
type: "task_status_update",
|
211
|
+
task_id: "task-123",
|
212
|
+
after: 1.hour.ago
|
213
|
+
)
|
214
|
+
|
215
|
+
queue.subscribe(filter) do |event|
|
216
|
+
# Process filtered events
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
## Integration with Agent Executors
|
221
|
+
|
222
|
+
Agent executors use the event system to communicate results:
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
class StreamingAgentExecutor < A2A::Server::AgentExecution::AgentExecutor
|
226
|
+
def execute(context, event_queue)
|
227
|
+
# Create task
|
228
|
+
task = create_task(context)
|
229
|
+
|
230
|
+
# Publish initial task
|
231
|
+
publish_task(event_queue, task)
|
232
|
+
|
233
|
+
# Publish status updates
|
234
|
+
publish_task_status_update(
|
235
|
+
event_queue,
|
236
|
+
task.id,
|
237
|
+
task.context_id,
|
238
|
+
working_status
|
239
|
+
)
|
240
|
+
|
241
|
+
# Process and publish streaming results
|
242
|
+
process_streaming(context.message, event_queue, task)
|
243
|
+
|
244
|
+
# Publish completion
|
245
|
+
publish_task_status_update(
|
246
|
+
event_queue,
|
247
|
+
task.id,
|
248
|
+
task.context_id,
|
249
|
+
completed_status
|
250
|
+
)
|
251
|
+
end
|
252
|
+
|
253
|
+
private
|
254
|
+
|
255
|
+
def process_streaming(message, event_queue, task)
|
256
|
+
words = message.parts.first.text.split
|
257
|
+
|
258
|
+
words.each do |word|
|
259
|
+
# Create partial response
|
260
|
+
response = A2A::Types::Message.new(
|
261
|
+
message_id: SecureRandom.uuid,
|
262
|
+
context_id: task.context_id,
|
263
|
+
role: "assistant",
|
264
|
+
parts: [A2A::Types::TextPart.new(text: word)]
|
265
|
+
)
|
266
|
+
|
267
|
+
# Publish message event
|
268
|
+
publish_message(event_queue, response)
|
269
|
+
|
270
|
+
sleep 0.1 # Simulate processing delay
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
```
|
275
|
+
|
276
|
+
## Event Persistence
|
277
|
+
|
278
|
+
### Database Event Store
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
class DatabaseEventStore
|
282
|
+
def initialize(db_connection)
|
283
|
+
@db = db_connection
|
284
|
+
end
|
285
|
+
|
286
|
+
def store_event(event)
|
287
|
+
@db.execute(
|
288
|
+
"INSERT INTO events (id, type, data, timestamp, task_id, context_id) VALUES (?, ?, ?, ?, ?, ?)",
|
289
|
+
event.id,
|
290
|
+
event.type,
|
291
|
+
event.data.to_json,
|
292
|
+
event.timestamp,
|
293
|
+
event.task_id,
|
294
|
+
event.context_id
|
295
|
+
)
|
296
|
+
end
|
297
|
+
|
298
|
+
def get_events(task_id: nil, context_id: nil, after: nil)
|
299
|
+
conditions = []
|
300
|
+
params = []
|
301
|
+
|
302
|
+
if task_id
|
303
|
+
conditions << "task_id = ?"
|
304
|
+
params << task_id
|
305
|
+
end
|
306
|
+
|
307
|
+
if context_id
|
308
|
+
conditions << "context_id = ?"
|
309
|
+
params << context_id
|
310
|
+
end
|
311
|
+
|
312
|
+
if after
|
313
|
+
conditions << "timestamp > ?"
|
314
|
+
params << after
|
315
|
+
end
|
316
|
+
|
317
|
+
where_clause = conditions.empty? ? "" : "WHERE #{conditions.join(' AND ')}"
|
318
|
+
|
319
|
+
@db.execute("SELECT * FROM events #{where_clause} ORDER BY timestamp", *params)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
# Usage with event consumer
|
324
|
+
event_store = DatabaseEventStore.new(db_connection)
|
325
|
+
|
326
|
+
consumer.register_handler("task") do |event|
|
327
|
+
event_store.store_event(event)
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
331
|
+
## Real-time Notifications
|
332
|
+
|
333
|
+
### WebSocket Integration
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
class WebSocketEventBroadcaster
|
337
|
+
def initialize(websocket_server)
|
338
|
+
@ws_server = websocket_server
|
339
|
+
@client_subscriptions = {}
|
340
|
+
end
|
341
|
+
|
342
|
+
def subscribe_client(client_id, websocket, filters = {})
|
343
|
+
@client_subscriptions[client_id] = {
|
344
|
+
websocket: websocket,
|
345
|
+
filters: filters
|
346
|
+
}
|
347
|
+
end
|
348
|
+
|
349
|
+
def broadcast_event(event)
|
350
|
+
@client_subscriptions.each do |client_id, subscription|
|
351
|
+
next unless event_matches_filters?(event, subscription[:filters])
|
352
|
+
|
353
|
+
begin
|
354
|
+
subscription[:websocket].send(event.to_h.to_json)
|
355
|
+
rescue => e
|
356
|
+
# Remove disconnected clients
|
357
|
+
@client_subscriptions.delete(client_id)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
private
|
363
|
+
|
364
|
+
def event_matches_filters?(event, filters)
|
365
|
+
return true if filters.empty?
|
366
|
+
|
367
|
+
filters.all? do |key, value|
|
368
|
+
case key
|
369
|
+
when :task_id
|
370
|
+
event.task_id == value
|
371
|
+
when :context_id
|
372
|
+
event.context_id == value
|
373
|
+
when :event_types
|
374
|
+
value.include?(event.type)
|
375
|
+
else
|
376
|
+
true
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# Integration
|
383
|
+
broadcaster = WebSocketEventBroadcaster.new(ws_server)
|
384
|
+
|
385
|
+
consumer.register_handler("task_status_update") do |event|
|
386
|
+
broadcaster.broadcast_event(event)
|
387
|
+
end
|
388
|
+
```
|
389
|
+
|
390
|
+
## Testing Events
|
391
|
+
|
392
|
+
### Testing Event Publishing
|
393
|
+
|
394
|
+
```ruby
|
395
|
+
RSpec.describe "Event Publishing" do
|
396
|
+
let(:event_queue) { A2A::Server::Events::InMemoryEventQueue.new }
|
397
|
+
let(:executor) { MyAgentExecutor.new }
|
398
|
+
|
399
|
+
it "publishes task events" do
|
400
|
+
events = []
|
401
|
+
event_queue.subscribe { |event| events << event }
|
402
|
+
|
403
|
+
context = create_test_context
|
404
|
+
executor.execute(context, event_queue)
|
405
|
+
|
406
|
+
task_events = events.select { |e| e.type == "task" }
|
407
|
+
expect(task_events).not_to be_empty
|
408
|
+
|
409
|
+
status_events = events.select { |e| e.type == "task_status_update" }
|
410
|
+
expect(status_events.map(&:data).map(&:status).map(&:state))
|
411
|
+
.to include("working", "completed")
|
412
|
+
end
|
413
|
+
end
|
414
|
+
```
|
415
|
+
|
416
|
+
### Testing Event Consumption
|
417
|
+
|
418
|
+
```ruby
|
419
|
+
RSpec.describe "Event Consumption" do
|
420
|
+
let(:event_queue) { A2A::Server::Events::InMemoryEventQueue.new }
|
421
|
+
let(:consumer) { A2A::Server::Events::EventConsumer.new(event_queue) }
|
422
|
+
|
423
|
+
it "processes events with handlers" do
|
424
|
+
processed_events = []
|
425
|
+
|
426
|
+
consumer.register_handler("test") do |event|
|
427
|
+
processed_events << event
|
428
|
+
end
|
429
|
+
|
430
|
+
consumer.start
|
431
|
+
|
432
|
+
# Publish test event
|
433
|
+
test_event = A2A::Server::Events::Event.new(
|
434
|
+
type: "test",
|
435
|
+
data: { message: "test data" }
|
436
|
+
)
|
437
|
+
event_queue.publish(test_event)
|
438
|
+
|
439
|
+
# Wait for processing
|
440
|
+
sleep 0.1
|
441
|
+
|
442
|
+
expect(processed_events).to include(test_event)
|
443
|
+
|
444
|
+
consumer.stop
|
445
|
+
end
|
446
|
+
end
|
447
|
+
```
|
448
|
+
|
449
|
+
## Performance Considerations
|
450
|
+
|
451
|
+
1. **Queue Size**: Monitor queue size to prevent memory issues
|
452
|
+
2. **Event Filtering**: Use efficient filters to reduce processing overhead
|
453
|
+
3. **Batch Processing**: Consider batching events for high-throughput scenarios
|
454
|
+
4. **Persistence**: Choose appropriate persistence strategies based on requirements
|
455
|
+
5. **Error Handling**: Implement robust error handling to prevent event loss
|
456
|
+
|
457
|
+
## Best Practices
|
458
|
+
|
459
|
+
1. **Event Granularity**: Balance between too many small events and too few large events
|
460
|
+
2. **Event Ordering**: Consider event ordering requirements for your use case
|
461
|
+
3. **Error Recovery**: Implement retry mechanisms for failed event processing
|
462
|
+
4. **Monitoring**: Monitor event queue health and processing metrics
|
463
|
+
5. **Testing**: Thoroughly test event flows in your application
|
464
|
+
6. **Documentation**: Document your custom event types and their expected data structures
|
465
|
+
|
466
|
+
The event system provides a powerful foundation for building responsive, real-time A2A agents while maintaining clean separation of concerns and enabling sophisticated processing workflows.
|
467
|
+
|
468
|
+
## Complete Examples
|
469
|
+
|
470
|
+
For complete working examples of event-driven agents, see the [A2A Ruby Samples Repository](https://github.com/a2aproject/a2a-ruby-samples), which includes:
|
471
|
+
|
472
|
+
- **Streaming Chat Agent** - Real-time event processing with Server-Sent Events
|
473
|
+
- **File Processing Agent** - Background job processing with progress events
|
474
|
+
- **Multi-Agent Client** - Event orchestration across multiple agents
|
475
|
+
- **Task Management Examples** - Complete task lifecycle event handling
|