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,415 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "json"
|
5
|
+
require "concurrent"
|
6
|
+
|
7
|
+
# Try to load multipart support - graceful degradation if not available
|
8
|
+
begin
|
9
|
+
require "faraday/multipart"
|
10
|
+
MULTIPART_AVAILABLE = true
|
11
|
+
rescue LoadError
|
12
|
+
MULTIPART_AVAILABLE = false
|
13
|
+
end
|
14
|
+
|
15
|
+
module A2A
|
16
|
+
module Transport
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# HTTP transport implementation using Faraday adapter pattern
|
22
|
+
# Provides connection pooling, timeout management, logging, and HTTPS support
|
23
|
+
#
|
24
|
+
module A2A
|
25
|
+
module Transport
|
26
|
+
class Http
|
27
|
+
# Default configuration values
|
28
|
+
DEFAULT_TIMEOUT = 30
|
29
|
+
DEFAULT_OPEN_TIMEOUT = 10
|
30
|
+
DEFAULT_READ_TIMEOUT = 30
|
31
|
+
DEFAULT_WRITE_TIMEOUT = 30
|
32
|
+
DEFAULT_POOL_SIZE = 5
|
33
|
+
DEFAULT_POOL_TIMEOUT = 5
|
34
|
+
DEFAULT_RETRY_COUNT = 3
|
35
|
+
DEFAULT_RETRY_DELAY = 1.0
|
36
|
+
|
37
|
+
attr_reader :base_url, :config, :connection
|
38
|
+
|
39
|
+
##
|
40
|
+
# Initialize HTTP transport
|
41
|
+
#
|
42
|
+
# @param base_url [String] Base URL for the HTTP endpoint
|
43
|
+
# @param config [Hash] Configuration options
|
44
|
+
# @option config [Integer] :timeout (30) Request timeout in seconds
|
45
|
+
# @option config [Integer] :open_timeout (10) Connection open timeout
|
46
|
+
# @option config [Integer] :read_timeout (30) Read timeout
|
47
|
+
# @option config [Integer] :write_timeout (30) Write timeout
|
48
|
+
# @option config [Integer] :pool_size (5) Connection pool size
|
49
|
+
# @option config [Integer] :pool_timeout (5) Pool checkout timeout
|
50
|
+
# @option config [Integer] :retry_count (3) Number of retries
|
51
|
+
# @option config [Float] :retry_delay (1.0) Delay between retries
|
52
|
+
# @option config [Boolean] :ssl_verify (true) Verify SSL certificates
|
53
|
+
# @option config [String] :ssl_ca_file Path to CA certificate file
|
54
|
+
# @option config [String] :ssl_ca_path Path to CA certificate directory
|
55
|
+
# @option config [Hash] :headers ({}) Default headers
|
56
|
+
# @option config [Boolean] :logging (false) Enable request/response logging
|
57
|
+
# @option config [Logger] :logger Logger instance
|
58
|
+
# @option config [Hash] :proxy Proxy configuration
|
59
|
+
#
|
60
|
+
def initialize(base_url, config = {})
|
61
|
+
@base_url = base_url
|
62
|
+
@config = default_config.merge(config)
|
63
|
+
@connection = build_connection
|
64
|
+
@metrics = Concurrent::Hash.new(0)
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Send HTTP request
|
69
|
+
#
|
70
|
+
# @param method [Symbol] HTTP method (:get, :post, :put, :delete, etc.)
|
71
|
+
# @param path [String] Request path
|
72
|
+
# @param params [Hash] Request parameters
|
73
|
+
# @param headers [Hash] Request headers
|
74
|
+
# @param body [String, Hash] Request body
|
75
|
+
# @return [Faraday::Response] HTTP response
|
76
|
+
# @raise [A2A::Errors::HTTPError] On HTTP errors
|
77
|
+
# @raise [A2A::Errors::TimeoutError] On timeout
|
78
|
+
# @raise [A2A::Errors::TransportError] On transport errors
|
79
|
+
#
|
80
|
+
def request(method, path = "", params: {}, headers: {}, body: nil)
|
81
|
+
start_time = Time.now
|
82
|
+
|
83
|
+
begin
|
84
|
+
response = @connection.public_send(method, path) do |req|
|
85
|
+
req.params.update(params) if params.any?
|
86
|
+
req.headers.update(headers) if headers.any?
|
87
|
+
req.body = prepare_body(body) if body
|
88
|
+
end
|
89
|
+
|
90
|
+
record_metrics(method, response.status, Time.now - start_time)
|
91
|
+
handle_response(response)
|
92
|
+
response
|
93
|
+
rescue A2A::Errors::HTTPError => e
|
94
|
+
# Re-raise A2A HTTP errors (from handle_response)
|
95
|
+
record_metrics(method, e.status_code || :http_error, Time.now - start_time)
|
96
|
+
raise e
|
97
|
+
rescue Faraday::TimeoutError => e
|
98
|
+
record_metrics(method, :timeout, Time.now - start_time)
|
99
|
+
raise A2A::Errors::TimeoutError, "Request timeout: #{e.message}"
|
100
|
+
rescue Faraday::ConnectionFailed => e
|
101
|
+
record_metrics(method, :connection_failed, Time.now - start_time)
|
102
|
+
# Check if it's a timeout-like error
|
103
|
+
if e.message.include?("timeout") || e.message.include?("execution expired")
|
104
|
+
raise A2A::Errors::TimeoutError, "Request timeout: #{e.message}"
|
105
|
+
end
|
106
|
+
|
107
|
+
raise A2A::Errors::TransportError, "Connection failed: #{e.message}"
|
108
|
+
rescue Faraday::SSLError => e
|
109
|
+
record_metrics(method, :ssl_error, Time.now - start_time)
|
110
|
+
raise A2A::Errors::TransportError, "SSL error: #{e.message}"
|
111
|
+
rescue Faraday::ClientError => e
|
112
|
+
record_metrics(method, :client_error, Time.now - start_time)
|
113
|
+
# Handle HTTP status errors from Faraday
|
114
|
+
raise A2A::Errors::TransportError, "Client error: #{e.message}" unless e.response && e.response[:status]
|
115
|
+
|
116
|
+
status = e.response[:status]
|
117
|
+
case status
|
118
|
+
when 400..499
|
119
|
+
raise A2A::Errors::HTTPError.new(
|
120
|
+
"Client error: #{status}",
|
121
|
+
status_code: status,
|
122
|
+
response_body: e.response[:body]
|
123
|
+
)
|
124
|
+
when 500..599
|
125
|
+
raise A2A::Errors::HTTPError.new(
|
126
|
+
"Server error: #{status}",
|
127
|
+
status_code: status,
|
128
|
+
response_body: e.response[:body]
|
129
|
+
)
|
130
|
+
else
|
131
|
+
raise A2A::Errors::HTTPError.new(
|
132
|
+
"HTTP error: #{status}",
|
133
|
+
status_code: status,
|
134
|
+
response_body: e.response[:body]
|
135
|
+
)
|
136
|
+
end
|
137
|
+
rescue StandardError => e
|
138
|
+
record_metrics(method, :error, Time.now - start_time)
|
139
|
+
# Check if it's a timeout-like error
|
140
|
+
if e.message.include?("timeout") || e.message.include?("execution expired")
|
141
|
+
raise A2A::Errors::TimeoutError, "Request timeout: #{e.message}"
|
142
|
+
end
|
143
|
+
|
144
|
+
raise A2A::Errors::TransportError, "Transport error: #{e.message}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Send GET request
|
150
|
+
#
|
151
|
+
# @param path [String] Request path
|
152
|
+
# @param params [Hash] Query parameters
|
153
|
+
# @param headers [Hash] Request headers
|
154
|
+
# @return [Faraday::Response] HTTP response
|
155
|
+
#
|
156
|
+
def get(path = "", params: {}, headers: {})
|
157
|
+
request(:get, path, params: params, headers: headers)
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Send POST request
|
162
|
+
#
|
163
|
+
# @param path [String] Request path
|
164
|
+
# @param body [String, Hash] Request body
|
165
|
+
# @param params [Hash] Query parameters
|
166
|
+
# @param headers [Hash] Request headers
|
167
|
+
# @return [Faraday::Response] HTTP response
|
168
|
+
#
|
169
|
+
def post(path = "", body: nil, params: {}, headers: {})
|
170
|
+
request(:post, path, params: params, headers: headers, body: body)
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Send PUT request
|
175
|
+
#
|
176
|
+
# @param path [String] Request path
|
177
|
+
# @param body [String, Hash] Request body
|
178
|
+
# @param params [Hash] Query parameters
|
179
|
+
# @param headers [Hash] Request headers
|
180
|
+
# @return [Faraday::Response] HTTP response
|
181
|
+
#
|
182
|
+
def put(path = "", body: nil, params: {}, headers: {})
|
183
|
+
request(:put, path, params: params, headers: headers, body: body)
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# Send DELETE request
|
188
|
+
#
|
189
|
+
# @param path [String] Request path
|
190
|
+
# @param params [Hash] Query parameters
|
191
|
+
# @param headers [Hash] Request headers
|
192
|
+
# @return [Faraday::Response] HTTP response
|
193
|
+
#
|
194
|
+
def delete(path = "", params: {}, headers: {})
|
195
|
+
request(:delete, path, params: params, headers: headers)
|
196
|
+
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# Send JSON-RPC request
|
200
|
+
#
|
201
|
+
# @param rpc_request [Hash] JSON-RPC request object
|
202
|
+
# @param headers [Hash] Additional headers
|
203
|
+
# @return [Hash] JSON-RPC response
|
204
|
+
# @raise [A2A::Errors::JSONError] On JSON parsing errors
|
205
|
+
#
|
206
|
+
def json_rpc_request(rpc_request, headers: {})
|
207
|
+
default_headers = {
|
208
|
+
"Content-Type" => "application/json",
|
209
|
+
"Accept" => "application/json"
|
210
|
+
}
|
211
|
+
|
212
|
+
response = post(
|
213
|
+
body: rpc_request.to_json,
|
214
|
+
headers: default_headers.merge(headers)
|
215
|
+
)
|
216
|
+
|
217
|
+
parse_json_response(response)
|
218
|
+
end
|
219
|
+
|
220
|
+
##
|
221
|
+
# Get connection metrics
|
222
|
+
#
|
223
|
+
# @return [Hash] Metrics data
|
224
|
+
#
|
225
|
+
def metrics
|
226
|
+
@metrics.to_h
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Reset connection metrics
|
231
|
+
#
|
232
|
+
def reset_metrics!
|
233
|
+
@metrics.clear
|
234
|
+
end
|
235
|
+
|
236
|
+
##
|
237
|
+
# Close connection and cleanup resources
|
238
|
+
#
|
239
|
+
def close
|
240
|
+
@connection&.close
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
##
|
246
|
+
# Build default configuration
|
247
|
+
#
|
248
|
+
# @return [Hash] Default configuration
|
249
|
+
#
|
250
|
+
def default_config
|
251
|
+
{
|
252
|
+
timeout: DEFAULT_TIMEOUT,
|
253
|
+
open_timeout: DEFAULT_OPEN_TIMEOUT,
|
254
|
+
read_timeout: DEFAULT_READ_TIMEOUT,
|
255
|
+
write_timeout: DEFAULT_WRITE_TIMEOUT,
|
256
|
+
pool_size: DEFAULT_POOL_SIZE,
|
257
|
+
pool_timeout: DEFAULT_POOL_TIMEOUT,
|
258
|
+
retry_count: DEFAULT_RETRY_COUNT,
|
259
|
+
retry_delay: DEFAULT_RETRY_DELAY,
|
260
|
+
ssl_verify: true,
|
261
|
+
headers: {},
|
262
|
+
logging: false,
|
263
|
+
logger: nil,
|
264
|
+
proxy: nil
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
268
|
+
##
|
269
|
+
# Build Faraday connection with configuration
|
270
|
+
#
|
271
|
+
# @return [Faraday::Connection] Configured connection
|
272
|
+
#
|
273
|
+
def build_connection
|
274
|
+
Faraday.new(@base_url) do |conn|
|
275
|
+
# Request/response middleware
|
276
|
+
conn.request :json
|
277
|
+
conn.request :multipart if MULTIPART_AVAILABLE
|
278
|
+
conn.request :url_encoded
|
279
|
+
|
280
|
+
# NOTE: Retry middleware requires faraday-retry gem
|
281
|
+
# Uncomment when faraday-retry is available:
|
282
|
+
# conn.request :retry,
|
283
|
+
# max: @config[:retry_count],
|
284
|
+
# interval: @config[:retry_delay],
|
285
|
+
# backoff_factor: 2,
|
286
|
+
# retry_statuses: [429, 500, 502, 503, 504],
|
287
|
+
# methods: [:get, :post, :put, :delete]
|
288
|
+
|
289
|
+
# Logging middleware
|
290
|
+
conn.response :logger, @config[:logger] || default_logger if @config[:logging]
|
291
|
+
|
292
|
+
# Response middleware
|
293
|
+
conn.response :json, content_type: /\bjson$/
|
294
|
+
# NOTE: Not using :raise_error to handle errors manually
|
295
|
+
|
296
|
+
# Adapter (use default net_http adapter)
|
297
|
+
# Note: net_http_persistent requires separate gem for connection pooling
|
298
|
+
conn.adapter Faraday.default_adapter
|
299
|
+
|
300
|
+
# Configure timeouts
|
301
|
+
conn.options.timeout = @config[:timeout]
|
302
|
+
conn.options.open_timeout = @config[:open_timeout]
|
303
|
+
conn.options.read_timeout = @config[:read_timeout]
|
304
|
+
conn.options.write_timeout = @config[:write_timeout]
|
305
|
+
|
306
|
+
# Configure SSL
|
307
|
+
conn.ssl.verify = @config[:ssl_verify]
|
308
|
+
conn.ssl.ca_file = @config[:ssl_ca_file] if @config[:ssl_ca_file]
|
309
|
+
conn.ssl.ca_path = @config[:ssl_ca_path] if @config[:ssl_ca_path]
|
310
|
+
|
311
|
+
# Configure proxy
|
312
|
+
conn.proxy = @config[:proxy] if @config[:proxy]
|
313
|
+
|
314
|
+
# Set default headers
|
315
|
+
conn.headers.update(@config[:headers]) if @config[:headers].any?
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
##
|
320
|
+
# Prepare request body
|
321
|
+
#
|
322
|
+
# @param body [String, Hash, Object] Request body
|
323
|
+
# @return [String] Prepared body
|
324
|
+
#
|
325
|
+
def prepare_body(body)
|
326
|
+
case body
|
327
|
+
when String
|
328
|
+
body
|
329
|
+
when Hash, Array
|
330
|
+
body.to_json
|
331
|
+
else
|
332
|
+
body.respond_to?(:to_json) ? body.to_json : body.to_s
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
##
|
337
|
+
# Handle HTTP response
|
338
|
+
#
|
339
|
+
# @param response [Faraday::Response] HTTP response
|
340
|
+
# @return [Faraday::Response] Validated response
|
341
|
+
# @raise [A2A::Errors::HTTPError] On HTTP errors
|
342
|
+
#
|
343
|
+
def handle_response(response)
|
344
|
+
case response.status
|
345
|
+
when 200..299
|
346
|
+
response
|
347
|
+
when 400..499
|
348
|
+
raise A2A::Errors::HTTPError.new(
|
349
|
+
"Client error: #{response.status}",
|
350
|
+
status_code: response.status,
|
351
|
+
response_body: response.body
|
352
|
+
)
|
353
|
+
when 500..599
|
354
|
+
raise A2A::Errors::HTTPError.new(
|
355
|
+
"Server error: #{response.status}",
|
356
|
+
status_code: response.status,
|
357
|
+
response_body: response.body
|
358
|
+
)
|
359
|
+
else
|
360
|
+
raise A2A::Errors::HTTPError.new(
|
361
|
+
"Unexpected status: #{response.status}",
|
362
|
+
status_code: response.status,
|
363
|
+
response_body: response.body
|
364
|
+
)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
##
|
369
|
+
# Parse JSON response
|
370
|
+
#
|
371
|
+
# @param response [Faraday::Response] HTTP response
|
372
|
+
# @return [Hash] Parsed JSON data
|
373
|
+
# @raise [A2A::Errors::JSONError] On JSON parsing errors
|
374
|
+
#
|
375
|
+
def parse_json_response(response)
|
376
|
+
return response.body if response.body.is_a?(Hash)
|
377
|
+
|
378
|
+
JSON.parse(response.body)
|
379
|
+
rescue JSON::ParserError => e
|
380
|
+
raise A2A::Errors::JSONError, "Invalid JSON response: #{e.message}"
|
381
|
+
end
|
382
|
+
|
383
|
+
##
|
384
|
+
# Record request metrics
|
385
|
+
#
|
386
|
+
# @param method [Symbol] HTTP method
|
387
|
+
# @param status [Integer, Symbol] Response status or error type
|
388
|
+
# @param duration [Float] Request duration in seconds
|
389
|
+
#
|
390
|
+
def record_metrics(method, status, duration)
|
391
|
+
@metrics["#{method}_requests"] += 1
|
392
|
+
@metrics["#{method}_#{status}"] += 1
|
393
|
+
@metrics["total_requests"] += 1
|
394
|
+
@metrics["total_duration"] += duration
|
395
|
+
|
396
|
+
# Track average duration
|
397
|
+
@metrics["average_duration"] = @metrics["total_duration"] / @metrics["total_requests"]
|
398
|
+
end
|
399
|
+
|
400
|
+
##
|
401
|
+
# Get default logger
|
402
|
+
#
|
403
|
+
# @return [Logger] Default logger instance
|
404
|
+
#
|
405
|
+
def default_logger
|
406
|
+
logger = Logger.new($stdout)
|
407
|
+
logger.level = Logger::INFO
|
408
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
409
|
+
"[#{datetime}] #{severity} -- #{progname}: #{msg}\n"
|
410
|
+
end
|
411
|
+
logger
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|