simple_a2a 0.1.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +67 -0
- data/README.md +78 -38
- data/compare_agent2agent.md +460 -0
- data/docs/api/client/index.md +19 -0
- data/docs/api/index.md +4 -3
- data/docs/api/models/index.md +13 -11
- data/docs/api/server/index.md +42 -10
- data/docs/api/storage/index.md +0 -1
- data/docs/architecture/index.md +17 -15
- data/docs/architecture/protocol.md +16 -1
- data/docs/assets/images/simple_a2a.jpg +0 -0
- data/docs/examples/agent-chaining.md +107 -0
- data/docs/examples/auth-headers.md +105 -0
- data/docs/examples/cancellation.md +105 -0
- data/docs/examples/index.md +123 -52
- data/docs/examples/interrupted-states.md +114 -0
- data/docs/examples/multipart.md +103 -0
- data/docs/examples/push-notifications.md +117 -0
- data/docs/examples/resubscribe.md +129 -0
- data/docs/examples/sqlite-storage.md +131 -0
- data/docs/examples/streaming.md +1 -4
- data/docs/guides/push-notifications.md +4 -1
- data/docs/guides/streaming.md +34 -5
- data/docs/index.md +55 -27
- data/examples/04_resubscribe/client.rb +140 -0
- data/examples/04_resubscribe/server.rb +75 -0
- data/examples/05_cancellation/client.rb +150 -0
- data/examples/05_cancellation/server.rb +77 -0
- data/examples/06_push_notifications/client.rb +192 -0
- data/examples/06_push_notifications/server.rb +123 -0
- data/examples/07_agent_chaining/client.rb +120 -0
- data/examples/07_agent_chaining/server.rb +150 -0
- data/examples/08_interrupted_states/client.rb +148 -0
- data/examples/08_interrupted_states/server.rb +142 -0
- data/examples/09_multipart/client.rb +117 -0
- data/examples/09_multipart/server.rb +97 -0
- data/examples/10_auth_headers/client.rb +92 -0
- data/examples/10_auth_headers/server.rb +98 -0
- data/examples/11_sqlite_storage/Brewfile +1 -0
- data/examples/11_sqlite_storage/Gemfile +9 -0
- data/examples/11_sqlite_storage/client.rb +114 -0
- data/examples/11_sqlite_storage/run +154 -0
- data/examples/11_sqlite_storage/server.rb +131 -0
- data/examples/README.md +384 -0
- data/lib/simple_a2a/client/sse.rb +15 -0
- data/lib/simple_a2a/server/app.rb +131 -45
- data/lib/simple_a2a/server/base.rb +19 -17
- data/lib/simple_a2a/server/broadcast_registry.rb +24 -0
- data/lib/simple_a2a/server/multi_agent.rb +1 -1
- data/lib/simple_a2a/server/push_config_store.rb +29 -0
- data/lib/simple_a2a/server/push_sender.rb +1 -0
- data/lib/simple_a2a/server/task_broadcast.rb +46 -0
- data/lib/simple_a2a/version.rb +1 -1
- metadata +38 -20
- data/lib/simple_a2a/server/event_router.rb +0 -50
|
@@ -3,33 +3,35 @@
|
|
|
3
3
|
module A2A
|
|
4
4
|
module Server
|
|
5
5
|
class Base
|
|
6
|
-
attr_reader :agent_card, :executor, :storage, :
|
|
6
|
+
attr_reader :agent_card, :executor, :storage, :push_sender
|
|
7
7
|
|
|
8
8
|
def initialize(
|
|
9
9
|
agent_card:,
|
|
10
10
|
executor:,
|
|
11
|
-
storage:
|
|
12
|
-
push_sender:
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
storage: Storage::Memory.new,
|
|
12
|
+
push_sender: nil,
|
|
13
|
+
push_config_store: nil,
|
|
14
|
+
host: "localhost",
|
|
15
|
+
port: 9292
|
|
15
16
|
)
|
|
16
|
-
@agent_card
|
|
17
|
-
@executor
|
|
18
|
-
@storage
|
|
19
|
-
@
|
|
20
|
-
@
|
|
21
|
-
@host
|
|
22
|
-
@port
|
|
17
|
+
@agent_card = agent_card
|
|
18
|
+
@executor = executor
|
|
19
|
+
@storage = storage
|
|
20
|
+
@push_sender = push_sender
|
|
21
|
+
@push_config_store = push_config_store
|
|
22
|
+
@host = host
|
|
23
|
+
@port = port
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def rack_app
|
|
26
27
|
klass = Class.new(App)
|
|
27
28
|
klass.configure(
|
|
28
|
-
agent_card:
|
|
29
|
-
storage:
|
|
30
|
-
executor:
|
|
31
|
-
|
|
32
|
-
push_sender:
|
|
29
|
+
agent_card: @agent_card,
|
|
30
|
+
storage: @storage,
|
|
31
|
+
executor: @executor,
|
|
32
|
+
broadcast_registry: BroadcastRegistry.new,
|
|
33
|
+
push_sender: @push_sender,
|
|
34
|
+
push_config_store: @push_config_store
|
|
33
35
|
)
|
|
34
36
|
klass.freeze.app
|
|
35
37
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module A2A
|
|
4
|
+
module Server
|
|
5
|
+
class BroadcastRegistry
|
|
6
|
+
def initialize
|
|
7
|
+
@broadcasts = {}
|
|
8
|
+
@mutex = Mutex.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def register(task_id, broadcast)
|
|
12
|
+
@mutex.synchronize { @broadcasts[task_id] = broadcast }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def unregister(task_id)
|
|
16
|
+
@mutex.synchronize { @broadcasts.delete(task_id) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def find(task_id)
|
|
20
|
+
@mutex.synchronize { @broadcasts[task_id] }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -40,7 +40,7 @@ module A2A
|
|
|
40
40
|
agent_card: cfg[:agent_card],
|
|
41
41
|
storage: cfg[:storage] || Storage::Memory.new,
|
|
42
42
|
executor: cfg[:executor],
|
|
43
|
-
|
|
43
|
+
broadcast_registry: BroadcastRegistry.new,
|
|
44
44
|
push_sender: cfg[:push_sender]
|
|
45
45
|
)
|
|
46
46
|
klass.freeze.app
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module A2A
|
|
4
|
+
module Server
|
|
5
|
+
class PushConfigStore
|
|
6
|
+
def initialize
|
|
7
|
+
@configs = {}
|
|
8
|
+
@mutex = Mutex.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def set(task_id, config)
|
|
12
|
+
@mutex.synchronize { @configs[task_id] = config }
|
|
13
|
+
config
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get(task_id)
|
|
17
|
+
@mutex.synchronize { @configs[task_id] }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def delete(task_id)
|
|
21
|
+
@mutex.synchronize { @configs.delete(task_id) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def list
|
|
25
|
+
@mutex.synchronize { @configs.dup }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ractor_queue"
|
|
4
|
+
|
|
5
|
+
module A2A
|
|
6
|
+
module Server
|
|
7
|
+
class TaskBroadcast
|
|
8
|
+
DONE = :done.freeze
|
|
9
|
+
|
|
10
|
+
BroadcastError = Struct.new(:message)
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@queues = []
|
|
14
|
+
@mutex = Mutex.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def subscribe(capacity: 64)
|
|
18
|
+
RactorQueue.new(capacity: capacity).tap do |q|
|
|
19
|
+
@mutex.synchronize { @queues << q }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def unsubscribe(queue)
|
|
24
|
+
@mutex.synchronize { @queues.delete(queue) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Duck-type compatible with the old EventRouter interface.
|
|
28
|
+
# task_id is accepted but ignored — the broadcast is already task-scoped.
|
|
29
|
+
def publish(_task_id, event)
|
|
30
|
+
snapshot = @mutex.synchronize { @queues.dup }
|
|
31
|
+
snapshot.each { |q| q.async_push(event) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def error(message)
|
|
35
|
+
ev = BroadcastError.new(message)
|
|
36
|
+
snapshot = @mutex.synchronize { @queues.dup }
|
|
37
|
+
snapshot.each { |q| q.async_push(ev) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def close
|
|
41
|
+
snapshot = @mutex.synchronize { @queues.dup }
|
|
42
|
+
snapshot.each { |q| q.async_push(DONE) }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/simple_a2a/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simple_a2a
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dewayne VanHoozer
|
|
@@ -122,33 +122,19 @@ dependencies:
|
|
|
122
122
|
- !ruby/object:Gem::Version
|
|
123
123
|
version: '2.0'
|
|
124
124
|
- !ruby/object:Gem::Dependency
|
|
125
|
-
name:
|
|
125
|
+
name: ractor_queue
|
|
126
126
|
requirement: !ruby/object:Gem::Requirement
|
|
127
127
|
requirements:
|
|
128
128
|
- - "~>"
|
|
129
129
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '0.
|
|
130
|
+
version: '0.2'
|
|
131
131
|
type: :runtime
|
|
132
132
|
prerelease: false
|
|
133
133
|
version_requirements: !ruby/object:Gem::Requirement
|
|
134
134
|
requirements:
|
|
135
135
|
- - "~>"
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
|
-
version: '0.
|
|
138
|
-
- !ruby/object:Gem::Dependency
|
|
139
|
-
name: typed_bus
|
|
140
|
-
requirement: !ruby/object:Gem::Requirement
|
|
141
|
-
requirements:
|
|
142
|
-
- - "~>"
|
|
143
|
-
- !ruby/object:Gem::Version
|
|
144
|
-
version: '0.0'
|
|
145
|
-
type: :runtime
|
|
146
|
-
prerelease: false
|
|
147
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
-
requirements:
|
|
149
|
-
- - "~>"
|
|
150
|
-
- !ruby/object:Gem::Version
|
|
151
|
-
version: '0.0'
|
|
137
|
+
version: '0.2'
|
|
152
138
|
- !ruby/object:Gem::Dependency
|
|
153
139
|
name: rake
|
|
154
140
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -248,7 +234,7 @@ dependencies:
|
|
|
248
234
|
- !ruby/object:Gem::Version
|
|
249
235
|
version: '0'
|
|
250
236
|
description: Client and server for the A2A protocol — async-first, Rack-compatible,
|
|
251
|
-
built on Falcon
|
|
237
|
+
built on Falcon.
|
|
252
238
|
email:
|
|
253
239
|
- dvanhoozer@gmail.com
|
|
254
240
|
executables: []
|
|
@@ -260,6 +246,7 @@ files:
|
|
|
260
246
|
- LICENSE.txt
|
|
261
247
|
- README.md
|
|
262
248
|
- Rakefile
|
|
249
|
+
- compare_agent2agent.md
|
|
263
250
|
- docs/api/client/index.md
|
|
264
251
|
- docs/api/index.md
|
|
265
252
|
- docs/api/models/index.md
|
|
@@ -268,9 +255,18 @@ files:
|
|
|
268
255
|
- docs/architecture/index.md
|
|
269
256
|
- docs/architecture/protocol.md
|
|
270
257
|
- docs/assets/css/custom.css
|
|
258
|
+
- docs/assets/images/simple_a2a.jpg
|
|
259
|
+
- docs/examples/agent-chaining.md
|
|
260
|
+
- docs/examples/auth-headers.md
|
|
271
261
|
- docs/examples/basic-usage.md
|
|
262
|
+
- docs/examples/cancellation.md
|
|
272
263
|
- docs/examples/index.md
|
|
264
|
+
- docs/examples/interrupted-states.md
|
|
273
265
|
- docs/examples/llm-research.md
|
|
266
|
+
- docs/examples/multipart.md
|
|
267
|
+
- docs/examples/push-notifications.md
|
|
268
|
+
- docs/examples/resubscribe.md
|
|
269
|
+
- docs/examples/sqlite-storage.md
|
|
274
270
|
- docs/examples/streaming.md
|
|
275
271
|
- docs/getting-started/installation.md
|
|
276
272
|
- docs/getting-started/quick-start.md
|
|
@@ -286,6 +282,26 @@ files:
|
|
|
286
282
|
- examples/03_llm_research/run
|
|
287
283
|
- examples/03_llm_research/server.rb
|
|
288
284
|
- examples/03_llm_research/web_client.rb
|
|
285
|
+
- examples/04_resubscribe/client.rb
|
|
286
|
+
- examples/04_resubscribe/server.rb
|
|
287
|
+
- examples/05_cancellation/client.rb
|
|
288
|
+
- examples/05_cancellation/server.rb
|
|
289
|
+
- examples/06_push_notifications/client.rb
|
|
290
|
+
- examples/06_push_notifications/server.rb
|
|
291
|
+
- examples/07_agent_chaining/client.rb
|
|
292
|
+
- examples/07_agent_chaining/server.rb
|
|
293
|
+
- examples/08_interrupted_states/client.rb
|
|
294
|
+
- examples/08_interrupted_states/server.rb
|
|
295
|
+
- examples/09_multipart/client.rb
|
|
296
|
+
- examples/09_multipart/server.rb
|
|
297
|
+
- examples/10_auth_headers/client.rb
|
|
298
|
+
- examples/10_auth_headers/server.rb
|
|
299
|
+
- examples/11_sqlite_storage/Brewfile
|
|
300
|
+
- examples/11_sqlite_storage/Gemfile
|
|
301
|
+
- examples/11_sqlite_storage/client.rb
|
|
302
|
+
- examples/11_sqlite_storage/run
|
|
303
|
+
- examples/11_sqlite_storage/server.rb
|
|
304
|
+
- examples/README.md
|
|
289
305
|
- examples/common_config.rb
|
|
290
306
|
- examples/run
|
|
291
307
|
- lib/simple_a2a.rb
|
|
@@ -315,12 +331,14 @@ files:
|
|
|
315
331
|
- lib/simple_a2a/server/agent_executor.rb
|
|
316
332
|
- lib/simple_a2a/server/app.rb
|
|
317
333
|
- lib/simple_a2a/server/base.rb
|
|
334
|
+
- lib/simple_a2a/server/broadcast_registry.rb
|
|
318
335
|
- lib/simple_a2a/server/context.rb
|
|
319
|
-
- lib/simple_a2a/server/event_router.rb
|
|
320
336
|
- lib/simple_a2a/server/falcon_runner.rb
|
|
321
337
|
- lib/simple_a2a/server/multi_agent.rb
|
|
338
|
+
- lib/simple_a2a/server/push_config_store.rb
|
|
322
339
|
- lib/simple_a2a/server/push_sender.rb
|
|
323
340
|
- lib/simple_a2a/server/resume_context.rb
|
|
341
|
+
- lib/simple_a2a/server/task_broadcast.rb
|
|
324
342
|
- lib/simple_a2a/storage/base.rb
|
|
325
343
|
- lib/simple_a2a/storage/memory.rb
|
|
326
344
|
- lib/simple_a2a/version.rb
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "typed_bus"
|
|
4
|
-
|
|
5
|
-
module A2A
|
|
6
|
-
module Server
|
|
7
|
-
class EventRouter
|
|
8
|
-
def initialize
|
|
9
|
-
@bus = TypedBus::MessageBus.new
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def open(task_id)
|
|
13
|
-
return if @bus.channel?(task_id.to_sym)
|
|
14
|
-
@bus.add_channel(task_id.to_sym, type: nil, timeout: nil)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def close(task_id)
|
|
18
|
-
@bus.remove_channel(task_id.to_sym)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def publish(task_id, event)
|
|
22
|
-
sym = task_id.to_sym
|
|
23
|
-
open(task_id) unless @bus.channel?(sym)
|
|
24
|
-
@bus.publish(sym, event)
|
|
25
|
-
rescue ArgumentError
|
|
26
|
-
nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def subscribe(task_id, &block)
|
|
30
|
-
sym = task_id.to_sym
|
|
31
|
-
open(task_id) unless @bus.channel?(sym)
|
|
32
|
-
@bus.subscribe(sym) do |delivery|
|
|
33
|
-
block.call(delivery.message)
|
|
34
|
-
delivery.ack!
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def unsubscribe(task_id, id_or_block)
|
|
39
|
-
return unless @bus.channel?(task_id.to_sym)
|
|
40
|
-
@bus.unsubscribe(task_id.to_sym, id_or_block)
|
|
41
|
-
rescue ArgumentError
|
|
42
|
-
nil
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def channel?(task_id)
|
|
46
|
-
@bus.channel?(task_id.to_sym)
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|