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,481 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "grpc"
|
5
|
+
rescue LoadError
|
6
|
+
# gRPC is optional - define a stub implementation
|
7
|
+
module GRPC
|
8
|
+
class BadStatus < StandardError; end
|
9
|
+
|
10
|
+
module Core
|
11
|
+
class StatusCodes
|
12
|
+
OK = 0
|
13
|
+
CANCELLED = 1
|
14
|
+
UNKNOWN = 2
|
15
|
+
INVALID_ARGUMENT = 3
|
16
|
+
DEADLINE_EXCEEDED = 4
|
17
|
+
NOT_FOUND = 5
|
18
|
+
ALREADY_EXISTS = 6
|
19
|
+
PERMISSION_DENIED = 7
|
20
|
+
RESOURCE_EXHAUSTED = 8
|
21
|
+
FAILED_PRECONDITION = 9
|
22
|
+
ABORTED = 10
|
23
|
+
OUT_OF_RANGE = 11
|
24
|
+
UNIMPLEMENTED = 12
|
25
|
+
INTERNAL = 13
|
26
|
+
UNAVAILABLE = 14
|
27
|
+
DATA_LOSS = 15
|
28
|
+
UNAUTHENTICATED = 16
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module A2A
|
35
|
+
module Transport
|
36
|
+
##
|
37
|
+
# gRPC transport implementation with optional dependency
|
38
|
+
# Provides bidirectional streaming support and gRPC-specific error mapping
|
39
|
+
#
|
40
|
+
class Grpc
|
41
|
+
# Check if gRPC is available
|
42
|
+
GRPC_AVAILABLE = defined?(::GRPC) && ::GRPC.const_defined?(:ClientStub)
|
43
|
+
|
44
|
+
# Default configuration values
|
45
|
+
DEFAULT_TIMEOUT = 30
|
46
|
+
DEFAULT_DEADLINE = 60
|
47
|
+
DEFAULT_MAX_RECEIVE_MESSAGE_SIZE = 4 * 1024 * 1024 # 4MB
|
48
|
+
DEFAULT_MAX_SEND_MESSAGE_SIZE = 4 * 1024 * 1024 # 4MB
|
49
|
+
DEFAULT_KEEPALIVE_TIME = 30
|
50
|
+
DEFAULT_KEEPALIVE_TIMEOUT = 5
|
51
|
+
|
52
|
+
attr_reader :endpoint, :config, :stub, :credentials
|
53
|
+
|
54
|
+
##
|
55
|
+
# Initialize gRPC transport
|
56
|
+
#
|
57
|
+
# @param endpoint [String] gRPC endpoint (host:port)
|
58
|
+
# @param config [Hash] Configuration options
|
59
|
+
# @option config [Integer] :timeout (30) Request timeout in seconds
|
60
|
+
# @option config [Integer] :deadline (60) Request deadline in seconds
|
61
|
+
# @option config [Integer] :max_receive_message_size (4MB) Max receive message size
|
62
|
+
# @option config [Integer] :max_send_message_size (4MB) Max send message size
|
63
|
+
# @option config [Integer] :keepalive_time (30) Keepalive time in seconds
|
64
|
+
# @option config [Integer] :keepalive_timeout (5) Keepalive timeout in seconds
|
65
|
+
# @option config [Boolean] :use_tls (true) Use TLS encryption
|
66
|
+
# @option config [String] :ca_file Path to CA certificate file
|
67
|
+
# @option config [String] :cert_file Path to client certificate file
|
68
|
+
# @option config [String] :key_file Path to client private key file
|
69
|
+
# @option config [Hash] :metadata ({}) Default metadata
|
70
|
+
# @option config [Object] :credentials Custom credentials object
|
71
|
+
#
|
72
|
+
def initialize(endpoint, config = {})
|
73
|
+
unless GRPC_AVAILABLE
|
74
|
+
raise A2A::Errors::TransportError,
|
75
|
+
"gRPC is not available. Install the 'grpc' gem to use gRPC transport."
|
76
|
+
end
|
77
|
+
|
78
|
+
@endpoint = endpoint
|
79
|
+
@config = default_config.merge(config)
|
80
|
+
@credentials = build_credentials
|
81
|
+
@stub = nil
|
82
|
+
@call_options = build_call_options
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Connect to gRPC service
|
87
|
+
#
|
88
|
+
# @return [Boolean] Connection success
|
89
|
+
# @raise [A2A::Errors::TransportError] On connection errors
|
90
|
+
#
|
91
|
+
def connect
|
92
|
+
return true if connected?
|
93
|
+
|
94
|
+
begin
|
95
|
+
@stub = build_stub
|
96
|
+
# Test connection with a simple call
|
97
|
+
@stub.class.rpc_descs.keys.first&.tap do |_method|
|
98
|
+
# This is a simplified connection test
|
99
|
+
end
|
100
|
+
true
|
101
|
+
rescue StandardError => e
|
102
|
+
raise A2A::Errors::TransportError, "Failed to connect to gRPC service: #{e.message}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Disconnect from gRPC service
|
108
|
+
#
|
109
|
+
def disconnect
|
110
|
+
@stub = nil
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Check if connected
|
115
|
+
#
|
116
|
+
# @return [Boolean] Connection status
|
117
|
+
#
|
118
|
+
def connected?
|
119
|
+
!@stub.nil?
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Send unary gRPC request
|
124
|
+
#
|
125
|
+
# @param method [Symbol] gRPC method name
|
126
|
+
# @param request [Object] Request message
|
127
|
+
# @param metadata [Hash] Request metadata
|
128
|
+
# @param timeout [Integer, nil] Request timeout
|
129
|
+
# @return [Object] Response message
|
130
|
+
# @raise [A2A::Errors::TransportError] On gRPC errors
|
131
|
+
#
|
132
|
+
def unary_call(method, request, metadata: {}, timeout: nil)
|
133
|
+
ensure_connected!
|
134
|
+
|
135
|
+
call_options = @call_options.dup
|
136
|
+
call_options[:timeout] = timeout if timeout
|
137
|
+
call_options[:metadata] = @config[:metadata].merge(metadata)
|
138
|
+
|
139
|
+
begin
|
140
|
+
@stub.public_send(method, request, call_options)
|
141
|
+
rescue ::GRPC::BadStatus => e
|
142
|
+
raise map_grpc_error(e)
|
143
|
+
rescue StandardError => e
|
144
|
+
raise A2A::Errors::TransportError, "gRPC call failed: #{e.message}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Send client streaming gRPC request
|
150
|
+
#
|
151
|
+
# @param method [Symbol] gRPC method name
|
152
|
+
# @param requests [Enumerator] Request stream
|
153
|
+
# @param metadata [Hash] Request metadata
|
154
|
+
# @param timeout [Integer, nil] Request timeout
|
155
|
+
# @return [Object] Response message
|
156
|
+
# @raise [A2A::Errors::TransportError] On gRPC errors
|
157
|
+
#
|
158
|
+
def client_streaming_call(method, requests, metadata: {}, timeout: nil)
|
159
|
+
ensure_connected!
|
160
|
+
|
161
|
+
call_options = @call_options.dup
|
162
|
+
call_options[:timeout] = timeout if timeout
|
163
|
+
call_options[:metadata] = @config[:metadata].merge(metadata)
|
164
|
+
|
165
|
+
begin
|
166
|
+
@stub.public_send(method, requests, call_options)
|
167
|
+
rescue ::GRPC::BadStatus => e
|
168
|
+
raise map_grpc_error(e)
|
169
|
+
rescue StandardError => e
|
170
|
+
raise A2A::Errors::TransportError, "gRPC streaming call failed: #{e.message}"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Send server streaming gRPC request
|
176
|
+
#
|
177
|
+
# @param method [Symbol] gRPC method name
|
178
|
+
# @param request [Object] Request message
|
179
|
+
# @param metadata [Hash] Request metadata
|
180
|
+
# @param timeout [Integer, nil] Request timeout
|
181
|
+
# @return [Enumerator] Response stream
|
182
|
+
# @raise [A2A::Errors::TransportError] On gRPC errors
|
183
|
+
#
|
184
|
+
def server_streaming_call(method, request, metadata: {}, timeout: nil)
|
185
|
+
ensure_connected!
|
186
|
+
|
187
|
+
call_options = @call_options.dup
|
188
|
+
call_options[:timeout] = timeout if timeout
|
189
|
+
call_options[:metadata] = @config[:metadata].merge(metadata)
|
190
|
+
|
191
|
+
begin
|
192
|
+
@stub.public_send(method, request, call_options)
|
193
|
+
rescue ::GRPC::BadStatus => e
|
194
|
+
raise map_grpc_error(e)
|
195
|
+
rescue StandardError => e
|
196
|
+
raise A2A::Errors::TransportError, "gRPC streaming call failed: #{e.message}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
##
|
201
|
+
# Send bidirectional streaming gRPC request
|
202
|
+
#
|
203
|
+
# @param method [Symbol] gRPC method name
|
204
|
+
# @param requests [Enumerator] Request stream
|
205
|
+
# @param metadata [Hash] Request metadata
|
206
|
+
# @param timeout [Integer, nil] Request timeout
|
207
|
+
# @return [Enumerator] Response stream
|
208
|
+
# @raise [A2A::Errors::TransportError] On gRPC errors
|
209
|
+
#
|
210
|
+
def bidi_streaming_call(method, requests, metadata: {}, timeout: nil)
|
211
|
+
ensure_connected!
|
212
|
+
|
213
|
+
call_options = @call_options.dup
|
214
|
+
call_options[:timeout] = timeout if timeout
|
215
|
+
call_options[:metadata] = @config[:metadata].merge(metadata)
|
216
|
+
|
217
|
+
begin
|
218
|
+
@stub.public_send(method, requests, call_options)
|
219
|
+
rescue ::GRPC::BadStatus => e
|
220
|
+
raise map_grpc_error(e)
|
221
|
+
rescue StandardError => e
|
222
|
+
raise A2A::Errors::TransportError, "gRPC bidirectional streaming call failed: #{e.message}"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
##
|
227
|
+
# Send A2A message via gRPC
|
228
|
+
#
|
229
|
+
# @param message [A2A::Types::Message] A2A message
|
230
|
+
# @param streaming [Boolean] Use streaming response
|
231
|
+
# @param metadata [Hash] Request metadata
|
232
|
+
# @return [Object, Enumerator] Response or response stream
|
233
|
+
#
|
234
|
+
def send_a2a_message(message, streaming: false, metadata: {})
|
235
|
+
request = build_a2a_request(message)
|
236
|
+
|
237
|
+
if streaming
|
238
|
+
server_streaming_call(:send_message_stream, request, metadata: metadata)
|
239
|
+
else
|
240
|
+
unary_call(:send_message, request, metadata: metadata)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
##
|
245
|
+
# Get A2A task via gRPC
|
246
|
+
#
|
247
|
+
# @param task_id [String] Task ID
|
248
|
+
# @param metadata [Hash] Request metadata
|
249
|
+
# @return [Object] Task response
|
250
|
+
#
|
251
|
+
def get_a2a_task(task_id, metadata: {})
|
252
|
+
request = build_task_request(task_id)
|
253
|
+
unary_call(:get_task, request, metadata: metadata)
|
254
|
+
end
|
255
|
+
|
256
|
+
##
|
257
|
+
# Cancel A2A task via gRPC
|
258
|
+
#
|
259
|
+
# @param task_id [String] Task ID
|
260
|
+
# @param metadata [Hash] Request metadata
|
261
|
+
# @return [Object] Cancellation response
|
262
|
+
#
|
263
|
+
def cancel_a2a_task(task_id, metadata: {})
|
264
|
+
request = build_task_request(task_id)
|
265
|
+
unary_call(:cancel_task, request, metadata: metadata)
|
266
|
+
end
|
267
|
+
|
268
|
+
##
|
269
|
+
# Get agent card via gRPC
|
270
|
+
#
|
271
|
+
# @param metadata [Hash] Request metadata
|
272
|
+
# @return [Object] Agent card response
|
273
|
+
#
|
274
|
+
def get_agent_card(metadata: {})
|
275
|
+
request = build_empty_request
|
276
|
+
unary_call(:get_agent_card, request, metadata: metadata)
|
277
|
+
end
|
278
|
+
|
279
|
+
private
|
280
|
+
|
281
|
+
##
|
282
|
+
# Ensure connection is established
|
283
|
+
#
|
284
|
+
# @raise [A2A::Errors::TransportError] If not connected
|
285
|
+
#
|
286
|
+
def ensure_connected!
|
287
|
+
connect unless connected?
|
288
|
+
raise A2A::Errors::TransportError, "Not connected to gRPC service" unless connected?
|
289
|
+
end
|
290
|
+
|
291
|
+
##
|
292
|
+
# Build default configuration
|
293
|
+
#
|
294
|
+
# @return [Hash] Default configuration
|
295
|
+
#
|
296
|
+
def default_config
|
297
|
+
{
|
298
|
+
timeout: DEFAULT_TIMEOUT,
|
299
|
+
deadline: DEFAULT_DEADLINE,
|
300
|
+
max_receive_message_size: DEFAULT_MAX_RECEIVE_MESSAGE_SIZE,
|
301
|
+
max_send_message_size: DEFAULT_MAX_SEND_MESSAGE_SIZE,
|
302
|
+
keepalive_time: DEFAULT_KEEPALIVE_TIME,
|
303
|
+
keepalive_timeout: DEFAULT_KEEPALIVE_TIMEOUT,
|
304
|
+
use_tls: true,
|
305
|
+
ca_file: nil,
|
306
|
+
cert_file: nil,
|
307
|
+
key_file: nil,
|
308
|
+
metadata: {},
|
309
|
+
credentials: nil
|
310
|
+
}
|
311
|
+
end
|
312
|
+
|
313
|
+
##
|
314
|
+
# Build gRPC credentials
|
315
|
+
#
|
316
|
+
# @return [Object] gRPC credentials
|
317
|
+
#
|
318
|
+
def build_credentials
|
319
|
+
return @config[:credentials] if @config[:credentials]
|
320
|
+
|
321
|
+
if @config[:use_tls]
|
322
|
+
if @config[:ca_file] || @config[:cert_file] || @config[:key_file]
|
323
|
+
# Custom TLS credentials
|
324
|
+
ca_cert = @config[:ca_file] ? File.read(@config[:ca_file]) : nil
|
325
|
+
cert = @config[:cert_file] ? File.read(@config[:cert_file]) : nil
|
326
|
+
key = @config[:key_file] ? File.read(@config[:key_file]) : nil
|
327
|
+
|
328
|
+
::GRPC::Core::ChannelCredentials.new(ca_cert, key, cert)
|
329
|
+
else
|
330
|
+
# Default TLS credentials
|
331
|
+
::GRPC::Core::ChannelCredentials.new
|
332
|
+
end
|
333
|
+
else
|
334
|
+
# Insecure credentials
|
335
|
+
:this_channel_is_insecure
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
##
|
340
|
+
# Build gRPC stub
|
341
|
+
#
|
342
|
+
# @return [Object] gRPC stub instance
|
343
|
+
#
|
344
|
+
def build_stub
|
345
|
+
# This would be replaced with actual A2A gRPC service stub
|
346
|
+
# For now, we'll create a generic stub interface
|
347
|
+
A2AServiceStub.new(@endpoint, @credentials, channel_args: build_channel_args)
|
348
|
+
end
|
349
|
+
|
350
|
+
##
|
351
|
+
# Build channel arguments
|
352
|
+
#
|
353
|
+
# @return [Hash] Channel arguments
|
354
|
+
#
|
355
|
+
def build_channel_args
|
356
|
+
{
|
357
|
+
"grpc.keepalive_time_ms" => @config[:keepalive_time] * 1000,
|
358
|
+
"grpc.keepalive_timeout_ms" => @config[:keepalive_timeout] * 1000,
|
359
|
+
"grpc.keepalive_permit_without_calls" => 1,
|
360
|
+
"grpc.http2.max_pings_without_data" => 0,
|
361
|
+
"grpc.http2.min_time_between_pings_ms" => 10_000,
|
362
|
+
"grpc.http2.min_ping_interval_without_data_ms" => 300_000,
|
363
|
+
"grpc.max_receive_message_length" => @config[:max_receive_message_size],
|
364
|
+
"grpc.max_send_message_length" => @config[:max_send_message_size]
|
365
|
+
}
|
366
|
+
end
|
367
|
+
|
368
|
+
##
|
369
|
+
# Build call options
|
370
|
+
#
|
371
|
+
# @return [Hash] Call options
|
372
|
+
#
|
373
|
+
def build_call_options
|
374
|
+
{
|
375
|
+
timeout: @config[:timeout],
|
376
|
+
deadline: Time.now + @config[:deadline],
|
377
|
+
metadata: @config[:metadata]
|
378
|
+
}
|
379
|
+
end
|
380
|
+
|
381
|
+
##
|
382
|
+
# Map gRPC error to A2A error
|
383
|
+
#
|
384
|
+
# @param grpc_error [GRPC::BadStatus] gRPC error
|
385
|
+
# @return [A2A::Errors::A2AError] Mapped A2A error
|
386
|
+
#
|
387
|
+
def map_grpc_error(grpc_error)
|
388
|
+
case grpc_error.code
|
389
|
+
when ::GRPC::Core::StatusCodes::CANCELLED
|
390
|
+
A2A::Errors::TaskNotCancelable.new("Request cancelled: #{grpc_error.details}")
|
391
|
+
when ::GRPC::Core::StatusCodes::INVALID_ARGUMENT
|
392
|
+
A2A::Errors::InvalidParams.new("Invalid argument: #{grpc_error.details}")
|
393
|
+
when ::GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
|
394
|
+
A2A::Errors::TimeoutError.new("Deadline exceeded: #{grpc_error.details}")
|
395
|
+
when ::GRPC::Core::StatusCodes::NOT_FOUND
|
396
|
+
A2A::Errors::TaskNotFound.new("Not found: #{grpc_error.details}")
|
397
|
+
when ::GRPC::Core::StatusCodes::PERMISSION_DENIED
|
398
|
+
A2A::Errors::AuthorizationFailed.new("Permission denied: #{grpc_error.details}")
|
399
|
+
when ::GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED
|
400
|
+
A2A::Errors::ResourceExhausted.new("Resource exhausted: #{grpc_error.details}")
|
401
|
+
when ::GRPC::Core::StatusCodes::UNIMPLEMENTED
|
402
|
+
A2A::Errors::CapabilityNotSupported.new("Unimplemented: #{grpc_error.details}")
|
403
|
+
when ::GRPC::Core::StatusCodes::UNAVAILABLE
|
404
|
+
A2A::Errors::AgentUnavailable.new("Service unavailable: #{grpc_error.details}")
|
405
|
+
when ::GRPC::Core::StatusCodes::UNAUTHENTICATED
|
406
|
+
A2A::Errors::AuthenticationRequired.new("Unauthenticated: #{grpc_error.details}")
|
407
|
+
else
|
408
|
+
A2A::Errors::TransportError.new("gRPC error (#{grpc_error.code}): #{grpc_error.details}")
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
##
|
413
|
+
# Build A2A message request
|
414
|
+
#
|
415
|
+
# @param message [A2A::Types::Message] A2A message
|
416
|
+
# @return [Object] gRPC request object
|
417
|
+
#
|
418
|
+
def build_a2a_request(message)
|
419
|
+
# This would convert A2A::Types::Message to protobuf message
|
420
|
+
# For now, return a hash representation
|
421
|
+
{
|
422
|
+
message: message.to_h
|
423
|
+
}
|
424
|
+
end
|
425
|
+
|
426
|
+
##
|
427
|
+
# Build task request
|
428
|
+
#
|
429
|
+
# @param task_id [String] Task ID
|
430
|
+
# @return [Object] gRPC request object
|
431
|
+
#
|
432
|
+
def build_task_request(task_id)
|
433
|
+
{
|
434
|
+
task_id: task_id
|
435
|
+
}
|
436
|
+
end
|
437
|
+
|
438
|
+
##
|
439
|
+
# Build empty request
|
440
|
+
#
|
441
|
+
# @return [Object] gRPC request object
|
442
|
+
#
|
443
|
+
def build_empty_request
|
444
|
+
{}
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
##
|
449
|
+
# Stub implementation for A2A gRPC service
|
450
|
+
# This would be replaced with generated protobuf stubs
|
451
|
+
#
|
452
|
+
class A2AServiceStub
|
453
|
+
def initialize(endpoint, credentials, channel_args: {})
|
454
|
+
@endpoint = endpoint
|
455
|
+
@credentials = credentials
|
456
|
+
@channel_args = channel_args
|
457
|
+
end
|
458
|
+
|
459
|
+
# Placeholder methods - would be generated from protobuf definitions
|
460
|
+
def send_message(_request, _options = {})
|
461
|
+
raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
|
462
|
+
end
|
463
|
+
|
464
|
+
def send_message_stream(_request, _options = {})
|
465
|
+
raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
|
466
|
+
end
|
467
|
+
|
468
|
+
def get_task(_request, _options = {})
|
469
|
+
raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
|
470
|
+
end
|
471
|
+
|
472
|
+
def cancel_task(_request, _options = {})
|
473
|
+
raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
|
474
|
+
end
|
475
|
+
|
476
|
+
def get_agent_card(_request, _options = {})
|
477
|
+
raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|