phronomy 0.21.0 → 0.22.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1da8f29678594e3b2c0b8db70e6485f3576ffe72bc19db0dd340abefbdc3118b
4
- data.tar.gz: 8dbf2cacbca536c99b7552f3494edf208986b9e45c3817fac700da445d27d2c3
3
+ metadata.gz: ce5a4ac39be13ef17ca1c7cd5d812b0ccdd8ea8523fdf2e59ee8f7e78e2830fa
4
+ data.tar.gz: 3c943cd7b11ddd1bbe2e22fdd16f3855564361c3dba093d39f5f32d193850b5a
5
5
  SHA512:
6
- metadata.gz: b513420381c3eb137470d834baca90938daded756b6d3e480eed295c592b9ada062f565eaf0a77c175075378fc747080aa7c8b6bf3ccd72da22dd0c4037e2608
7
- data.tar.gz: 6cea982e98ef21605ff8b55de55a6693fd2b3c3002c0212c8c9c4912f68c5bbd74204669cea3658ec7c174f506e19bda5b5f1084cccee78fa49615b181863941
6
+ metadata.gz: e41fcf5aee7eaed49f3ef13c7399c787c281232909b07d1fa2a2e14b4a4d443222280ead723e885faeea0534e9173c036e16e90e6af04de4a440f0a76020e2c1
7
+ data.tar.gz: f0dbf8fa8947760cafbc530226a021b009acf3f0be37ddee08a5404087422b5508cd5df83ab50456b43fa8f1eb57a889f8a1974372d95b98edcad1752ddda562
data/README.md CHANGED
@@ -91,6 +91,17 @@ task = ResearchAgent.new.invoke_async("Research Ruby AI frameworks")
91
91
  result = task.wait_result # top-level/external caller only
92
92
  ```
93
93
 
94
+ A block listener receives Agent lifecycle events and is equivalent to `on_event:`:
95
+
96
+ ```ruby
97
+ task = ResearchAgent.new.invoke_async("Research Ruby AI frameworks") do |event|
98
+ puts event.type # :done, :error, :tool_call, :tool_result, etc.
99
+ end
100
+
101
+ # on_event: keyword form is also accepted and behaves identically
102
+ task = ResearchAgent.new.invoke_async("Research Ruby AI frameworks", on_event: listener)
103
+ ```
104
+
94
105
  ## Runtime model
95
106
 
96
107
  Phronomy uses one completion model with two execution mechanisms:
@@ -142,6 +142,19 @@ task = agent.invoke_async("Hello")
142
142
  result = task.wait_result
143
143
  ```
144
144
 
145
+ Both `invoke` and `invoke_async` can receive public Agent events through either
146
+ an `on_event:` listener or a block. A block is convenient when the listener is
147
+ local to the call:
148
+
149
+ ```ruby
150
+ task = agent.invoke_async("Hello") do |event|
151
+ puts event.payload[:output] if event.type == :done
152
+ end
153
+ ```
154
+
155
+ Use `on_event:` when the listener already exists as a callable. Do not provide
156
+ both `on_event:` and a block to the same invocation.
157
+
145
158
  `Phronomy::Task` is the common caller-facing completion handle for asynchronous
146
159
  Phronomy work. Logical lifecycle progress is driven by EventLoop/FSMSession;
147
160
  synchronous work that must execute away from EventLoop is submitted to
@@ -159,13 +172,12 @@ end
159
172
  ```
160
173
 
161
174
  ```ruby
162
- task = agent.stream_async(
163
- "Explain the design",
164
- on_event: ->(event) { puts event.payload if event.type == :token }
165
- )
175
+ task = agent.stream_async("Explain the design") do |event|
176
+ puts event.payload if event.type == :token
177
+ end
166
178
  ```
167
179
 
168
- Streaming callbacks execute on EventLoop and therefore should return quickly.
180
+ Agent event callbacks execute on EventLoop and therefore should return quickly.
169
181
 
170
182
  ## Human-in-the-loop approval
171
183
 
@@ -283,18 +295,15 @@ workflow = Phronomy::Workflow.define(AnswerContext) do
283
295
  entry :asking, ->(ctx) {
284
296
  thread_id = ctx.thread_id
285
297
 
286
- my_agent.invoke_async(
287
- ctx.question,
288
- on_event: ->(event) {
289
- next unless event.type == :done
290
-
291
- workflow.signal(
292
- thread_id: thread_id,
293
- event: :answer_ready,
294
- payload: {answer: event.payload[:output]}
295
- )
296
- }
297
- )
298
+ my_agent.invoke_async(ctx.question) do |event|
299
+ next unless event.type == :done
300
+
301
+ workflow.signal(
302
+ thread_id: thread_id,
303
+ event: :answer_ready,
304
+ payload: {answer: event.payload[:output]}
305
+ )
306
+ end
298
307
 
299
308
  ctx
300
309
  }
@@ -42,36 +42,33 @@ workflow = Phronomy::Workflow.define(GenerationContext) do
42
42
  request_id = context.generation_request_id
43
43
 
44
44
  # The Agent Task is intentionally not returned from the entry action.
45
- # on_event is the application-level integration channel.
46
- agent.invoke_async(
47
- context.prompt,
48
- on_event: ->(agent_event) {
49
- workflow_event =
50
- case agent_event.type
51
- when :done
52
- :generation_completed
53
- when :error, :timeout, :cancelled, :approval_required
54
- :generation_failed
55
- end
56
- next unless workflow_event
57
-
58
- workflow.signal(
59
- thread_id: context.thread_id,
60
- event: workflow_event,
61
- payload: {
62
- generation_request_id: request_id,
63
- agent_result: (
64
- agent_event.payload if agent_event.type == :done
65
- ),
66
- error:
67
- agent_event.payload[:error] ||
68
- Phronomy::Error.new(
69
- "Agent requested Tool approval"
70
- )
71
- }
72
- )
73
- }
74
- )
45
+ # The block is the application-level integration channel.
46
+ agent.invoke_async(context.prompt) do |agent_event|
47
+ workflow_event =
48
+ case agent_event.type
49
+ when :done
50
+ :generation_completed
51
+ when :error, :timeout, :cancelled, :approval_required
52
+ :generation_failed
53
+ end
54
+ next unless workflow_event
55
+
56
+ workflow.signal(
57
+ thread_id: context.thread_id,
58
+ event: workflow_event,
59
+ payload: {
60
+ generation_request_id: request_id,
61
+ agent_result: (
62
+ agent_event.payload if agent_event.type == :done
63
+ ),
64
+ error:
65
+ agent_event.payload[:error] ||
66
+ Phronomy::Error.new(
67
+ "Agent requested Tool approval"
68
+ )
69
+ }
70
+ )
71
+ end
75
72
 
76
73
  context
77
74
  }
@@ -5,7 +5,15 @@ module Phronomy
5
5
  # Public Agent execution API. Agent instances are always stateful and all
6
6
  # operations are coordinated through Phronomy::Persistence.
7
7
  module AsyncEventApi
8
- def invoke(input, thread_id: nil, config: {}, invocation_context: nil, on_event: nil)
8
+ def invoke(
9
+ input,
10
+ thread_id: nil,
11
+ config: {},
12
+ invocation_context: nil,
13
+ on_event: nil,
14
+ &block
15
+ )
16
+ listener = resolve_event_listener(on_event, block)
9
17
  if invocation_context
10
18
  thread_id, config = _apply_invocation_context(thread_id, config, invocation_context)
11
19
  end
@@ -15,7 +23,7 @@ module Phronomy
15
23
  input,
16
24
  thread_id: thread_id,
17
25
  config: config,
18
- on_event: on_event
26
+ on_event: listener
19
27
  ).wait_result
20
28
  [result, result[:usage]]
21
29
  end
@@ -27,8 +35,10 @@ module Phronomy
27
35
  config: {},
28
36
  invocation_context: nil,
29
37
  on_tool_approval_required: nil,
30
- on_event: nil
38
+ on_event: nil,
39
+ &block
31
40
  )
41
+ listener = resolve_event_listener(on_event, block)
32
42
  if invocation_context
33
43
  thread_id, config = _apply_invocation_context(thread_id, config, invocation_context)
34
44
  end
@@ -40,7 +50,7 @@ module Phronomy
40
50
  mode: :invoke,
41
51
  approval_policy: approval[:policy],
42
52
  approval_listener: approval[:listener],
43
- on_event: on_event
53
+ on_event: listener
44
54
  )
45
55
  end
46
56
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- VERSION = "0.21.0"
4
+ VERSION = "0.22.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phronomy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raizo T.C.S