Class: A2A::Server::Handler
- Inherits:
-
Object
- Object
- A2A::Server::Handler
- Defined in:
- lib/a2a/server/handler.rb
Instance Attribute Summary collapse
-
#agent ⇒ Object
readonly
Returns the value of attribute agent.
-
#middleware_stack ⇒ Object
readonly
Returns the value of attribute middleware_stack.
Instance Method Summary collapse
-
#add_middleware(middleware) ⇒ Object
Add middleware to the handler.
-
#apply_middleware_stack(request, context) { ... } ⇒ Object
private
Apply the middleware stack to a request.
-
#capabilities ⇒ Array<A2A::Protocol::Capability>
Get all capabilities from the agent.
-
#find_capability_by_method(method_name) ⇒ A2A::Protocol::Capability?
Find capability by method name.
-
#handle_batch_request(requests, context: nil) ⇒ String
Handle a batch JSON-RPC request.
-
#handle_request(request_body, context: nil) ⇒ String
Handle a JSON-RPC request string.
-
#handle_single_request(request, context: nil) ⇒ String
Handle a single JSON-RPC request.
-
#initialize(agent, middleware: []) ⇒ Handler
constructor
Initialize a new request handler.
-
#method_definition(method_name) ⇒ Hash?
Get method definition.
-
#method_registered?(method_name) ⇒ Boolean
Check if a method is registered.
-
#registered_methods ⇒ Array<String>
Get all registered methods from the agent.
-
#remove_middleware(middleware) ⇒ Object
Remove middleware from the handler.
-
#route_request(request, context: nil) ⇒ Hash?
Route a request to the appropriate agent method.
-
#validate_agent! ⇒ Object
private
Validate that the agent includes the Agent module.
-
#validate_method_parameters(method_name, params) ⇒ Object
private
Validate method parameters against capability schema.
-
#validate_request(request) ⇒ Object
private
Validate a JSON-RPC request.
Constructor Details
#initialize(agent, middleware: []) ⇒ Handler
Initialize a new request handler
29 30 31 32 33 34 |
# File 'lib/a2a/server/handler.rb', line 29 def initialize(agent, middleware: []) @agent = agent @middleware_stack = middleware.dup validate_agent! end |
Instance Attribute Details
#agent ⇒ Object (readonly)
Returns the value of attribute agent.
22 23 24 |
# File 'lib/a2a/server/handler.rb', line 22 def agent @agent end |
#middleware_stack ⇒ Object (readonly)
Returns the value of attribute middleware_stack.
22 23 24 |
# File 'lib/a2a/server/handler.rb', line 22 def middleware_stack @middleware_stack end |
Instance Method Details
#add_middleware(middleware) ⇒ Object
Add middleware to the handler
153 154 155 |
# File 'lib/a2a/server/handler.rb', line 153 def add_middleware(middleware) @middleware_stack << middleware end |
#apply_middleware_stack(request, context) { ... } ⇒ Object (private)
Apply the middleware stack to a request
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/a2a/server/handler.rb', line 260 def apply_middleware_stack(request, context, &block) # Build the middleware chain from the outside in chain = block @middleware_stack.reverse_each do |middleware| current_chain = chain chain = lambda do if middleware.respond_to?(:call) middleware.call(request, context) { current_chain.call } else # Fallback for middleware that don't implement call current_chain.call end end end # Execute the chain chain.call end |
#capabilities ⇒ Array<A2A::Protocol::Capability>
Get all capabilities from the agent
195 196 197 |
# File 'lib/a2a/server/handler.rb', line 195 def capabilities @agent.class.a2a_capability_registry.all end |
#find_capability_by_method(method_name) ⇒ A2A::Protocol::Capability?
Find capability by method name
204 205 206 |
# File 'lib/a2a/server/handler.rb', line 204 def find_capability_by_method(method_name) @agent.class.a2a_capability_registry.find_by_method(method_name).first end |
#handle_batch_request(requests, context: nil) ⇒ String
Handle a batch JSON-RPC request
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/a2a/server/handler.rb', line 101 def handle_batch_request(requests, context: nil) # Process each request in the batch responses = requests.map do |request| # Apply middleware stack for each request apply_middleware_stack(request, context) do route_request(request, context: context) end rescue StandardError => e # Convert errors to error responses A2A::Errors::ErrorUtils.exception_to_json_rpc_error(e, request_id: request.id) end # Filter out nil responses (from notifications) batch_response = A2A::Protocol::JsonRpc.build_batch_response(responses.compact) # Return empty array if no responses (all notifications) batch_response.to_json end |
#handle_request(request_body, context: nil) ⇒ String
Handle a JSON-RPC request string
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/a2a/server/handler.rb', line 42 def handle_request(request_body, context: nil) A2A::Monitoring::Instrumentation.instrument_request({ method: "parse_request" }) do # Parse the JSON-RPC request parsed_request = A2A::Protocol::JsonRpc.parse_request(request_body) # Handle single or batch requests if parsed_request.is_a?(Array) handle_batch_request(parsed_request, context: context) else handle_single_request(parsed_request, context: context) end rescue A2A::Errors::A2AError => e # Return error response for A2A errors error_response = A2A::Protocol::JsonRpc.build_error_response( code: e.code, message: e., data: e.data, id: nil # Unknown ID for parse errors ) error_response.to_json rescue StandardError => e # Return internal error for unexpected errors error_response = A2A::Protocol::JsonRpc.build_error_response( code: A2A::Protocol::JsonRpc::INTERNAL_ERROR, message: "Internal server error: #{e.message}", id: nil ) error_response.to_json end end |
#handle_single_request(request, context: nil) ⇒ String
Handle a single JSON-RPC request
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/a2a/server/handler.rb', line 79 def handle_single_request(request, context: nil) # Apply middleware stack response = apply_middleware_stack(request, context) do # Route the request to the agent route_request(request, context: context) end # Convert response to JSON if response response.to_json else # No response for notifications nil end end |
#method_definition(method_name) ⇒ Hash?
Get method definition
187 188 189 |
# File 'lib/a2a/server/handler.rb', line 187 def method_definition(method_name) @agent.class.a2a_method_definition(method_name) end |
#method_registered?(method_name) ⇒ Boolean
Check if a method is registered
178 179 180 |
# File 'lib/a2a/server/handler.rb', line 178 def method_registered?(method_name) @agent.class.a2a_method_registered?(method_name) end |
#registered_methods ⇒ Array<String>
Get all registered methods from the agent
169 170 171 |
# File 'lib/a2a/server/handler.rb', line 169 def registered_methods @agent.class.a2a_method_registry.keys end |
#remove_middleware(middleware) ⇒ Object
Remove middleware from the handler
161 162 163 |
# File 'lib/a2a/server/handler.rb', line 161 def remove_middleware(middleware) @middleware_stack.delete(middleware) end |
#route_request(request, context: nil) ⇒ Hash?
Route a request to the appropriate agent method
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/a2a/server/handler.rb', line 126 def route_request(request, context: nil) # Validate the request validate_request(request) # Create or enhance context request_context = context || A2A::Server::Context.new(request: request) request_context.instance_variable_set(:@request, request) if context # Check if the method exists unless @agent.class.a2a_method_registered?(request.method) raise A2A::Errors::MethodNotFound, "Method '#{request.method}' not found" end # Get method definition for validation @agent.class.a2a_method_definition(request.method) # Validate parameters against capability schema if available validate_method_parameters(request.method, request.params) # Delegate to the agent @agent.handle_a2a_request(request, context: request_context) end |
#validate_agent! ⇒ Object (private)
Validate that the agent includes the Agent module
212 213 214 215 216 |
# File 'lib/a2a/server/handler.rb', line 212 def validate_agent! return if @agent.class.included_modules.include?(A2A::Server::Agent) raise ArgumentError, "Agent must include A2A::Server::Agent module" end |
#validate_method_parameters(method_name, params) ⇒ Object (private)
Validate method parameters against capability schema
242 243 244 245 246 247 248 249 250 251 |
# File 'lib/a2a/server/handler.rb', line 242 def validate_method_parameters(method_name, params) capability = find_capability_by_method(method_name) return unless capability # Skip validation if no capability defined begin capability.validate_input(params) rescue ArgumentError => e raise A2A::Errors::InvalidParams, "Parameter validation failed: #{e.message}" end end |
#validate_request(request) ⇒ Object (private)
Validate a JSON-RPC request
223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/a2a/server/handler.rb', line 223 def validate_request(request) raise A2A::Errors::InvalidRequest, "Invalid request object" unless request.is_a?(A2A::Protocol::Request) if request.method.nil? || (respond_to?(:empty?) && empty?) || (is_a?(String) && strip.empty?) raise A2A::Errors::InvalidRequest, "Method name is required" end return if request.params.is_a?(Hash) || request.params.is_a?(Array) raise A2A::Errors::InvalidParams, "Parameters must be an object or array" end |