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,668 @@
|
|
1
|
+
# Getting Started with A2A Ruby SDK
|
2
|
+
|
3
|
+
Welcome to the A2A Ruby SDK! This guide will help you get up and running with Google's Agent2Agent (A2A) Protocol in your Ruby applications.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
|
7
|
+
- [Installation](#installation)
|
8
|
+
- [Basic Concepts](#basic-concepts)
|
9
|
+
- [Your First A2A Client](#your-first-a2a-client)
|
10
|
+
- [Your First A2A Server](#your-first-a2a-server)
|
11
|
+
- [Rails Integration](#rails-integration)
|
12
|
+
- [Authentication](#authentication)
|
13
|
+
- [Task Management](#task-management)
|
14
|
+
- [Troubleshooting](#troubleshooting)
|
15
|
+
- [Next Steps](#next-steps)
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
### Requirements
|
20
|
+
|
21
|
+
- Ruby 2.7 or higher
|
22
|
+
- Bundler
|
23
|
+
|
24
|
+
### Install the Gem
|
25
|
+
|
26
|
+
Add to your Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'a2a-ruby'
|
30
|
+
```
|
31
|
+
|
32
|
+
Then run:
|
33
|
+
|
34
|
+
```bash
|
35
|
+
bundle install
|
36
|
+
```
|
37
|
+
|
38
|
+
Or install directly:
|
39
|
+
|
40
|
+
```bash
|
41
|
+
gem install a2a-ruby
|
42
|
+
```
|
43
|
+
|
44
|
+
### Verify Installation
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
require 'a2a'
|
48
|
+
puts A2A::VERSION
|
49
|
+
```
|
50
|
+
|
51
|
+
## Basic Concepts
|
52
|
+
|
53
|
+
### Agent2Agent Protocol
|
54
|
+
|
55
|
+
The A2A protocol enables agents to communicate with each other using standardized message formats and transport protocols. Key concepts include:
|
56
|
+
|
57
|
+
- **Messages**: Structured communication between agents
|
58
|
+
- **Tasks**: Long-running operations with lifecycle management
|
59
|
+
- **Agent Cards**: Self-describing agent capabilities
|
60
|
+
- **Transports**: Communication protocols (JSON-RPC, gRPC, HTTP+JSON)
|
61
|
+
|
62
|
+
### Core Components
|
63
|
+
|
64
|
+
- **Client**: Consumes services from other agents
|
65
|
+
- **Server**: Exposes your application as an A2A agent
|
66
|
+
- **Types**: Protocol-compliant data structures
|
67
|
+
- **Transport**: Communication layer (HTTP, gRPC, SSE)
|
68
|
+
|
69
|
+
## Your First A2A Client
|
70
|
+
|
71
|
+
Let's create a simple client to communicate with an A2A agent:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
require 'a2a'
|
75
|
+
|
76
|
+
# Create a client pointing to an A2A agent
|
77
|
+
client = A2A::Client::HttpClient.new("https://example-agent.com/a2a")
|
78
|
+
|
79
|
+
# Create a message
|
80
|
+
message = A2A::Types::Message.new(
|
81
|
+
message_id: SecureRandom.uuid,
|
82
|
+
role: "user",
|
83
|
+
parts: [
|
84
|
+
A2A::Types::TextPart.new(text: "What's the weather like today?")
|
85
|
+
]
|
86
|
+
)
|
87
|
+
|
88
|
+
# Send the message and handle responses
|
89
|
+
begin
|
90
|
+
client.send_message(message) do |response|
|
91
|
+
case response
|
92
|
+
when A2A::Types::Message
|
93
|
+
puts "Agent: #{response.parts.first.text}"
|
94
|
+
when A2A::Types::TaskStatusUpdateEvent
|
95
|
+
puts "Task Status: #{response.status.state}"
|
96
|
+
when A2A::Types::Task
|
97
|
+
puts "Task Created: #{response.id}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
rescue A2A::Errors::ClientError => e
|
101
|
+
puts "Error: #{e.message}"
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
### Client Configuration
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# Configure client behavior
|
109
|
+
config = A2A::Client::Config.new
|
110
|
+
config.streaming = true
|
111
|
+
config.timeout = 60
|
112
|
+
config.supported_transports = ['JSONRPC', 'HTTP+JSON']
|
113
|
+
|
114
|
+
client = A2A::Client::HttpClient.new(
|
115
|
+
"https://example-agent.com/a2a",
|
116
|
+
config: config
|
117
|
+
)
|
118
|
+
```
|
119
|
+
|
120
|
+
### Working with Tasks
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
# Get task status
|
124
|
+
task = client.get_task("task-123")
|
125
|
+
puts "Task state: #{task.status.state}"
|
126
|
+
|
127
|
+
# Cancel a task
|
128
|
+
client.cancel_task("task-123")
|
129
|
+
|
130
|
+
# Resubscribe to task updates
|
131
|
+
client.resubscribe("task-123") do |event|
|
132
|
+
puts "Task update: #{event.status.state}"
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
## Your First A2A Server
|
137
|
+
|
138
|
+
Create an A2A server to expose your application's capabilities:
|
139
|
+
|
140
|
+
### Plain Ruby Server
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
require 'a2a'
|
144
|
+
require 'sinatra'
|
145
|
+
|
146
|
+
class WeatherAgent
|
147
|
+
include A2A::Server::Agent
|
148
|
+
|
149
|
+
# Define agent skills
|
150
|
+
a2a_skill "weather_lookup" do |skill|
|
151
|
+
skill.description = "Get current weather information"
|
152
|
+
skill.tags = ["weather", "information"]
|
153
|
+
skill.examples = ["What's the weather in San Francisco?"]
|
154
|
+
skill.input_modes = ["text"]
|
155
|
+
skill.output_modes = ["text"]
|
156
|
+
end
|
157
|
+
|
158
|
+
# Define A2A methods
|
159
|
+
a2a_method "get_weather" do |params|
|
160
|
+
location = params[:location] || "Unknown"
|
161
|
+
|
162
|
+
# Simulate weather lookup
|
163
|
+
{
|
164
|
+
location: location,
|
165
|
+
temperature: "72°F",
|
166
|
+
condition: "Sunny",
|
167
|
+
timestamp: Time.current.iso8601
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
# Streaming method example
|
172
|
+
a2a_method "weather_forecast", streaming: true do |params|
|
173
|
+
Enumerator.new do |yielder|
|
174
|
+
# Yield status updates
|
175
|
+
yielder << A2A::Types::TaskStatusUpdateEvent.new(
|
176
|
+
task_id: params[:task_id],
|
177
|
+
context_id: params[:context_id],
|
178
|
+
status: A2A::Types::TaskStatus.new(state: "working")
|
179
|
+
)
|
180
|
+
|
181
|
+
# Generate forecast data
|
182
|
+
5.times do |day|
|
183
|
+
forecast = {
|
184
|
+
day: day + 1,
|
185
|
+
temperature: "#{70 + rand(10)}°F",
|
186
|
+
condition: ["Sunny", "Cloudy", "Rainy"].sample
|
187
|
+
}
|
188
|
+
|
189
|
+
yielder << A2A::Types::Message.new(
|
190
|
+
message_id: SecureRandom.uuid,
|
191
|
+
role: "agent",
|
192
|
+
parts: [A2A::Types::TextPart.new(text: forecast.to_json)]
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Final status
|
197
|
+
yielder << A2A::Types::TaskStatusUpdateEvent.new(
|
198
|
+
task_id: params[:task_id],
|
199
|
+
context_id: params[:context_id],
|
200
|
+
status: A2A::Types::TaskStatus.new(state: "completed")
|
201
|
+
)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Sinatra integration
|
207
|
+
post '/a2a/rpc' do
|
208
|
+
content_type :json
|
209
|
+
|
210
|
+
agent = WeatherAgent.new
|
211
|
+
request_body = request.body.read
|
212
|
+
|
213
|
+
begin
|
214
|
+
json_rpc_request = A2A::Protocol::JsonRpc.parse_request(request_body)
|
215
|
+
response = agent.handle_a2a_request(json_rpc_request)
|
216
|
+
response.to_json
|
217
|
+
rescue A2A::Errors::A2AError => e
|
218
|
+
status 400
|
219
|
+
e.to_json_rpc_error.to_json
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
get '/a2a/agent-card' do
|
224
|
+
content_type :json
|
225
|
+
|
226
|
+
agent = WeatherAgent.new
|
227
|
+
card = agent.generate_agent_card(
|
228
|
+
name: "Weather Agent",
|
229
|
+
description: "Provides weather information and forecasts",
|
230
|
+
version: "1.0.0",
|
231
|
+
url: "#{request.base_url}/a2a"
|
232
|
+
)
|
233
|
+
|
234
|
+
card.to_h.to_json
|
235
|
+
end
|
236
|
+
```
|
237
|
+
|
238
|
+
## Rails Integration
|
239
|
+
|
240
|
+
The A2A Ruby SDK provides seamless Rails integration through an engine.
|
241
|
+
|
242
|
+
### Setup
|
243
|
+
|
244
|
+
Generate the A2A configuration:
|
245
|
+
|
246
|
+
```bash
|
247
|
+
rails generate a2a:install
|
248
|
+
```
|
249
|
+
|
250
|
+
This creates:
|
251
|
+
- `config/initializers/a2a.rb` - Configuration file
|
252
|
+
- Routes for A2A endpoints
|
253
|
+
- Database migrations (if using ActiveRecord storage)
|
254
|
+
|
255
|
+
### Create an Agent Controller
|
256
|
+
|
257
|
+
```bash
|
258
|
+
rails generate a2a:agent weather
|
259
|
+
```
|
260
|
+
|
261
|
+
This generates:
|
262
|
+
- `app/controllers/weather_agent_controller.rb`
|
263
|
+
- Spec file
|
264
|
+
- README with usage instructions
|
265
|
+
|
266
|
+
### Example Agent Controller
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
class WeatherAgentController < ApplicationController
|
270
|
+
include A2A::Rails::ControllerHelpers
|
271
|
+
|
272
|
+
# Define agent metadata
|
273
|
+
a2a_config(
|
274
|
+
name: "Weather Service Agent",
|
275
|
+
description: "Provides weather information and forecasts",
|
276
|
+
version: "1.0.0"
|
277
|
+
)
|
278
|
+
|
279
|
+
# Define skills
|
280
|
+
a2a_skill "weather_lookup" do |skill|
|
281
|
+
skill.description = "Get current weather for any location"
|
282
|
+
skill.tags = ["weather", "current", "lookup"]
|
283
|
+
skill.examples = [
|
284
|
+
"What's the weather in New York?",
|
285
|
+
"Current conditions in Tokyo"
|
286
|
+
]
|
287
|
+
end
|
288
|
+
|
289
|
+
# A2A method implementations
|
290
|
+
a2a_method "get_current_weather" do |params|
|
291
|
+
location = params[:location]
|
292
|
+
|
293
|
+
# Your weather service logic here
|
294
|
+
weather_data = WeatherService.current(location)
|
295
|
+
|
296
|
+
{
|
297
|
+
location: location,
|
298
|
+
temperature: weather_data.temperature,
|
299
|
+
condition: weather_data.condition,
|
300
|
+
humidity: weather_data.humidity,
|
301
|
+
timestamp: Time.current.iso8601
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
a2a_method "get_forecast", streaming: true do |params|
|
306
|
+
location = params[:location]
|
307
|
+
days = params[:days] || 5
|
308
|
+
|
309
|
+
Enumerator.new do |yielder|
|
310
|
+
# Initial status
|
311
|
+
yielder << task_status_update("working", "Fetching forecast data...")
|
312
|
+
|
313
|
+
# Get forecast data
|
314
|
+
forecast = WeatherService.forecast(location, days)
|
315
|
+
|
316
|
+
forecast.each_with_index do |day_forecast, index|
|
317
|
+
# Yield each day's forecast
|
318
|
+
message = A2A::Types::Message.new(
|
319
|
+
message_id: SecureRandom.uuid,
|
320
|
+
role: "agent",
|
321
|
+
parts: [
|
322
|
+
A2A::Types::TextPart.new(
|
323
|
+
text: "Day #{index + 1}: #{day_forecast.condition}, #{day_forecast.temperature}"
|
324
|
+
)
|
325
|
+
]
|
326
|
+
)
|
327
|
+
yielder << message
|
328
|
+
|
329
|
+
# Progress update
|
330
|
+
progress = ((index + 1).to_f / days * 100).round
|
331
|
+
yielder << task_status_update("working", "Progress: #{progress}%", progress)
|
332
|
+
end
|
333
|
+
|
334
|
+
# Completion
|
335
|
+
yielder << task_status_update("completed", "Forecast complete")
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
private
|
340
|
+
|
341
|
+
def task_status_update(state, message = nil, progress = nil)
|
342
|
+
A2A::Types::TaskStatusUpdateEvent.new(
|
343
|
+
task_id: params[:task_id],
|
344
|
+
context_id: params[:context_id],
|
345
|
+
status: A2A::Types::TaskStatus.new(
|
346
|
+
state: state,
|
347
|
+
message: message,
|
348
|
+
progress: progress
|
349
|
+
)
|
350
|
+
)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
```
|
354
|
+
|
355
|
+
### Routes
|
356
|
+
|
357
|
+
The Rails engine automatically provides these routes:
|
358
|
+
|
359
|
+
```ruby
|
360
|
+
# config/routes.rb
|
361
|
+
Rails.application.routes.draw do
|
362
|
+
mount A2A::Engine => "/a2a"
|
363
|
+
|
364
|
+
# This provides:
|
365
|
+
# POST /a2a/rpc - JSON-RPC endpoint
|
366
|
+
# GET /a2a/agent-card - Agent card discovery
|
367
|
+
# GET /a2a/capabilities - Capabilities listing
|
368
|
+
end
|
369
|
+
```
|
370
|
+
|
371
|
+
## Authentication
|
372
|
+
|
373
|
+
### Client Authentication
|
374
|
+
|
375
|
+
```ruby
|
376
|
+
# OAuth 2.0
|
377
|
+
auth = A2A::Client::Auth::OAuth2.new(
|
378
|
+
client_id: "your-client-id",
|
379
|
+
client_secret: "your-client-secret",
|
380
|
+
token_url: "https://auth.example.com/token"
|
381
|
+
)
|
382
|
+
|
383
|
+
client = A2A::Client::HttpClient.new(
|
384
|
+
"https://agent.example.com/a2a",
|
385
|
+
auth: auth
|
386
|
+
)
|
387
|
+
|
388
|
+
# JWT Bearer Token
|
389
|
+
auth = A2A::Client::Auth::JWT.new(token: "your-jwt-token")
|
390
|
+
|
391
|
+
# API Key
|
392
|
+
auth = A2A::Client::Auth::ApiKey.new(
|
393
|
+
key: "your-api-key",
|
394
|
+
header: "X-API-Key" # or use query parameter
|
395
|
+
)
|
396
|
+
```
|
397
|
+
|
398
|
+
### Server Authentication
|
399
|
+
|
400
|
+
```ruby
|
401
|
+
class SecureAgentController < ApplicationController
|
402
|
+
include A2A::Rails::ControllerHelpers
|
403
|
+
|
404
|
+
# Configure authentication
|
405
|
+
before_action :authenticate_a2a_request
|
406
|
+
|
407
|
+
private
|
408
|
+
|
409
|
+
def authenticate_a2a_request
|
410
|
+
# JWT validation example
|
411
|
+
token = request.headers['Authorization']&.sub(/^Bearer /, '')
|
412
|
+
|
413
|
+
begin
|
414
|
+
payload = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256')
|
415
|
+
@current_user = User.find(payload[0]['user_id'])
|
416
|
+
rescue JWT::DecodeError
|
417
|
+
render json: { error: 'Invalid token' }, status: :unauthorized
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
```
|
422
|
+
|
423
|
+
## Task Management
|
424
|
+
|
425
|
+
### Creating and Managing Tasks
|
426
|
+
|
427
|
+
```ruby
|
428
|
+
# In your agent method
|
429
|
+
a2a_method "long_running_task" do |params|
|
430
|
+
# Create a task
|
431
|
+
task = create_task(
|
432
|
+
type: "data_processing",
|
433
|
+
params: params,
|
434
|
+
metadata: { user_id: current_user.id }
|
435
|
+
)
|
436
|
+
|
437
|
+
# Start background processing
|
438
|
+
ProcessDataJob.perform_later(task.id, params)
|
439
|
+
|
440
|
+
# Return task immediately
|
441
|
+
task
|
442
|
+
end
|
443
|
+
|
444
|
+
# Background job
|
445
|
+
class ProcessDataJob < ApplicationJob
|
446
|
+
def perform(task_id, params)
|
447
|
+
task_manager = A2A::Server::TaskManager.new
|
448
|
+
|
449
|
+
begin
|
450
|
+
# Update status
|
451
|
+
task_manager.update_task_status(task_id,
|
452
|
+
A2A::Types::TaskStatus.new(state: "working")
|
453
|
+
)
|
454
|
+
|
455
|
+
# Do work...
|
456
|
+
result = process_data(params)
|
457
|
+
|
458
|
+
# Complete task
|
459
|
+
task_manager.update_task_status(task_id,
|
460
|
+
A2A::Types::TaskStatus.new(
|
461
|
+
state: "completed",
|
462
|
+
result: result
|
463
|
+
)
|
464
|
+
)
|
465
|
+
rescue => e
|
466
|
+
# Handle errors
|
467
|
+
task_manager.update_task_status(task_id,
|
468
|
+
A2A::Types::TaskStatus.new(
|
469
|
+
state: "failed",
|
470
|
+
error: { message: e.message, type: e.class.name }
|
471
|
+
)
|
472
|
+
)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
```
|
477
|
+
|
478
|
+
### Push Notifications
|
479
|
+
|
480
|
+
```ruby
|
481
|
+
# Set up push notifications for a task
|
482
|
+
push_config = A2A::Types::PushNotificationConfig.new(
|
483
|
+
url: "https://your-app.com/webhooks/a2a",
|
484
|
+
authentication: {
|
485
|
+
type: "bearer",
|
486
|
+
token: "your-webhook-token"
|
487
|
+
}
|
488
|
+
)
|
489
|
+
|
490
|
+
client.set_task_callback(task_id, push_config)
|
491
|
+
|
492
|
+
# Webhook handler
|
493
|
+
post '/webhooks/a2a' do
|
494
|
+
# Verify authentication
|
495
|
+
token = request.headers['Authorization']&.sub(/^Bearer /, '')
|
496
|
+
halt 401 unless token == ENV['WEBHOOK_TOKEN']
|
497
|
+
|
498
|
+
# Parse event
|
499
|
+
event_data = JSON.parse(request.body.read)
|
500
|
+
|
501
|
+
case event_data['type']
|
502
|
+
when 'TaskStatusUpdateEvent'
|
503
|
+
handle_task_status_update(event_data)
|
504
|
+
when 'TaskArtifactUpdateEvent'
|
505
|
+
handle_task_artifact_update(event_data)
|
506
|
+
end
|
507
|
+
|
508
|
+
status 200
|
509
|
+
end
|
510
|
+
```
|
511
|
+
|
512
|
+
## Troubleshooting
|
513
|
+
|
514
|
+
### Common Issues
|
515
|
+
|
516
|
+
#### Connection Errors
|
517
|
+
|
518
|
+
```ruby
|
519
|
+
begin
|
520
|
+
client.send_message(message)
|
521
|
+
rescue A2A::Errors::HTTPError => e
|
522
|
+
puts "HTTP Error: #{e.message}"
|
523
|
+
# Check network connectivity and endpoint URL
|
524
|
+
rescue A2A::Errors::TimeoutError => e
|
525
|
+
puts "Timeout: #{e.message}"
|
526
|
+
# Increase timeout or check server performance
|
527
|
+
end
|
528
|
+
```
|
529
|
+
|
530
|
+
#### Authentication Failures
|
531
|
+
|
532
|
+
```ruby
|
533
|
+
begin
|
534
|
+
client.send_message(message)
|
535
|
+
rescue A2A::Errors::AuthenticationError => e
|
536
|
+
puts "Auth Error: #{e.message}"
|
537
|
+
# Check credentials and token expiration
|
538
|
+
end
|
539
|
+
```
|
540
|
+
|
541
|
+
#### Protocol Errors
|
542
|
+
|
543
|
+
```ruby
|
544
|
+
begin
|
545
|
+
response = client.send_message(message)
|
546
|
+
rescue A2A::Errors::InvalidRequest => e
|
547
|
+
puts "Invalid Request: #{e.message}"
|
548
|
+
# Check message format and required fields
|
549
|
+
rescue A2A::Errors::MethodNotFound => e
|
550
|
+
puts "Method Not Found: #{e.message}"
|
551
|
+
# Check agent card for available methods
|
552
|
+
end
|
553
|
+
```
|
554
|
+
|
555
|
+
### Debugging
|
556
|
+
|
557
|
+
Enable debug logging:
|
558
|
+
|
559
|
+
```ruby
|
560
|
+
A2A.configure do |config|
|
561
|
+
config.log_level = :debug
|
562
|
+
end
|
563
|
+
```
|
564
|
+
|
565
|
+
Use the development console:
|
566
|
+
|
567
|
+
```bash
|
568
|
+
bin/console
|
569
|
+
```
|
570
|
+
|
571
|
+
```ruby
|
572
|
+
# Test agent card retrieval
|
573
|
+
client = A2A::Client::HttpClient.new("https://agent.example.com/a2a")
|
574
|
+
card = client.get_card
|
575
|
+
puts card.to_h.to_json
|
576
|
+
```
|
577
|
+
|
578
|
+
### Performance Issues
|
579
|
+
|
580
|
+
Monitor performance with built-in metrics:
|
581
|
+
|
582
|
+
```ruby
|
583
|
+
A2A.configure do |config|
|
584
|
+
config.enable_metrics = true
|
585
|
+
config.metrics_backend = :prometheus # or :statsd
|
586
|
+
end
|
587
|
+
```
|
588
|
+
|
589
|
+
### FAQ
|
590
|
+
|
591
|
+
**Q: How do I handle file uploads in messages?**
|
592
|
+
|
593
|
+
A: Use `FilePart` with base64 encoding or URI references:
|
594
|
+
|
595
|
+
```ruby
|
596
|
+
# Base64 file
|
597
|
+
file_part = A2A::Types::FilePart.new(
|
598
|
+
file: A2A::Types::FileWithBytes.new(
|
599
|
+
name: "document.pdf",
|
600
|
+
mime_type: "application/pdf",
|
601
|
+
bytes: Base64.encode64(file_content)
|
602
|
+
)
|
603
|
+
)
|
604
|
+
|
605
|
+
# URI reference
|
606
|
+
file_part = A2A::Types::FilePart.new(
|
607
|
+
file: A2A::Types::FileWithUri.new(
|
608
|
+
name: "document.pdf",
|
609
|
+
mime_type: "application/pdf",
|
610
|
+
uri: "https://storage.example.com/files/document.pdf"
|
611
|
+
)
|
612
|
+
)
|
613
|
+
```
|
614
|
+
|
615
|
+
**Q: How do I implement custom authentication?**
|
616
|
+
|
617
|
+
A: Create a custom auth strategy:
|
618
|
+
|
619
|
+
```ruby
|
620
|
+
class CustomAuth < A2A::Client::Auth::Base
|
621
|
+
def initialize(api_key)
|
622
|
+
@api_key = api_key
|
623
|
+
end
|
624
|
+
|
625
|
+
def apply_auth(request)
|
626
|
+
request.headers['X-Custom-Auth'] = @api_key
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
client = A2A::Client::HttpClient.new(url, auth: CustomAuth.new("key"))
|
631
|
+
```
|
632
|
+
|
633
|
+
**Q: Can I use multiple transports?**
|
634
|
+
|
635
|
+
A: Yes, configure transport preferences:
|
636
|
+
|
637
|
+
```ruby
|
638
|
+
config = A2A::Client::Config.new
|
639
|
+
config.supported_transports = ['JSONRPC', 'GRPC', 'HTTP+JSON']
|
640
|
+
config.use_client_preference = true
|
641
|
+
|
642
|
+
client = A2A::Client::HttpClient.new(url, config: config)
|
643
|
+
```
|
644
|
+
|
645
|
+
## Next Steps
|
646
|
+
|
647
|
+
Now that you have the basics, explore these advanced topics:
|
648
|
+
|
649
|
+
- [Client Documentation](client.md) - Advanced client configuration and usage
|
650
|
+
- [Server Documentation](server.md) - Building production-ready A2A servers
|
651
|
+
- [Rails Integration](rails.md) - Deep dive into Rails-specific features
|
652
|
+
- [Authentication Guide](authentication.md) - Comprehensive security setup
|
653
|
+
- [Deployment Guide](deployment.md) - Production deployment best practices
|
654
|
+
- [API Reference](https://rubydoc.info/gems/a2a-ruby) - Complete API documentation
|
655
|
+
|
656
|
+
### Example Applications
|
657
|
+
|
658
|
+
Check out complete example applications:
|
659
|
+
|
660
|
+
- [Weather Agent](https://github.com/a2aproject/a2a-ruby-examples/tree/main/weather-agent)
|
661
|
+
- [File Processing Service](https://github.com/a2aproject/a2a-ruby-examples/tree/main/file-processor)
|
662
|
+
- [Multi-Agent Chat](https://github.com/a2aproject/a2a-ruby-examples/tree/main/multi-agent-chat)
|
663
|
+
|
664
|
+
### Community
|
665
|
+
|
666
|
+
- [GitHub Discussions](https://github.com/a2aproject/a2a-ruby/discussions)
|
667
|
+
- [Issue Tracker](https://github.com/a2aproject/a2a-ruby/issues)
|
668
|
+
- [Contributing Guide](../CONTRIBUTING.md)
|