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,279 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "request_context"
|
4
|
+
require_relative "../events/event_queue"
|
5
|
+
|
6
|
+
module A2A
|
7
|
+
module Server
|
8
|
+
module AgentExecution
|
9
|
+
##
|
10
|
+
# Abstract base class for agent executors
|
11
|
+
#
|
12
|
+
# Agent executors contain the core logic of the agent, executing tasks based on
|
13
|
+
# requests and publishing updates to an event queue. This mirrors the Python
|
14
|
+
# AgentExecutor interface.
|
15
|
+
#
|
16
|
+
class AgentExecutor
|
17
|
+
##
|
18
|
+
# Execute the agent's logic for a given request context
|
19
|
+
#
|
20
|
+
# The agent should read necessary information from the context and
|
21
|
+
# publish Task or Message events, or TaskStatusUpdateEvent/TaskArtifactUpdateEvent
|
22
|
+
# to the event_queue. This method should return once the agent's execution
|
23
|
+
# for this request is complete or yields control (e.g., enters an input-required state).
|
24
|
+
#
|
25
|
+
# @param context [RequestContext] The request context containing the message, task ID, etc.
|
26
|
+
# @param event_queue [A2A::Server::Events::EventQueue] The queue to publish events to
|
27
|
+
# @abstract Subclasses must implement this method
|
28
|
+
def execute(context, event_queue)
|
29
|
+
raise NotImplementedError, "Subclasses must implement execute"
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Request the agent to cancel an ongoing task
|
34
|
+
#
|
35
|
+
# The agent should attempt to stop the task identified by the task_id
|
36
|
+
# in the context and publish a TaskStatusUpdateEvent with state 'canceled'
|
37
|
+
# to the event_queue.
|
38
|
+
#
|
39
|
+
# @param context [RequestContext] The request context containing the task ID to cancel
|
40
|
+
# @param event_queue [A2A::Server::Events::EventQueue] The queue to publish the cancellation status update to
|
41
|
+
# @abstract Subclasses must implement this method
|
42
|
+
def cancel(context, event_queue)
|
43
|
+
raise NotImplementedError, "Subclasses must implement cancel"
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
##
|
49
|
+
# Helper method to publish a task status update event
|
50
|
+
#
|
51
|
+
# @param event_queue [A2A::Server::Events::EventQueue] The event queue
|
52
|
+
# @param task_id [String] The task ID
|
53
|
+
# @param context_id [String] The context ID
|
54
|
+
# @param status [A2A::Types::TaskStatus] The new task status
|
55
|
+
# @param metadata [Hash, nil] Optional metadata
|
56
|
+
def publish_task_status_update(event_queue, task_id, context_id, status, metadata = nil)
|
57
|
+
event_data = A2A::Types::TaskStatusUpdateEvent.new(
|
58
|
+
task_id: task_id,
|
59
|
+
context_id: context_id,
|
60
|
+
status: status,
|
61
|
+
metadata: metadata
|
62
|
+
)
|
63
|
+
|
64
|
+
event = A2A::Server::Events::Event.new(
|
65
|
+
type: "task_status_update",
|
66
|
+
data: event_data
|
67
|
+
)
|
68
|
+
|
69
|
+
event_queue.publish(event)
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Helper method to publish a task artifact update event
|
74
|
+
#
|
75
|
+
# @param event_queue [A2A::Server::Events::EventQueue] The event queue
|
76
|
+
# @param task_id [String] The task ID
|
77
|
+
# @param context_id [String] The context ID
|
78
|
+
# @param artifact [A2A::Types::Artifact] The artifact
|
79
|
+
# @param append [Boolean] Whether this is an append operation
|
80
|
+
# @param metadata [Hash, nil] Optional metadata
|
81
|
+
def publish_task_artifact_update(event_queue, task_id, context_id, artifact, append = false, metadata = nil)
|
82
|
+
event_data = A2A::Types::TaskArtifactUpdateEvent.new(
|
83
|
+
task_id: task_id,
|
84
|
+
context_id: context_id,
|
85
|
+
artifact: artifact,
|
86
|
+
append: append,
|
87
|
+
metadata: metadata
|
88
|
+
)
|
89
|
+
|
90
|
+
event = A2A::Server::Events::Event.new(
|
91
|
+
type: "task_artifact_update",
|
92
|
+
data: event_data
|
93
|
+
)
|
94
|
+
|
95
|
+
event_queue.publish(event)
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Helper method to publish a task object
|
100
|
+
#
|
101
|
+
# @param event_queue [A2A::Server::Events::EventQueue] The event queue
|
102
|
+
# @param task [A2A::Types::Task] The task object
|
103
|
+
def publish_task(event_queue, task)
|
104
|
+
event = A2A::Server::Events::Event.new(
|
105
|
+
type: "task",
|
106
|
+
data: task
|
107
|
+
)
|
108
|
+
|
109
|
+
event_queue.publish(event)
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Helper method to publish a message object
|
114
|
+
#
|
115
|
+
# @param event_queue [A2A::Server::Events::EventQueue] The event queue
|
116
|
+
# @param message [A2A::Types::Message] The message object
|
117
|
+
def publish_message(event_queue, message)
|
118
|
+
event = A2A::Server::Events::Event.new(
|
119
|
+
type: "message",
|
120
|
+
data: message
|
121
|
+
)
|
122
|
+
|
123
|
+
event_queue.publish(event)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Simple agent executor implementation
|
129
|
+
#
|
130
|
+
# A basic implementation that can be used as a starting point for custom agents.
|
131
|
+
# Provides default behavior for task creation and status management.
|
132
|
+
#
|
133
|
+
class SimpleAgentExecutor < AgentExecutor
|
134
|
+
def initialize(agent_card: nil, task_manager: nil)
|
135
|
+
@agent_card = agent_card
|
136
|
+
@task_manager = task_manager
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Execute a request by creating a task and processing the message
|
141
|
+
#
|
142
|
+
# @param context [RequestContext] The request context
|
143
|
+
# @param event_queue [A2A::Server::Events::EventQueue] The event queue
|
144
|
+
def execute(context, event_queue)
|
145
|
+
# Create or get the task
|
146
|
+
task = ensure_task(context)
|
147
|
+
publish_task(event_queue, task)
|
148
|
+
|
149
|
+
# Update task to working state
|
150
|
+
working_status = A2A::Types::TaskStatus.new(
|
151
|
+
state: A2A::Types::TASK_STATE_WORKING,
|
152
|
+
message: "Processing request",
|
153
|
+
updated_at: Time.now.utc.iso8601
|
154
|
+
)
|
155
|
+
|
156
|
+
publish_task_status_update(
|
157
|
+
event_queue,
|
158
|
+
task.id,
|
159
|
+
task.context_id,
|
160
|
+
working_status
|
161
|
+
)
|
162
|
+
|
163
|
+
# Process the message (delegate to subclass)
|
164
|
+
result = process_message(context.message, task, context)
|
165
|
+
|
166
|
+
# Update task with result
|
167
|
+
completed_status = A2A::Types::TaskStatus.new(
|
168
|
+
state: A2A::Types::TASK_STATE_COMPLETED,
|
169
|
+
result: result,
|
170
|
+
updated_at: Time.now.utc.iso8601
|
171
|
+
)
|
172
|
+
|
173
|
+
publish_task_status_update(
|
174
|
+
event_queue,
|
175
|
+
task.id,
|
176
|
+
task.context_id,
|
177
|
+
completed_status
|
178
|
+
)
|
179
|
+
rescue StandardError => e
|
180
|
+
# Handle errors by updating task status
|
181
|
+
if task
|
182
|
+
error_status = A2A::Types::TaskStatus.new(
|
183
|
+
state: A2A::Types::TASK_STATE_FAILED,
|
184
|
+
error: { message: e.message, type: e.class.name },
|
185
|
+
updated_at: Time.now.utc.iso8601
|
186
|
+
)
|
187
|
+
|
188
|
+
publish_task_status_update(
|
189
|
+
event_queue,
|
190
|
+
task.id,
|
191
|
+
task.context_id,
|
192
|
+
error_status
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
raise
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# Cancel a task by updating its status
|
201
|
+
#
|
202
|
+
# @param context [RequestContext] The request context
|
203
|
+
# @param event_queue [A2A::Server::Events::EventQueue] The event queue
|
204
|
+
def cancel(context, event_queue)
|
205
|
+
task_id = context.task_id
|
206
|
+
return unless task_id
|
207
|
+
|
208
|
+
canceled_status = A2A::Types::TaskStatus.new(
|
209
|
+
state: A2A::Types::TASK_STATE_CANCELED,
|
210
|
+
message: "Task canceled by request",
|
211
|
+
updated_at: Time.now.utc.iso8601
|
212
|
+
)
|
213
|
+
|
214
|
+
publish_task_status_update(
|
215
|
+
event_queue,
|
216
|
+
task_id,
|
217
|
+
context.context_id,
|
218
|
+
canceled_status
|
219
|
+
)
|
220
|
+
end
|
221
|
+
|
222
|
+
protected
|
223
|
+
|
224
|
+
##
|
225
|
+
# Process a message (to be implemented by subclasses)
|
226
|
+
#
|
227
|
+
# @param message [A2A::Types::Message] The message to process
|
228
|
+
# @param task [A2A::Types::Task] The associated task
|
229
|
+
# @param context [RequestContext] The request context
|
230
|
+
# @return [Object] Processing result
|
231
|
+
def process_message(message, _task, _context)
|
232
|
+
# Default implementation just echoes the message
|
233
|
+
{
|
234
|
+
echo: message.to_h,
|
235
|
+
processed_at: Time.now.utc.iso8601
|
236
|
+
}
|
237
|
+
end
|
238
|
+
|
239
|
+
private
|
240
|
+
|
241
|
+
##
|
242
|
+
# Ensure a task exists for the request context
|
243
|
+
#
|
244
|
+
# @param context [RequestContext] The request context
|
245
|
+
# @return [A2A::Types::Task] The task object
|
246
|
+
def ensure_task(context)
|
247
|
+
if context.task_id && @task_manager
|
248
|
+
# Try to get existing task
|
249
|
+
existing_task = begin
|
250
|
+
@task_manager.get_task(context.task_id)
|
251
|
+
rescue StandardError
|
252
|
+
nil
|
253
|
+
end
|
254
|
+
return existing_task if existing_task
|
255
|
+
end
|
256
|
+
|
257
|
+
# Create new task
|
258
|
+
task_id = context.task_id || SecureRandom.uuid
|
259
|
+
context_id = context.context_id || SecureRandom.uuid
|
260
|
+
|
261
|
+
A2A::Types::Task.new(
|
262
|
+
id: task_id,
|
263
|
+
context_id: context_id,
|
264
|
+
status: A2A::Types::TaskStatus.new(
|
265
|
+
state: A2A::Types::TASK_STATE_SUBMITTED,
|
266
|
+
message: "Task created",
|
267
|
+
updated_at: Time.now.utc.iso8601
|
268
|
+
),
|
269
|
+
history: context.message ? [context.message] : [],
|
270
|
+
metadata: {
|
271
|
+
created_at: Time.now.utc.iso8601,
|
272
|
+
executor: self.class.name
|
273
|
+
}
|
274
|
+
)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module A2A
|
4
|
+
module Server
|
5
|
+
module AgentExecution
|
6
|
+
##
|
7
|
+
# Request context for agent execution
|
8
|
+
#
|
9
|
+
# Contains all the information needed for an agent to process a request,
|
10
|
+
# including the message, task ID, context ID, and server call context.
|
11
|
+
#
|
12
|
+
class RequestContext
|
13
|
+
attr_reader :message, :task_id, :context_id, :server_context, :metadata
|
14
|
+
|
15
|
+
##
|
16
|
+
# Initialize a new request context
|
17
|
+
#
|
18
|
+
# @param message [A2A::Types::Message, nil] The message that initiated the request
|
19
|
+
# @param task_id [String, nil] The task ID if continuing an existing task
|
20
|
+
# @param context_id [String, nil] The context ID for the conversation
|
21
|
+
# @param server_context [A2A::Server::Context, nil] The server call context
|
22
|
+
# @param metadata [Hash] Additional metadata for the request
|
23
|
+
def initialize(message: nil, task_id: nil, context_id: nil, server_context: nil, metadata: {})
|
24
|
+
@message = message
|
25
|
+
@task_id = task_id
|
26
|
+
@context_id = context_id || message&.context_id
|
27
|
+
@server_context = server_context
|
28
|
+
@metadata = metadata.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Check if this is a new task (no task_id provided)
|
33
|
+
#
|
34
|
+
# @return [Boolean] True if this is a new task
|
35
|
+
def new_task?
|
36
|
+
@task_id.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Check if this is continuing an existing task
|
41
|
+
#
|
42
|
+
# @return [Boolean] True if continuing an existing task
|
43
|
+
def continuing_task?
|
44
|
+
!@task_id.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Check if this request has a message
|
49
|
+
#
|
50
|
+
# @return [Boolean] True if a message is present
|
51
|
+
def has_message?
|
52
|
+
!@message.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Get the user from the server context if available
|
57
|
+
#
|
58
|
+
# @return [Object, nil] The user object or nil
|
59
|
+
def user
|
60
|
+
@server_context&.user
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Check if the request is authenticated
|
65
|
+
#
|
66
|
+
# @return [Boolean] True if authenticated
|
67
|
+
def authenticated?
|
68
|
+
@server_context&.authenticated? || false
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Get authentication data for a specific scheme
|
73
|
+
#
|
74
|
+
# @param scheme [String] The authentication scheme
|
75
|
+
# @return [Object, nil] Authentication data or nil
|
76
|
+
def authentication(scheme)
|
77
|
+
@server_context&.get_authentication(scheme)
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Get metadata value
|
82
|
+
#
|
83
|
+
# @param key [String, Symbol] The metadata key
|
84
|
+
# @return [Object, nil] The metadata value or nil
|
85
|
+
def get_metadata(key)
|
86
|
+
@metadata[key] || @server_context&.get_metadata(key)
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Set metadata value
|
91
|
+
#
|
92
|
+
# @param key [String, Symbol] The metadata key
|
93
|
+
# @param value [Object] The metadata value
|
94
|
+
def set_metadata(key, value)
|
95
|
+
@metadata[key] = value
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Convert to hash representation
|
100
|
+
#
|
101
|
+
# @return [Hash] Hash representation of the context
|
102
|
+
def to_h
|
103
|
+
{
|
104
|
+
message: @message&.to_h,
|
105
|
+
task_id: @task_id,
|
106
|
+
context_id: @context_id,
|
107
|
+
metadata: @metadata,
|
108
|
+
authenticated: authenticated?,
|
109
|
+
user: user&.to_s
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Create a copy of this context with modifications
|
115
|
+
#
|
116
|
+
# @param **changes [Hash] Changes to apply
|
117
|
+
# @return [RequestContext] New context with changes applied
|
118
|
+
def with(**changes)
|
119
|
+
RequestContext.new(
|
120
|
+
message: changes[:message] || @message,
|
121
|
+
task_id: changes[:task_id] || @task_id,
|
122
|
+
context_id: changes[:context_id] || @context_id,
|
123
|
+
server_context: changes[:server_context] || @server_context,
|
124
|
+
metadata: @metadata.merge(changes[:metadata] || {})
|
125
|
+
)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Builder for creating request contexts from various inputs
|
131
|
+
#
|
132
|
+
# Provides a convenient way to build RequestContext objects from
|
133
|
+
# different types of input (JSON-RPC requests, HTTP requests, etc.)
|
134
|
+
#
|
135
|
+
class RequestContextBuilder
|
136
|
+
##
|
137
|
+
# Build a request context from a message send request
|
138
|
+
#
|
139
|
+
# @param params [Hash] The message send parameters
|
140
|
+
# @param server_context [A2A::Server::Context, nil] The server context
|
141
|
+
# @return [RequestContext] The built request context
|
142
|
+
def self.from_message_send(params, server_context = nil)
|
143
|
+
message_data = params["message"] || params[:message]
|
144
|
+
task_id = params["taskId"] || params[:task_id]
|
145
|
+
context_id = params["contextId"] || params[:context_id]
|
146
|
+
|
147
|
+
message = if message_data.is_a?(A2A::Types::Message)
|
148
|
+
message_data
|
149
|
+
elsif message_data.is_a?(Hash)
|
150
|
+
A2A::Types::Message.from_h(message_data)
|
151
|
+
else
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
|
155
|
+
# Extract context_id from message if not provided in params
|
156
|
+
context_id ||= message&.context_id
|
157
|
+
|
158
|
+
RequestContext.new(
|
159
|
+
message: message,
|
160
|
+
task_id: task_id,
|
161
|
+
context_id: context_id,
|
162
|
+
server_context: server_context,
|
163
|
+
metadata: {
|
164
|
+
request_type: "message_send",
|
165
|
+
params: params
|
166
|
+
}
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Build a request context from a task operation request
|
172
|
+
#
|
173
|
+
# @param params [Hash] The task operation parameters
|
174
|
+
# @param server_context [A2A::Server::Context, nil] The server context
|
175
|
+
# @param operation [String] The operation type (e.g., 'get', 'cancel')
|
176
|
+
# @return [RequestContext] The built request context
|
177
|
+
def self.from_task_operation(params, server_context = nil, operation: "get")
|
178
|
+
task_id = params["id"] || params[:id] || params["taskId"] || params[:task_id]
|
179
|
+
context_id = params["contextId"] || params[:context_id]
|
180
|
+
|
181
|
+
RequestContext.new(
|
182
|
+
task_id: task_id,
|
183
|
+
context_id: context_id,
|
184
|
+
server_context: server_context,
|
185
|
+
metadata: {
|
186
|
+
request_type: "task_#{operation}",
|
187
|
+
params: params
|
188
|
+
}
|
189
|
+
)
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Build a request context from a streaming message request
|
194
|
+
#
|
195
|
+
# @param params [Hash] The streaming message parameters
|
196
|
+
# @param server_context [A2A::Server::Context, nil] The server context
|
197
|
+
# @return [RequestContext] The built request context
|
198
|
+
def self.from_streaming_message(params, server_context = nil)
|
199
|
+
context = from_message_send(params, server_context)
|
200
|
+
context.set_metadata(:streaming, true)
|
201
|
+
context.set_metadata(:request_type, "message_stream")
|
202
|
+
context
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Build a request context from a task resubscription request
|
207
|
+
#
|
208
|
+
# @param params [Hash] The resubscription parameters
|
209
|
+
# @param server_context [A2A::Server::Context, nil] The server context
|
210
|
+
# @return [RequestContext] The built request context
|
211
|
+
def self.from_task_resubscription(params, server_context = nil)
|
212
|
+
context = from_task_operation(params, server_context, operation: "resubscribe")
|
213
|
+
context.set_metadata(:streaming, true)
|
214
|
+
context
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|