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,537 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../protocol/capability"
|
4
|
+
|
5
|
+
module A2A
|
6
|
+
module Server
|
7
|
+
##
|
8
|
+
# Agent DSL for defining A2A-compatible methods and capabilities
|
9
|
+
#
|
10
|
+
# This module provides a DSL for defining agent methods that can be called
|
11
|
+
# via the A2A protocol. It includes method registration, capability definition,
|
12
|
+
# parameter validation, and middleware support.
|
13
|
+
#
|
14
|
+
# @example Basic agent definition
|
15
|
+
# class MyAgent
|
16
|
+
# include A2A::Server::Agent
|
17
|
+
#
|
18
|
+
# a2a_method "greet" do |params|
|
19
|
+
# { message: "Hello, #{params['name']}!" }
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# a2a_capability "greeting" do
|
23
|
+
# method "greet"
|
24
|
+
# description "Greet a user by name"
|
25
|
+
# input_schema type: "object", properties: { name: { type: "string" } }
|
26
|
+
# output_schema type: "object", properties: { message: { type: "string" } }
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
module Agent
|
31
|
+
def self.included(base)
|
32
|
+
base.extend(ClassMethods)
|
33
|
+
|
34
|
+
# Initialize class-level storage for A2A methods and capabilities
|
35
|
+
if defined?(ActiveSupport) && base.respond_to?(:class_attribute)
|
36
|
+
# Use ActiveSupport's class_attribute if available
|
37
|
+
base.class_attribute :_a2a_methods, default: {}
|
38
|
+
base.class_attribute :_a2a_capabilities, default: A2A::Protocol::CapabilityRegistry.new
|
39
|
+
base.class_attribute :_a2a_config, default: {}
|
40
|
+
base.class_attribute :_a2a_middleware, default: []
|
41
|
+
else
|
42
|
+
# Fallback implementation without ActiveSupport
|
43
|
+
base.instance_variable_set(:@_a2a_methods, {})
|
44
|
+
base.instance_variable_set(:@_a2a_capabilities, A2A::Protocol::CapabilityRegistry.new)
|
45
|
+
base.instance_variable_set(:@_a2a_config, {})
|
46
|
+
base.instance_variable_set(:@_a2a_middleware, [])
|
47
|
+
|
48
|
+
# Define accessor methods
|
49
|
+
base.define_singleton_method(:_a2a_methods) { @_a2a_methods }
|
50
|
+
base.define_singleton_method(:_a2a_capabilities) { @_a2a_capabilities }
|
51
|
+
base.define_singleton_method(:_a2a_config) { @_a2a_config }
|
52
|
+
base.define_singleton_method(:_a2a_middleware) { @_a2a_middleware }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module ClassMethods
|
57
|
+
##
|
58
|
+
# Define an A2A method that can be called via JSON-RPC
|
59
|
+
#
|
60
|
+
# @param name [String] The method name
|
61
|
+
# @param options [Hash] Method options
|
62
|
+
# @option options [Boolean] :streaming Whether the method supports streaming
|
63
|
+
# @option options [Boolean] :async Whether the method supports async execution
|
64
|
+
# @option options [Array<String>] :security Required security schemes
|
65
|
+
# @option options [Hash] :metadata Additional method metadata
|
66
|
+
# @yield [params, context] The method implementation block
|
67
|
+
# @yieldparam params [Hash] The method parameters
|
68
|
+
# @yieldparam context [A2A::Server::Context] The request context
|
69
|
+
# @yieldreturn [Object] The method result
|
70
|
+
#
|
71
|
+
# @example Define a simple method
|
72
|
+
# a2a_method "echo" do |params|
|
73
|
+
# { message: params['message'] }
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# @example Define a streaming method
|
77
|
+
# a2a_method "stream_data", streaming: true do |params, context|
|
78
|
+
# Enumerator.new do |yielder|
|
79
|
+
# 10.times do |i|
|
80
|
+
# yielder << { count: i }
|
81
|
+
# sleep 0.1
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
def a2a_method(name, **options, &block)
|
87
|
+
if name.nil? || (respond_to?(:empty?) && empty?) || (is_a?(String) && strip.empty?)
|
88
|
+
raise ArgumentError,
|
89
|
+
"Method name is required"
|
90
|
+
end
|
91
|
+
raise ArgumentError, "Method block is required" unless block_given?
|
92
|
+
|
93
|
+
method_name = name.to_s
|
94
|
+
|
95
|
+
_a2a_methods[method_name] = {
|
96
|
+
handler: block,
|
97
|
+
options: options.dup,
|
98
|
+
streaming: options[:streaming] || false,
|
99
|
+
async: options[:async] || false,
|
100
|
+
security: options[:security] || [],
|
101
|
+
metadata: options[:metadata] || {}
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Define a capability using the DSL
|
107
|
+
#
|
108
|
+
# @param name [String] The capability name
|
109
|
+
# @yield Block for capability definition
|
110
|
+
#
|
111
|
+
# @example Define a capability
|
112
|
+
# a2a_capability "text_analysis" do
|
113
|
+
# method "analyze_text"
|
114
|
+
# description "Analyze text for sentiment and topics"
|
115
|
+
# input_schema type: "object", properties: { text: { type: "string" } }
|
116
|
+
# output_schema type: "object", properties: { sentiment: { type: "string" } }
|
117
|
+
# tags ["nlp", "analysis"]
|
118
|
+
# streaming_supported true
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
def a2a_capability(name, &block)
|
122
|
+
if name.nil? || (respond_to?(:empty?) && empty?) || (is_a?(String) && strip.empty?)
|
123
|
+
raise ArgumentError,
|
124
|
+
"Capability name is required"
|
125
|
+
end
|
126
|
+
raise ArgumentError, "Capability block is required" unless block_given?
|
127
|
+
|
128
|
+
builder = CapabilityBuilder.new(name.to_s)
|
129
|
+
builder.instance_eval(&block)
|
130
|
+
capability = builder.build
|
131
|
+
|
132
|
+
_a2a_capabilities.register(capability)
|
133
|
+
capability
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Configure the agent
|
138
|
+
#
|
139
|
+
# @param options [Hash] Configuration options
|
140
|
+
# @option options [String] :name Agent name
|
141
|
+
# @option options [String] :description Agent description
|
142
|
+
# @option options [String] :version Agent version
|
143
|
+
# @option options [Array<String>] :default_input_modes Default input modes
|
144
|
+
# @option options [Array<String>] :default_output_modes Default output modes
|
145
|
+
# @option options [Hash] :metadata Additional metadata
|
146
|
+
#
|
147
|
+
# @example Configure agent
|
148
|
+
# a2a_config name: "My Agent",
|
149
|
+
# description: "A helpful agent",
|
150
|
+
# version: "1.0.0"
|
151
|
+
#
|
152
|
+
def a2a_config(**options)
|
153
|
+
_a2a_config.merge!(options)
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Add middleware to the agent
|
158
|
+
#
|
159
|
+
# @param middleware_class [Class] The middleware class
|
160
|
+
# @param options [Hash] Middleware options
|
161
|
+
#
|
162
|
+
# @example Add authentication middleware
|
163
|
+
# a2a_middleware AuthenticationMiddleware, required: true
|
164
|
+
#
|
165
|
+
def a2a_middleware(middleware_class, **options)
|
166
|
+
_a2a_middleware << { class: middleware_class, options: options }
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Get all registered A2A methods
|
171
|
+
#
|
172
|
+
# @return [Hash] Hash of method name to method definition
|
173
|
+
def a2a_method_registry
|
174
|
+
_a2a_methods.dup
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Get the capability registry
|
179
|
+
#
|
180
|
+
# @return [A2A::Protocol::CapabilityRegistry] The capability registry
|
181
|
+
def a2a_capability_registry
|
182
|
+
_a2a_capabilities
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Check if a method is registered
|
187
|
+
#
|
188
|
+
# @param name [String] The method name
|
189
|
+
# @return [Boolean] True if the method is registered
|
190
|
+
def a2a_method_registered?(name)
|
191
|
+
_a2a_methods.key?(name.to_s)
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Get method definition
|
196
|
+
#
|
197
|
+
# @param name [String] The method name
|
198
|
+
# @return [Hash, nil] The method definition or nil if not found
|
199
|
+
def a2a_method_definition(name)
|
200
|
+
_a2a_methods[name.to_s]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
##
|
205
|
+
# Handle an A2A JSON-RPC request
|
206
|
+
#
|
207
|
+
# @param request [A2A::Protocol::Request] The JSON-RPC request
|
208
|
+
# @param context [A2A::Server::Context, nil] The request context
|
209
|
+
# @return [Hash] The JSON-RPC response
|
210
|
+
def handle_a2a_request(request, context: nil)
|
211
|
+
method_name = request.method
|
212
|
+
method_def = self.class.a2a_method_definition(method_name)
|
213
|
+
|
214
|
+
raise A2A::Errors::MethodNotFound, "Method '#{method_name}' not found" unless method_def
|
215
|
+
|
216
|
+
# Create context if not provided
|
217
|
+
context ||= A2A::Server::Context.new(request: request)
|
218
|
+
|
219
|
+
# Validate security requirements
|
220
|
+
validate_security_requirements(method_def, context)
|
221
|
+
|
222
|
+
# Execute with middleware chain
|
223
|
+
result = execute_with_middleware(method_def, request.params, context)
|
224
|
+
|
225
|
+
# Build response
|
226
|
+
A2A::Protocol::JsonRpc.build_response(result: result, id: request.id) unless request.notification?
|
227
|
+
rescue A2A::Errors::A2AError => e
|
228
|
+
# Return error response for A2A errors
|
229
|
+
unless request.notification?
|
230
|
+
A2A::Protocol::JsonRpc.build_error_response(
|
231
|
+
code: e.code,
|
232
|
+
message: e.message,
|
233
|
+
data: e.data,
|
234
|
+
id: request.id
|
235
|
+
)
|
236
|
+
end
|
237
|
+
rescue StandardError => e
|
238
|
+
# Convert other errors to internal errors
|
239
|
+
unless request.notification?
|
240
|
+
A2A::Protocol::JsonRpc.build_error_response(
|
241
|
+
code: A2A::Protocol::JsonRpc::INTERNAL_ERROR,
|
242
|
+
message: e.message,
|
243
|
+
id: request.id
|
244
|
+
)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
##
|
249
|
+
# Execute a method with the middleware chain
|
250
|
+
#
|
251
|
+
# @param method_def [Hash] The method definition
|
252
|
+
# @param params [Hash] The method parameters
|
253
|
+
# @param context [A2A::Server::Context] The request context
|
254
|
+
# @return [Object] The method result
|
255
|
+
def execute_with_middleware(method_def, params, context)
|
256
|
+
# Build middleware chain
|
257
|
+
middleware_chain = build_middleware_chain(method_def, context)
|
258
|
+
|
259
|
+
# Execute the chain
|
260
|
+
middleware_chain.call(params) do
|
261
|
+
# Execute the actual method
|
262
|
+
execute_method(method_def, params, context)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
##
|
267
|
+
# Execute the actual method implementation
|
268
|
+
#
|
269
|
+
# @param method_def [Hash] The method definition
|
270
|
+
# @param params [Hash] The method parameters
|
271
|
+
# @param context [A2A::Server::Context] The request context
|
272
|
+
# @return [Object] The method result
|
273
|
+
def execute_method(method_def, params, context)
|
274
|
+
handler = method_def[:handler]
|
275
|
+
|
276
|
+
# Call the handler with appropriate parameters
|
277
|
+
case handler.arity
|
278
|
+
when 0
|
279
|
+
instance_exec(&handler)
|
280
|
+
when 1
|
281
|
+
instance_exec(params, &handler)
|
282
|
+
when 2
|
283
|
+
instance_exec(params, context, &handler)
|
284
|
+
else
|
285
|
+
# For methods with more parameters, pass params and context
|
286
|
+
instance_exec(params, context, &handler)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
private
|
291
|
+
|
292
|
+
##
|
293
|
+
# Validate security requirements for a method
|
294
|
+
#
|
295
|
+
# @param method_def [Hash] The method definition
|
296
|
+
# @param context [A2A::Server::Context] The request context
|
297
|
+
# @raise [A2A::Errors::AuthenticationRequired] If authentication is required but missing
|
298
|
+
# @raise [A2A::Errors::AuthorizationFailed] If authorization fails
|
299
|
+
def validate_security_requirements(method_def, context)
|
300
|
+
security_requirements = method_def[:security] || []
|
301
|
+
return if security_requirements.empty?
|
302
|
+
|
303
|
+
# Check if any required security scheme is satisfied
|
304
|
+
satisfied = security_requirements.any? do |scheme|
|
305
|
+
context.authenticated_with?(scheme)
|
306
|
+
end
|
307
|
+
|
308
|
+
return if satisfied
|
309
|
+
|
310
|
+
if context.authenticated?
|
311
|
+
raise A2A::Errors::AuthorizationFailed, "Method requires one of: #{security_requirements.join(', ')}"
|
312
|
+
end
|
313
|
+
|
314
|
+
raise A2A::Errors::AuthenticationRequired,
|
315
|
+
"Method requires authentication with: #{security_requirements.join(', ')}"
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# Build the middleware chain for method execution
|
320
|
+
#
|
321
|
+
# @param method_def [Hash] The method definition
|
322
|
+
# @param context [A2A::Server::Context] The request context
|
323
|
+
# @return [Proc] The middleware chain
|
324
|
+
def build_middleware_chain(_method_def, context)
|
325
|
+
# Combine class-level and method-level middleware
|
326
|
+
all_middleware = self.class._a2a_middleware.dup
|
327
|
+
|
328
|
+
# Build the chain from the inside out
|
329
|
+
chain = ->(_params, &block) { block.call }
|
330
|
+
|
331
|
+
all_middleware.reverse_each do |middleware_def|
|
332
|
+
middleware_class = middleware_def[:class]
|
333
|
+
middleware_options = middleware_def[:options]
|
334
|
+
|
335
|
+
# Create middleware instance
|
336
|
+
middleware = middleware_class.new(**middleware_options)
|
337
|
+
|
338
|
+
# Wrap the current chain
|
339
|
+
current_chain = chain
|
340
|
+
chain = lambda do |params, &block|
|
341
|
+
middleware.call(params, context) do
|
342
|
+
current_chain.call(params, &block)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
chain
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
##
|
352
|
+
# Builder class for capability DSL
|
353
|
+
#
|
354
|
+
class CapabilityBuilder
|
355
|
+
def initialize(name)
|
356
|
+
@name = name
|
357
|
+
@attributes = {}
|
358
|
+
end
|
359
|
+
|
360
|
+
##
|
361
|
+
# Set the method name for this capability
|
362
|
+
#
|
363
|
+
# @param method_name [String, Symbol] The A2A method name
|
364
|
+
def method(method_name)
|
365
|
+
@attributes[:method] = method_name.to_s
|
366
|
+
end
|
367
|
+
|
368
|
+
##
|
369
|
+
# Set the capability description
|
370
|
+
#
|
371
|
+
# @param desc [String] The description
|
372
|
+
def description(desc)
|
373
|
+
@attributes[:description] = desc
|
374
|
+
end
|
375
|
+
|
376
|
+
##
|
377
|
+
# Set the input schema
|
378
|
+
#
|
379
|
+
# @param schema [Hash] The JSON Schema for input validation
|
380
|
+
def input_schema(schema)
|
381
|
+
@attributes[:input_schema] = schema
|
382
|
+
end
|
383
|
+
|
384
|
+
##
|
385
|
+
# Set the output schema
|
386
|
+
#
|
387
|
+
# @param schema [Hash] The JSON Schema for output validation
|
388
|
+
def output_schema(schema)
|
389
|
+
@attributes[:output_schema] = schema
|
390
|
+
end
|
391
|
+
|
392
|
+
##
|
393
|
+
# Set capability tags
|
394
|
+
#
|
395
|
+
# @param tag_list [Array<String>] List of tags
|
396
|
+
def tags(tag_list)
|
397
|
+
@attributes[:tags] = tag_list
|
398
|
+
end
|
399
|
+
|
400
|
+
##
|
401
|
+
# Set security requirements
|
402
|
+
#
|
403
|
+
# @param requirements [Array<String>] List of required security schemes
|
404
|
+
def security_requirements(requirements)
|
405
|
+
@attributes[:security_requirements] = requirements
|
406
|
+
end
|
407
|
+
|
408
|
+
##
|
409
|
+
# Set additional metadata
|
410
|
+
#
|
411
|
+
# @param meta [Hash] Metadata hash
|
412
|
+
def metadata(meta)
|
413
|
+
@attributes[:metadata] = meta
|
414
|
+
end
|
415
|
+
|
416
|
+
##
|
417
|
+
# Set streaming support
|
418
|
+
#
|
419
|
+
# @param supported [Boolean] Whether streaming is supported
|
420
|
+
def streaming_supported(supported = true)
|
421
|
+
@attributes[:streaming_supported] = supported
|
422
|
+
end
|
423
|
+
|
424
|
+
##
|
425
|
+
# Set async support
|
426
|
+
#
|
427
|
+
# @param supported [Boolean] Whether async execution is supported
|
428
|
+
def async_supported(supported = true)
|
429
|
+
@attributes[:async_supported] = supported
|
430
|
+
end
|
431
|
+
|
432
|
+
##
|
433
|
+
# Add an example
|
434
|
+
#
|
435
|
+
# @param example [Hash] Example with input/output/description
|
436
|
+
def example(example)
|
437
|
+
@attributes[:examples] ||= []
|
438
|
+
@attributes[:examples] << example
|
439
|
+
end
|
440
|
+
|
441
|
+
##
|
442
|
+
# Build the capability
|
443
|
+
#
|
444
|
+
# @return [A2A::Protocol::Capability] The built capability
|
445
|
+
def build
|
446
|
+
A2A::Protocol::Capability.new(
|
447
|
+
name: @name,
|
448
|
+
**@attributes
|
449
|
+
)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
##
|
454
|
+
# Request context for A2A method execution
|
455
|
+
#
|
456
|
+
class Context
|
457
|
+
attr_reader :request, :user, :session, :metadata
|
458
|
+
|
459
|
+
def initialize(request: nil, user: nil, session: nil, metadata: {})
|
460
|
+
@request = request
|
461
|
+
@user = user
|
462
|
+
@session = session
|
463
|
+
@metadata = metadata
|
464
|
+
@auth_schemes = {}
|
465
|
+
end
|
466
|
+
|
467
|
+
##
|
468
|
+
# Check if the request is authenticated
|
469
|
+
#
|
470
|
+
# @return [Boolean] True if authenticated
|
471
|
+
def authenticated?
|
472
|
+
!@auth_schemes.empty?
|
473
|
+
end
|
474
|
+
|
475
|
+
##
|
476
|
+
# Check if authenticated with a specific scheme
|
477
|
+
#
|
478
|
+
# @param scheme [String] The security scheme name
|
479
|
+
# @return [Boolean] True if authenticated with the scheme
|
480
|
+
def authenticated_with?(scheme)
|
481
|
+
@auth_schemes.key?(scheme)
|
482
|
+
end
|
483
|
+
|
484
|
+
##
|
485
|
+
# Set authentication for a scheme
|
486
|
+
#
|
487
|
+
# @param scheme [String] The security scheme name
|
488
|
+
# @param data [Object] Authentication data
|
489
|
+
def set_authentication(scheme, data)
|
490
|
+
@auth_schemes[scheme] = data
|
491
|
+
end
|
492
|
+
|
493
|
+
##
|
494
|
+
# Get authentication data for a scheme
|
495
|
+
#
|
496
|
+
# @param scheme [String] The security scheme name
|
497
|
+
# @return [Object, nil] The authentication data
|
498
|
+
def get_authentication(scheme)
|
499
|
+
@auth_schemes[scheme]
|
500
|
+
end
|
501
|
+
|
502
|
+
##
|
503
|
+
# Set user information
|
504
|
+
#
|
505
|
+
# @param user [Object] User object or identifier
|
506
|
+
def set_user(user)
|
507
|
+
@user = user
|
508
|
+
end
|
509
|
+
|
510
|
+
##
|
511
|
+
# Set session information
|
512
|
+
#
|
513
|
+
# @param session [Object] Session object or identifier
|
514
|
+
def set_session(session)
|
515
|
+
@session = session
|
516
|
+
end
|
517
|
+
|
518
|
+
##
|
519
|
+
# Set metadata
|
520
|
+
#
|
521
|
+
# @param key [String, Symbol] Metadata key
|
522
|
+
# @param value [Object] Metadata value
|
523
|
+
def set_metadata(key, value)
|
524
|
+
@metadata[key] = value
|
525
|
+
end
|
526
|
+
|
527
|
+
##
|
528
|
+
# Get metadata
|
529
|
+
#
|
530
|
+
# @param key [String, Symbol] Metadata key
|
531
|
+
# @return [Object, nil] Metadata value
|
532
|
+
def get_metadata(key)
|
533
|
+
@metadata[key]
|
534
|
+
end
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|