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
@@ -0,0 +1,765 @@
|
|
1
|
+
# Troubleshooting Guide
|
2
|
+
|
3
|
+
This guide helps you diagnose and resolve common issues when using the A2A Ruby SDK.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
|
7
|
+
- [Connection Issues](#connection-issues)
|
8
|
+
- [Authentication Problems](#authentication-problems)
|
9
|
+
- [Protocol Errors](#protocol-errors)
|
10
|
+
- [Performance Issues](#performance-issues)
|
11
|
+
- [Rails Integration Issues](#rails-integration-issues)
|
12
|
+
- [Debugging Tools](#debugging-tools)
|
13
|
+
- [Common Error Codes](#common-error-codes)
|
14
|
+
- [Frequently Asked Questions](#frequently-asked-questions)
|
15
|
+
|
16
|
+
## Connection Issues
|
17
|
+
|
18
|
+
### Cannot Connect to Agent
|
19
|
+
|
20
|
+
**Symptoms:**
|
21
|
+
- `A2A::Errors::HTTPError: Connection refused`
|
22
|
+
- `A2A::Errors::TimeoutError: Request timeout`
|
23
|
+
|
24
|
+
**Solutions:**
|
25
|
+
|
26
|
+
1. **Check the endpoint URL:**
|
27
|
+
```ruby
|
28
|
+
# Verify the URL is correct and accessible
|
29
|
+
client = A2A::Client::HttpClient.new("https://agent.example.com/a2a")
|
30
|
+
|
31
|
+
# Test basic connectivity
|
32
|
+
begin
|
33
|
+
card = client.get_card
|
34
|
+
puts "Connection successful"
|
35
|
+
rescue A2A::Errors::HTTPError => e
|
36
|
+
puts "Connection failed: #{e.message}"
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
2. **Verify SSL certificates:**
|
41
|
+
```ruby
|
42
|
+
# For development, you might need to disable SSL verification
|
43
|
+
require 'faraday'
|
44
|
+
|
45
|
+
client = A2A::Client::HttpClient.new("https://localhost:3000/a2a") do |conn|
|
46
|
+
conn.ssl.verify = false # Only for development!
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
3. **Check firewall and network settings:**
|
51
|
+
```bash
|
52
|
+
# Test basic connectivity
|
53
|
+
curl -v https://agent.example.com/a2a/agent-card
|
54
|
+
|
55
|
+
# Check DNS resolution
|
56
|
+
nslookup agent.example.com
|
57
|
+
```
|
58
|
+
|
59
|
+
### Timeout Issues
|
60
|
+
|
61
|
+
**Symptoms:**
|
62
|
+
- Requests taking too long
|
63
|
+
- `A2A::Errors::TimeoutError`
|
64
|
+
|
65
|
+
**Solutions:**
|
66
|
+
|
67
|
+
1. **Increase timeout:**
|
68
|
+
```ruby
|
69
|
+
config = A2A::Client::Config.new
|
70
|
+
config.timeout = 120 # 2 minutes
|
71
|
+
|
72
|
+
client = A2A::Client::HttpClient.new(url, config: config)
|
73
|
+
```
|
74
|
+
|
75
|
+
2. **Use streaming for long operations:**
|
76
|
+
```ruby
|
77
|
+
# Instead of blocking calls, use streaming
|
78
|
+
client.send_message(message) do |response|
|
79
|
+
# Handle responses as they arrive
|
80
|
+
puts "Received: #{response}"
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
3. **Implement retry logic:**
|
85
|
+
```ruby
|
86
|
+
require 'retries'
|
87
|
+
|
88
|
+
with_retries(max_tries: 3, base_sleep_seconds: 1, max_sleep_seconds: 5) do
|
89
|
+
client.send_message(message)
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
## Authentication Problems
|
94
|
+
|
95
|
+
### Invalid Credentials
|
96
|
+
|
97
|
+
**Symptoms:**
|
98
|
+
- `A2A::Errors::AuthenticationError: Invalid credentials`
|
99
|
+
- HTTP 401 Unauthorized responses
|
100
|
+
|
101
|
+
**Solutions:**
|
102
|
+
|
103
|
+
1. **Verify OAuth 2.0 configuration:**
|
104
|
+
```ruby
|
105
|
+
auth = A2A::Client::Auth::OAuth2.new(
|
106
|
+
client_id: ENV['A2A_CLIENT_ID'],
|
107
|
+
client_secret: ENV['A2A_CLIENT_SECRET'],
|
108
|
+
token_url: "https://auth.example.com/oauth/token"
|
109
|
+
)
|
110
|
+
|
111
|
+
# Test token acquisition
|
112
|
+
begin
|
113
|
+
token = auth.get_token
|
114
|
+
puts "Token acquired: #{token[0..20]}..."
|
115
|
+
rescue => e
|
116
|
+
puts "Token error: #{e.message}"
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
2. **Check JWT token validity:**
|
121
|
+
```ruby
|
122
|
+
require 'jwt'
|
123
|
+
|
124
|
+
token = "your-jwt-token"
|
125
|
+
begin
|
126
|
+
payload = JWT.decode(token, nil, false) # Don't verify for debugging
|
127
|
+
puts "Token payload: #{payload}"
|
128
|
+
puts "Expires at: #{Time.at(payload[0]['exp'])}"
|
129
|
+
rescue JWT::DecodeError => e
|
130
|
+
puts "Invalid JWT: #{e.message}"
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
3. **Verify API key configuration:**
|
135
|
+
```ruby
|
136
|
+
auth = A2A::Client::Auth::ApiKey.new(
|
137
|
+
key: ENV['A2A_API_KEY'],
|
138
|
+
header: "X-API-Key" # or parameter: "api_key"
|
139
|
+
)
|
140
|
+
|
141
|
+
# Test with debug logging
|
142
|
+
A2A.configure { |c| c.log_level = :debug }
|
143
|
+
client = A2A::Client::HttpClient.new(url, auth: auth)
|
144
|
+
```
|
145
|
+
|
146
|
+
### Token Expiration
|
147
|
+
|
148
|
+
**Symptoms:**
|
149
|
+
- Authentication works initially, then fails
|
150
|
+
- `A2A::Errors::AuthenticationError: Token expired`
|
151
|
+
|
152
|
+
**Solutions:**
|
153
|
+
|
154
|
+
1. **Implement automatic token refresh:**
|
155
|
+
```ruby
|
156
|
+
class RefreshableAuth < A2A::Client::Auth::OAuth2
|
157
|
+
def apply_auth(request)
|
158
|
+
if token_expired?
|
159
|
+
refresh_token!
|
160
|
+
end
|
161
|
+
super
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def token_expired?
|
167
|
+
@token_expires_at && Time.current >= @token_expires_at
|
168
|
+
end
|
169
|
+
|
170
|
+
def refresh_token!
|
171
|
+
@token, @token_expires_at = get_token
|
172
|
+
end
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
2. **Handle auth errors gracefully:**
|
177
|
+
```ruby
|
178
|
+
begin
|
179
|
+
response = client.send_message(message)
|
180
|
+
rescue A2A::Errors::AuthenticationError
|
181
|
+
# Refresh credentials and retry
|
182
|
+
client.auth.refresh!
|
183
|
+
response = client.send_message(message)
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
## Protocol Errors
|
188
|
+
|
189
|
+
### Invalid JSON-RPC Format
|
190
|
+
|
191
|
+
**Symptoms:**
|
192
|
+
- `A2A::Errors::InvalidRequest: Invalid JSON-RPC format`
|
193
|
+
- `A2A::Errors::ParseError: Invalid JSON`
|
194
|
+
|
195
|
+
**Solutions:**
|
196
|
+
|
197
|
+
1. **Validate message structure:**
|
198
|
+
```ruby
|
199
|
+
message = A2A::Types::Message.new(
|
200
|
+
message_id: SecureRandom.uuid, # Required
|
201
|
+
role: "user", # Required: "user" or "agent"
|
202
|
+
parts: [ # Required: array of parts
|
203
|
+
A2A::Types::TextPart.new(text: "Hello")
|
204
|
+
]
|
205
|
+
)
|
206
|
+
|
207
|
+
# Validate before sending
|
208
|
+
if message.valid?
|
209
|
+
client.send_message(message)
|
210
|
+
else
|
211
|
+
puts "Invalid message: #{message.errors.full_messages}"
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
2. **Check JSON-RPC request format:**
|
216
|
+
```ruby
|
217
|
+
# Manual JSON-RPC request (for debugging)
|
218
|
+
request = {
|
219
|
+
jsonrpc: "2.0", # Required
|
220
|
+
method: "message/send", # Required
|
221
|
+
params: message.to_h, # Optional
|
222
|
+
id: 1 # Required for requests (omit for notifications)
|
223
|
+
}
|
224
|
+
```
|
225
|
+
|
226
|
+
### Method Not Found
|
227
|
+
|
228
|
+
**Symptoms:**
|
229
|
+
- `A2A::Errors::MethodNotFound: Method 'xyz' not found`
|
230
|
+
|
231
|
+
**Solutions:**
|
232
|
+
|
233
|
+
1. **Check agent card for available methods:**
|
234
|
+
```ruby
|
235
|
+
card = client.get_card
|
236
|
+
puts "Available capabilities:"
|
237
|
+
card.capabilities.each do |capability|
|
238
|
+
puts "- #{capability.name}: #{capability.description}"
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
2. **Verify method name spelling:**
|
243
|
+
```ruby
|
244
|
+
# Standard A2A methods
|
245
|
+
valid_methods = [
|
246
|
+
"message/send",
|
247
|
+
"message/stream",
|
248
|
+
"tasks/get",
|
249
|
+
"tasks/cancel",
|
250
|
+
"tasks/resubscribe",
|
251
|
+
"tasks/pushNotificationConfig/set",
|
252
|
+
"tasks/pushNotificationConfig/get",
|
253
|
+
"tasks/pushNotificationConfig/list",
|
254
|
+
"tasks/pushNotificationConfig/delete",
|
255
|
+
"agent/getAuthenticatedExtendedCard"
|
256
|
+
]
|
257
|
+
```
|
258
|
+
|
259
|
+
### Task Errors
|
260
|
+
|
261
|
+
**Symptoms:**
|
262
|
+
- `A2A::Errors::TaskNotFound: Task not found`
|
263
|
+
- `A2A::Errors::TaskNotCancelable: Task cannot be canceled`
|
264
|
+
|
265
|
+
**Solutions:**
|
266
|
+
|
267
|
+
1. **Check task status before operations:**
|
268
|
+
```ruby
|
269
|
+
begin
|
270
|
+
task = client.get_task(task_id)
|
271
|
+
puts "Task state: #{task.status.state}"
|
272
|
+
|
273
|
+
if task.status.state.in?(['submitted', 'working'])
|
274
|
+
client.cancel_task(task_id)
|
275
|
+
else
|
276
|
+
puts "Task cannot be canceled (state: #{task.status.state})"
|
277
|
+
end
|
278
|
+
rescue A2A::Errors::TaskNotFound
|
279
|
+
puts "Task #{task_id} not found"
|
280
|
+
end
|
281
|
+
```
|
282
|
+
|
283
|
+
2. **Handle task lifecycle properly:**
|
284
|
+
```ruby
|
285
|
+
# Valid task states
|
286
|
+
CANCELABLE_STATES = ['submitted', 'working'].freeze
|
287
|
+
FINAL_STATES = ['completed', 'canceled', 'failed', 'rejected'].freeze
|
288
|
+
|
289
|
+
def can_cancel_task?(task)
|
290
|
+
CANCELABLE_STATES.include?(task.status.state)
|
291
|
+
end
|
292
|
+
|
293
|
+
def task_finished?(task)
|
294
|
+
FINAL_STATES.include?(task.status.state)
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
## Performance Issues
|
299
|
+
|
300
|
+
### Slow Response Times
|
301
|
+
|
302
|
+
**Symptoms:**
|
303
|
+
- Requests taking longer than expected
|
304
|
+
- High memory usage
|
305
|
+
|
306
|
+
**Solutions:**
|
307
|
+
|
308
|
+
1. **Enable connection pooling:**
|
309
|
+
```ruby
|
310
|
+
require 'faraday/net_http_persistent'
|
311
|
+
|
312
|
+
client = A2A::Client::HttpClient.new(url) do |conn|
|
313
|
+
conn.adapter :net_http_persistent
|
314
|
+
end
|
315
|
+
```
|
316
|
+
|
317
|
+
2. **Use streaming for large responses:**
|
318
|
+
```ruby
|
319
|
+
# Instead of loading everything into memory
|
320
|
+
client.send_message(message, streaming: true) do |chunk|
|
321
|
+
process_chunk(chunk) # Process incrementally
|
322
|
+
end
|
323
|
+
```
|
324
|
+
|
325
|
+
3. **Monitor performance:**
|
326
|
+
```ruby
|
327
|
+
A2A.configure do |config|
|
328
|
+
config.enable_metrics = true
|
329
|
+
config.log_level = :info
|
330
|
+
end
|
331
|
+
|
332
|
+
# Add custom timing
|
333
|
+
start_time = Time.current
|
334
|
+
response = client.send_message(message)
|
335
|
+
duration = Time.current - start_time
|
336
|
+
puts "Request took #{duration}s"
|
337
|
+
```
|
338
|
+
|
339
|
+
### Memory Leaks
|
340
|
+
|
341
|
+
**Symptoms:**
|
342
|
+
- Increasing memory usage over time
|
343
|
+
- Out of memory errors
|
344
|
+
|
345
|
+
**Solutions:**
|
346
|
+
|
347
|
+
1. **Properly close streaming connections:**
|
348
|
+
```ruby
|
349
|
+
enumerator = client.send_message(message)
|
350
|
+
begin
|
351
|
+
enumerator.each do |response|
|
352
|
+
process_response(response)
|
353
|
+
end
|
354
|
+
ensure
|
355
|
+
enumerator.close if enumerator.respond_to?(:close)
|
356
|
+
end
|
357
|
+
```
|
358
|
+
|
359
|
+
2. **Limit message history:**
|
360
|
+
```ruby
|
361
|
+
# When getting tasks, limit history
|
362
|
+
task = client.get_task(task_id, history_length: 10)
|
363
|
+
```
|
364
|
+
|
365
|
+
3. **Use object pooling for frequent operations:**
|
366
|
+
```ruby
|
367
|
+
class MessagePool
|
368
|
+
def initialize
|
369
|
+
@pool = []
|
370
|
+
end
|
371
|
+
|
372
|
+
def get_message
|
373
|
+
@pool.pop || A2A::Types::Message.allocate
|
374
|
+
end
|
375
|
+
|
376
|
+
def return_message(message)
|
377
|
+
message.reset!
|
378
|
+
@pool.push(message) if @pool.size < 100
|
379
|
+
end
|
380
|
+
end
|
381
|
+
```
|
382
|
+
|
383
|
+
## Rails Integration Issues
|
384
|
+
|
385
|
+
### Routes Not Working
|
386
|
+
|
387
|
+
**Symptoms:**
|
388
|
+
- 404 errors for A2A endpoints
|
389
|
+
- Routes not appearing in `rails routes`
|
390
|
+
|
391
|
+
**Solutions:**
|
392
|
+
|
393
|
+
1. **Verify engine mounting:**
|
394
|
+
```ruby
|
395
|
+
# config/routes.rb
|
396
|
+
Rails.application.routes.draw do
|
397
|
+
mount A2A::Engine => "/a2a"
|
398
|
+
end
|
399
|
+
```
|
400
|
+
|
401
|
+
2. **Check route generation:**
|
402
|
+
```bash
|
403
|
+
rails routes | grep a2a
|
404
|
+
# Should show:
|
405
|
+
# POST /a2a/rpc
|
406
|
+
# GET /a2a/agent-card
|
407
|
+
# GET /a2a/capabilities
|
408
|
+
```
|
409
|
+
|
410
|
+
3. **Verify controller inclusion:**
|
411
|
+
```ruby
|
412
|
+
class MyAgentController < ApplicationController
|
413
|
+
include A2A::Rails::ControllerHelpers # Required
|
414
|
+
|
415
|
+
# Your A2A methods here
|
416
|
+
end
|
417
|
+
```
|
418
|
+
|
419
|
+
### Database Issues
|
420
|
+
|
421
|
+
**Symptoms:**
|
422
|
+
- Task storage errors
|
423
|
+
- Migration failures
|
424
|
+
|
425
|
+
**Solutions:**
|
426
|
+
|
427
|
+
1. **Run A2A migrations:**
|
428
|
+
```bash
|
429
|
+
rails generate a2a:migration
|
430
|
+
rails db:migrate
|
431
|
+
```
|
432
|
+
|
433
|
+
2. **Check database configuration:**
|
434
|
+
```ruby
|
435
|
+
# config/initializers/a2a.rb
|
436
|
+
A2A.configure do |config|
|
437
|
+
config.storage_backend = :database # or :memory, :redis
|
438
|
+
config.database_url = ENV['DATABASE_URL']
|
439
|
+
end
|
440
|
+
```
|
441
|
+
|
442
|
+
3. **Verify model associations:**
|
443
|
+
```ruby
|
444
|
+
# Check if models are properly loaded
|
445
|
+
A2A::Server::Models::Task.first
|
446
|
+
A2A::Server::Models::PushNotificationConfig.first
|
447
|
+
```
|
448
|
+
|
449
|
+
## Debugging Tools
|
450
|
+
|
451
|
+
### Enable Debug Logging
|
452
|
+
|
453
|
+
```ruby
|
454
|
+
A2A.configure do |config|
|
455
|
+
config.log_level = :debug
|
456
|
+
config.log_requests = true
|
457
|
+
config.log_responses = true
|
458
|
+
end
|
459
|
+
```
|
460
|
+
|
461
|
+
### Use the Console
|
462
|
+
|
463
|
+
```bash
|
464
|
+
# Rails console
|
465
|
+
rails console
|
466
|
+
|
467
|
+
# Gem console
|
468
|
+
cd a2a-ruby && bin/console
|
469
|
+
```
|
470
|
+
|
471
|
+
```ruby
|
472
|
+
# Test basic functionality
|
473
|
+
client = A2A::Client::HttpClient.new("http://localhost:3000/a2a")
|
474
|
+
card = client.get_card
|
475
|
+
puts JSON.pretty_generate(card.to_h)
|
476
|
+
```
|
477
|
+
|
478
|
+
### Network Debugging
|
479
|
+
|
480
|
+
```ruby
|
481
|
+
# Add request/response logging
|
482
|
+
require 'faraday/logging'
|
483
|
+
|
484
|
+
client = A2A::Client::HttpClient.new(url) do |conn|
|
485
|
+
conn.response :logger, Rails.logger, bodies: true
|
486
|
+
end
|
487
|
+
```
|
488
|
+
|
489
|
+
### Performance Profiling
|
490
|
+
|
491
|
+
```ruby
|
492
|
+
require 'ruby-prof'
|
493
|
+
|
494
|
+
RubyProf.start
|
495
|
+
client.send_message(message)
|
496
|
+
result = RubyProf.stop
|
497
|
+
|
498
|
+
# Print results
|
499
|
+
printer = RubyProf::FlatPrinter.new(result)
|
500
|
+
printer.print(STDOUT)
|
501
|
+
```
|
502
|
+
|
503
|
+
## Error Hierarchy
|
504
|
+
|
505
|
+
The A2A Ruby SDK provides a comprehensive error hierarchy:
|
506
|
+
|
507
|
+
```
|
508
|
+
StandardError
|
509
|
+
└── A2A::Errors::A2AError
|
510
|
+
├── JSON-RPC Standard Errors
|
511
|
+
│ ├── A2A::Errors::ParseError (-32700)
|
512
|
+
│ ├── A2A::Errors::InvalidRequest (-32600)
|
513
|
+
│ ├── A2A::Errors::MethodNotFound (-32601)
|
514
|
+
│ ├── A2A::Errors::InvalidParams (-32602)
|
515
|
+
│ └── A2A::Errors::InternalError (-32603)
|
516
|
+
├── A2A Protocol Errors
|
517
|
+
│ ├── A2A::Errors::TaskNotFound (-32001)
|
518
|
+
│ ├── A2A::Errors::TaskNotCancelable (-32002)
|
519
|
+
│ └── A2A::Errors::AuthenticationRequired (-32003)
|
520
|
+
├── Transport Errors
|
521
|
+
│ ├── A2A::Errors::HTTPError
|
522
|
+
│ ├── A2A::Errors::TimeoutError
|
523
|
+
│ └── A2A::Errors::ConnectionError
|
524
|
+
└── Configuration Errors
|
525
|
+
└── A2A::Errors::ConfigurationError
|
526
|
+
```
|
527
|
+
|
528
|
+
## Error Handling Patterns
|
529
|
+
|
530
|
+
### Client-Side Error Handling
|
531
|
+
|
532
|
+
```ruby
|
533
|
+
begin
|
534
|
+
response = client.send_message(message)
|
535
|
+
rescue A2A::Errors::MethodNotFound => e
|
536
|
+
puts "Method not supported: #{e.message}"
|
537
|
+
rescue A2A::Errors::AuthenticationRequired => e
|
538
|
+
puts "Authentication needed: #{e.message}"
|
539
|
+
rescue A2A::Errors::HTTPError => e
|
540
|
+
puts "HTTP error: #{e.message}"
|
541
|
+
rescue A2A::Errors::A2AError => e
|
542
|
+
puts "A2A error: #{e.message}"
|
543
|
+
end
|
544
|
+
```
|
545
|
+
|
546
|
+
### Server-Side Error Handling
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
a2a_method "risky_operation" do |params|
|
550
|
+
begin
|
551
|
+
# Your operation
|
552
|
+
{ success: true }
|
553
|
+
rescue ArgumentError => e
|
554
|
+
raise A2A::Errors::InvalidParams, "Invalid parameters: #{e.message}"
|
555
|
+
rescue StandardError => e
|
556
|
+
raise A2A::Errors::InternalError, "Operation failed: #{e.message}"
|
557
|
+
end
|
558
|
+
end
|
559
|
+
```
|
560
|
+
|
561
|
+
## Common Error Codes
|
562
|
+
|
563
|
+
### JSON-RPC Standard Errors
|
564
|
+
|
565
|
+
| Code | Error | Description |
|
566
|
+
|------|-------|-------------|
|
567
|
+
| -32700 | Parse error | Invalid JSON |
|
568
|
+
| -32600 | Invalid Request | Invalid JSON-RPC format |
|
569
|
+
| -32601 | Method not found | Method doesn't exist |
|
570
|
+
| -32602 | Invalid params | Invalid method parameters |
|
571
|
+
| -32603 | Internal error | Server internal error |
|
572
|
+
|
573
|
+
### A2A-Specific Errors
|
574
|
+
|
575
|
+
| Code | Error | Description |
|
576
|
+
|------|-------|-------------|
|
577
|
+
| -32001 | Task not found | Task ID doesn't exist |
|
578
|
+
| -32002 | Task not cancelable | Task in non-cancelable state |
|
579
|
+
| -32003 | Invalid task state | Invalid state transition |
|
580
|
+
| -32004 | Authentication required | Auth needed for operation |
|
581
|
+
| -32005 | Insufficient permissions | User lacks required permissions |
|
582
|
+
| -32006 | Rate limit exceeded | Too many requests |
|
583
|
+
| -32007 | Invalid agent card | Agent card validation failed |
|
584
|
+
| -32008 | Transport not supported | Requested transport unavailable |
|
585
|
+
| -32009 | Invalid message format | Message doesn't match schema |
|
586
|
+
| -32010 | Service unavailable | Temporary service outage |
|
587
|
+
|
588
|
+
## Frequently Asked Questions
|
589
|
+
|
590
|
+
### What Ruby versions are supported?
|
591
|
+
|
592
|
+
- Ruby 2.7 or higher
|
593
|
+
- Rails 6.0+ (for Rails integration)
|
594
|
+
- JRuby and TruffleRuby compatibility
|
595
|
+
|
596
|
+
### Do I need Rails to use A2A Ruby SDK?
|
597
|
+
|
598
|
+
No! The SDK works with any Ruby application:
|
599
|
+
- Plain Ruby scripts
|
600
|
+
- Sinatra applications
|
601
|
+
- Rack applications
|
602
|
+
- Rails applications (with enhanced integration)
|
603
|
+
|
604
|
+
### How do I connect to an A2A agent?
|
605
|
+
|
606
|
+
```ruby
|
607
|
+
require 'a2a'
|
608
|
+
|
609
|
+
client = A2A::Client::HttpClient.new("https://agent.example.com/a2a")
|
610
|
+
|
611
|
+
message = A2A::Types::Message.new(
|
612
|
+
message_id: SecureRandom.uuid,
|
613
|
+
role: "user",
|
614
|
+
parts: [A2A::Types::TextPart.new(text: "Hello!")]
|
615
|
+
)
|
616
|
+
|
617
|
+
client.send_message(message) do |response|
|
618
|
+
puts response
|
619
|
+
end
|
620
|
+
```
|
621
|
+
|
622
|
+
### How do I handle authentication?
|
623
|
+
|
624
|
+
```ruby
|
625
|
+
# OAuth 2.0
|
626
|
+
client = A2A::Client::HttpClient.new("https://agent.example.com/a2a") do |config|
|
627
|
+
config.auth_strategy = :oauth2
|
628
|
+
config.oauth2_token = "your_access_token"
|
629
|
+
end
|
630
|
+
|
631
|
+
# API Key
|
632
|
+
client = A2A::Client::HttpClient.new("https://agent.example.com/a2a") do |config|
|
633
|
+
config.auth_strategy = :api_key
|
634
|
+
config.api_key = "your_api_key"
|
635
|
+
end
|
636
|
+
```
|
637
|
+
|
638
|
+
### How do I create a simple agent?
|
639
|
+
|
640
|
+
```ruby
|
641
|
+
class MyAgent
|
642
|
+
include A2A::Server::Agent
|
643
|
+
|
644
|
+
a2a_method "greet" do |params|
|
645
|
+
{ message: "Hello, #{params[:name] || 'there'}!" }
|
646
|
+
end
|
647
|
+
end
|
648
|
+
```
|
649
|
+
|
650
|
+
### Q: Why am I getting SSL certificate errors?
|
651
|
+
|
652
|
+
A: For development with self-signed certificates:
|
653
|
+
|
654
|
+
```ruby
|
655
|
+
# Disable SSL verification (development only!)
|
656
|
+
client = A2A::Client::HttpClient.new(url) do |conn|
|
657
|
+
conn.ssl.verify = false
|
658
|
+
end
|
659
|
+
|
660
|
+
# Or set certificate bundle
|
661
|
+
client = A2A::Client::HttpClient.new(url) do |conn|
|
662
|
+
conn.ssl.ca_file = '/path/to/ca-bundle.crt'
|
663
|
+
end
|
664
|
+
```
|
665
|
+
|
666
|
+
### Q: How do I handle network interruptions in streaming?
|
667
|
+
|
668
|
+
A: Implement reconnection logic:
|
669
|
+
|
670
|
+
```ruby
|
671
|
+
def stream_with_reconnect(client, message, max_retries: 3)
|
672
|
+
retries = 0
|
673
|
+
|
674
|
+
begin
|
675
|
+
client.send_message(message) do |response|
|
676
|
+
yield response
|
677
|
+
end
|
678
|
+
rescue A2A::Errors::HTTPError, A2A::Errors::TimeoutError => e
|
679
|
+
retries += 1
|
680
|
+
if retries <= max_retries
|
681
|
+
sleep(2 ** retries) # Exponential backoff
|
682
|
+
retry
|
683
|
+
else
|
684
|
+
raise e
|
685
|
+
end
|
686
|
+
end
|
687
|
+
end
|
688
|
+
```
|
689
|
+
|
690
|
+
### Q: How do I test A2A agents in my test suite?
|
691
|
+
|
692
|
+
A: Use test doubles and helpers:
|
693
|
+
|
694
|
+
```ruby
|
695
|
+
# spec/support/a2a_helpers.rb
|
696
|
+
RSpec.configure do |config|
|
697
|
+
config.include A2AHelpers
|
698
|
+
end
|
699
|
+
|
700
|
+
# In your tests
|
701
|
+
it "handles A2A messages" do
|
702
|
+
message = build_a2a_message(text: "test")
|
703
|
+
client = mock_a2a_client(send_message: mock_response)
|
704
|
+
|
705
|
+
result = client.send_message(message)
|
706
|
+
expect(result).to be_a(A2A::Types::Message)
|
707
|
+
end
|
708
|
+
```
|
709
|
+
|
710
|
+
### Q: Can I use A2A with other Ruby web frameworks?
|
711
|
+
|
712
|
+
A: Yes! The core SDK works with any Ruby application:
|
713
|
+
|
714
|
+
```ruby
|
715
|
+
# Sinatra example
|
716
|
+
require 'sinatra'
|
717
|
+
require 'a2a'
|
718
|
+
|
719
|
+
class MyAgent
|
720
|
+
include A2A::Server::Agent
|
721
|
+
# Define your methods
|
722
|
+
end
|
723
|
+
|
724
|
+
post '/a2a/rpc' do
|
725
|
+
agent = MyAgent.new
|
726
|
+
request_body = request.body.read
|
727
|
+
|
728
|
+
json_rpc_request = A2A::Protocol::JsonRpc.parse_request(request_body)
|
729
|
+
response = agent.handle_a2a_request(json_rpc_request)
|
730
|
+
|
731
|
+
content_type :json
|
732
|
+
response.to_json
|
733
|
+
end
|
734
|
+
```
|
735
|
+
|
736
|
+
### Q: How do I implement custom middleware?
|
737
|
+
|
738
|
+
A: Create middleware classes:
|
739
|
+
|
740
|
+
```ruby
|
741
|
+
class CustomLoggingMiddleware
|
742
|
+
def initialize(app)
|
743
|
+
@app = app
|
744
|
+
end
|
745
|
+
|
746
|
+
def call(request)
|
747
|
+
start_time = Time.current
|
748
|
+
|
749
|
+
response = @app.call(request)
|
750
|
+
|
751
|
+
duration = Time.current - start_time
|
752
|
+
Rails.logger.info "A2A Request: #{request.method} (#{duration}s)"
|
753
|
+
|
754
|
+
response
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
758
|
+
# Add to client
|
759
|
+
client.add_middleware(CustomLoggingMiddleware)
|
760
|
+
```
|
761
|
+
|
762
|
+
For more help:
|
763
|
+
- [GitHub Issues](https://github.com/a2aproject/a2a-ruby/issues)
|
764
|
+
- [Discussions](https://github.com/a2aproject/a2a-ruby/discussions)
|
765
|
+
- [API Documentation](https://rubydoc.info/gems/a2a-ruby)
|