Class: A2A::Protocol::JsonRpc
- Inherits:
-
Object
- Object
- A2A::Protocol::JsonRpc
- Defined in:
- lib/a2a/protocol/json_rpc.rb
Overview
JSON-RPC 2.0 implementation for A2A protocol
This class provides parsing and building functionality for JSON-RPC 2.0 requests and responses, including support for batch requests and proper error handling. Optimized for performance with optional Oj JSON parser support.
Constant Summary collapse
- JSONRPC_VERSION =
JSON-RPC 2.0 version string
"2.0"- PARSE_ERROR =
Standard JSON-RPC error codes
-32_700- INVALID_REQUEST =
-32_600- METHOD_NOT_FOUND =
-32_601- INVALID_PARAMS =
-32_602- INTERNAL_ERROR =
-32_603- TASK_NOT_FOUND =
A2A-specific error codes
-32_001- TASK_NOT_CANCELABLE =
-32_002- INVALID_TASK_STATE =
-32_003- AUTHENTICATION_REQUIRED =
-32_004- AUTHORIZATION_FAILED =
-32_005- RATE_LIMIT_EXCEEDED =
-32_006- AGENT_UNAVAILABLE =
-32_007- PROTOCOL_VERSION_MISMATCH =
-32_008- CAPABILITY_NOT_SUPPORTED =
-32_009- RESOURCE_EXHAUSTED =
-32_010
Class Method Summary collapse
-
.build_batch_response(responses) ⇒ Array<Hash>
Build a JSON-RPC 2.0 batch response.
-
.build_error_response(code:, message:, id:, data: nil) ⇒ Hash
Build an error response.
-
.build_response(id:, **kwargs) ⇒ Hash
Build a JSON-RPC 2.0 response.
-
.generate_json(object) ⇒ String
Generate JSON string using optimized generator if available.
- .normalize_error(error) ⇒ Object private
-
.parse_json(json_string) ⇒ Hash, Array
Parse JSON string using optimized parser if available.
-
.parse_request(json_string) ⇒ Request+
Parse a JSON-RPC 2.0 request from JSON string.
- .parse_single_request(hash) ⇒ Object private
-
.valid_request?(hash) ⇒ Boolean
Check if a hash represents a valid JSON-RPC 2.0 request.
Class Method Details
.build_batch_response(responses) ⇒ Array<Hash>
Build a JSON-RPC 2.0 batch response
138 139 140 141 |
# File 'lib/a2a/protocol/json_rpc.rb', line 138 def self.build_batch_response(responses) # Filter out notification responses (id: nil) responses.reject { |resp| resp[:id].nil? } end |
.build_error_response(code:, message:, id:, data: nil) ⇒ Hash
Build an error response
151 152 153 154 155 156 |
# File 'lib/a2a/protocol/json_rpc.rb', line 151 def self.build_error_response(code:, message:, id:, data: nil) error = { code: code, message: } error[:data] = data if data build_response(error: error, id: id) end |
.build_response(id:, **kwargs) ⇒ Hash
Build a JSON-RPC 2.0 response
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/a2a/protocol/json_rpc.rb', line 112 def self.build_response(id:, **kwargs) result_provided = kwargs.key?(:result) error_provided = kwargs.key?(:error) raise ArgumentError, "Cannot specify both result and error" if result_provided && error_provided raise ArgumentError, "Must specify either result or error" unless result_provided || error_provided response = { jsonrpc: JSONRPC_VERSION, id: id } if error_provided response[:error] = normalize_error(kwargs[:error]) else response[:result] = kwargs[:result] end response end |
.generate_json(object) ⇒ String
Generate JSON string using optimized generator if available
64 65 66 67 68 69 70 |
# File 'lib/a2a/protocol/json_rpc.rb', line 64 def self.generate_json(object) if OJ_AVAILABLE Oj.dump(object, mode: :compat) else JSON.generate(object) end end |
.normalize_error(error) ⇒ Object (private)
190 191 192 193 194 195 196 197 198 |
# File 'lib/a2a/protocol/json_rpc.rb', line 190 private_class_method def self.normalize_error(error) if error.is_a?(A2A::Errors::A2AError) error.to_json_rpc_error elsif error.is_a?(Hash) error else { code: INTERNAL_ERROR, message: error.to_s } end end |
.parse_json(json_string) ⇒ Hash, Array
Parse JSON string using optimized parser if available
51 52 53 54 55 56 57 |
# File 'lib/a2a/protocol/json_rpc.rb', line 51 def self.parse_json(json_string) if OJ_AVAILABLE Oj.load(json_string, mode: :strict, symbol_keys: false) else JSON.parse(json_string) end end |
.parse_request(json_string) ⇒ Request+
Parse a JSON-RPC 2.0 request from JSON string
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/a2a/protocol/json_rpc.rb', line 79 def self.parse_request(json_string) # Performance optimization: early return for empty strings return nil if json_string.nil? || (json_string.respond_to?(:empty?) && json_string.empty?) # Ensure we have a string json_string = json_string.to_s unless json_string.is_a?(String) begin # Use optimized JSON parser if available parsed = parse_json(json_string) rescue JSON::ParserError, Oj::ParseError => e raise A2A::Errors::ParseError, "Invalid JSON: #{e.message}" end if parsed.is_a?(Array) # Batch request raise A2A::Errors::InvalidRequest, "Empty batch request" if parsed.empty? # Performance optimization: use map! for in-place modification parsed.map! { |req| parse_single_request(req) } else # Single request parse_single_request(parsed) end end |
.parse_single_request(hash) ⇒ Object (private)
179 180 181 182 183 184 185 186 187 188 |
# File 'lib/a2a/protocol/json_rpc.rb', line 179 private_class_method def self.parse_single_request(hash) raise A2A::Errors::InvalidRequest, "Invalid request format" unless valid_request?(hash) Request.new( jsonrpc: hash["jsonrpc"], method: hash["method"], params: hash["params"] || {}, id: hash["id"] ) end |
.valid_request?(hash) ⇒ Boolean
Check if a hash represents a valid JSON-RPC 2.0 request
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/a2a/protocol/json_rpc.rb', line 163 def self.valid_request?(hash) return false unless hash.is_a?(Hash) return false unless hash["jsonrpc"] == JSONRPC_VERSION return false unless hash["method"].is_a?(String) # id can be string, number, or null (for notifications) id = hash["id"] return false unless id.nil? || id.is_a?(String) || id.is_a?(Integer) # params is optional but must be object or array if present params = hash["params"] return false if params && !params.is_a?(Hash) && !params.is_a?(Array) true end |