phronomy 0.14.0 → 0.15.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 +65 -0
- data/README.md +236 -57
- data/benchmark/bench_agent_invoke.rb +2 -3
- data/docs/decisions/004-invoke-timeout-is-not-cancellation.md +14 -67
- data/docs/decisions/011-delegate-transport-policy-to-adapters.md +82 -0
- data/examples/workflows/agent_event_mapping.rb +104 -0
- data/examples/workflows/generic_task_event_mapping.rb +58 -0
- data/lib/phronomy/agent/agent_invocation.rb +385 -0
- data/lib/phronomy/agent/agent_invocation_registry.rb +75 -0
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +448 -0
- data/lib/phronomy/agent/approval_evaluation_request.rb +102 -0
- data/lib/phronomy/agent/async_event_api.rb +471 -0
- data/lib/phronomy/agent/base.rb +500 -411
- data/lib/phronomy/agent/context/capability/base.rb +51 -119
- data/lib/phronomy/agent/llm_operation_result.rb +23 -0
- data/lib/phronomy/agent/phase_machine_builder.rb +75 -137
- data/lib/phronomy/agent/tool_approval_request.rb +121 -0
- data/lib/phronomy/agent/tool_call_intercepted.rb +11 -15
- data/lib/phronomy/agent/tool_executor.rb +47 -69
- data/lib/phronomy/agent/tool_invocation.rb +634 -0
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +378 -0
- data/lib/phronomy/agent.rb +21 -9
- data/lib/phronomy/configuration.rb +42 -6
- data/lib/phronomy/engine/event_loop.rb +269 -112
- data/lib/phronomy/engine/fsm_session.rb +180 -142
- data/lib/phronomy/engine/task.rb +5 -10
- data/lib/phronomy/event.rb +8 -8
- data/lib/phronomy/generator_verifier.rb +253 -142
- data/lib/phronomy/invalid_async_entry_action_error.rb +9 -0
- data/lib/phronomy/invalid_async_transition_action_error.rb +11 -0
- data/lib/phronomy/invalid_async_workflow_action_error.rb +9 -0
- data/lib/phronomy/invocation_context.rb +5 -19
- data/lib/phronomy/llm_adapter/base.rb +25 -34
- data/lib/phronomy/metrics.rb +2 -0
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +54 -89
- data/lib/phronomy/stream_callback_error.rb +35 -0
- data/lib/phronomy/tools/mcp.rb +25 -0
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/phase_machine_builder.rb +129 -186
- data/lib/phronomy/workflow.rb +122 -261
- data/lib/phronomy/workflow_context.rb +54 -102
- data/lib/phronomy/workflow_runner.rb +238 -300
- data/lib/phronomy.rb +6 -4
- data/scripts/check_readme_runnable.rb +4 -1
- metadata +18 -7
- data/lib/phronomy/agent/concerns/retryable.rb +0 -103
- data/lib/phronomy/agent/context/capability/scope_policy.rb +0 -54
- data/lib/phronomy/agent/invocation_context.rb +0 -171
- data/lib/phronomy/agent/invocation_session.rb +0 -352
- data/lib/phronomy/agent/suspended_session_registry.rb +0 -54
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "state_machines"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module Agent
|
|
7
|
+
# Builds an FSMSession for one ToolInvocation.
|
|
8
|
+
#
|
|
9
|
+
# Authorization and execution Tasks are observed by ToolInvocation-specific
|
|
10
|
+
# callbacks that post explicit FSM events. The generated phase machine does
|
|
11
|
+
# not await Tasks.
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
class ToolInvocationSessionBuilder
|
|
15
|
+
AUTO_STATE_SET = {
|
|
16
|
+
idle: true,
|
|
17
|
+
validating: true,
|
|
18
|
+
queued: true
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
DECLARED_STATES = %i[
|
|
22
|
+
idle validating authorizing awaiting_approval authorized queued running
|
|
23
|
+
completed failed rejected cancelled
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
WAIT_STATES = %i[awaiting_approval authorized].freeze
|
|
27
|
+
|
|
28
|
+
EXTERNAL_EVENTS = {
|
|
29
|
+
authorization_completed: [
|
|
30
|
+
{from: :authorizing, to: :cancelled, guard: ->(ctx) {
|
|
31
|
+
ctx.cancelled?
|
|
32
|
+
}},
|
|
33
|
+
{from: :authorizing, to: :failed, guard: ->(ctx) {
|
|
34
|
+
ctx.failed?
|
|
35
|
+
}},
|
|
36
|
+
{from: :authorizing, to: :rejected, guard: ->(ctx) {
|
|
37
|
+
ctx.rejected?
|
|
38
|
+
}},
|
|
39
|
+
{from: :authorizing, to: :awaiting_approval, guard: ->(ctx) {
|
|
40
|
+
ctx.awaiting_approval?
|
|
41
|
+
}},
|
|
42
|
+
{from: :authorizing, to: :authorized, guard: ->(ctx) {
|
|
43
|
+
ctx.authorized?
|
|
44
|
+
}}
|
|
45
|
+
],
|
|
46
|
+
execution_completed: [
|
|
47
|
+
{from: :running, to: :cancelled, guard: ->(ctx) {
|
|
48
|
+
ctx.cancelled?
|
|
49
|
+
}},
|
|
50
|
+
{from: :running, to: :failed, guard: ->(ctx) {
|
|
51
|
+
ctx.failed?
|
|
52
|
+
}},
|
|
53
|
+
{from: :running, to: :completed, guard: ->(ctx) {
|
|
54
|
+
ctx.execution_completed?
|
|
55
|
+
}}
|
|
56
|
+
],
|
|
57
|
+
approve: [
|
|
58
|
+
{from: :awaiting_approval, to: :authorized, guard: nil}
|
|
59
|
+
],
|
|
60
|
+
reject: [
|
|
61
|
+
{from: :awaiting_approval, to: :rejected, guard: nil}
|
|
62
|
+
],
|
|
63
|
+
dispatch: [
|
|
64
|
+
{from: :authorized, to: :queued, guard: nil}
|
|
65
|
+
],
|
|
66
|
+
cancel: [
|
|
67
|
+
{from: :awaiting_approval, to: :cancelled, guard: nil},
|
|
68
|
+
{from: :authorized, to: :cancelled, guard: nil},
|
|
69
|
+
{from: :queued, to: :cancelled, guard: nil},
|
|
70
|
+
{from: :running, to: :cancelled, guard: nil}
|
|
71
|
+
]
|
|
72
|
+
}.freeze
|
|
73
|
+
|
|
74
|
+
def self.build(
|
|
75
|
+
tool_invocation:,
|
|
76
|
+
runtime: Phronomy::Runtime.instance
|
|
77
|
+
)
|
|
78
|
+
build_session(
|
|
79
|
+
tool_invocation: tool_invocation,
|
|
80
|
+
runtime: runtime
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.build_for_resume(
|
|
85
|
+
tool_invocation:,
|
|
86
|
+
resume_event:,
|
|
87
|
+
resume_phase:,
|
|
88
|
+
runtime: Phronomy::Runtime.instance
|
|
89
|
+
)
|
|
90
|
+
build_session(
|
|
91
|
+
tool_invocation: tool_invocation,
|
|
92
|
+
runtime: runtime,
|
|
93
|
+
resume_event: resume_event,
|
|
94
|
+
resume_phase: resume_phase
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.build_session(
|
|
99
|
+
tool_invocation:,
|
|
100
|
+
runtime:,
|
|
101
|
+
resume_event: nil,
|
|
102
|
+
resume_phase: nil
|
|
103
|
+
)
|
|
104
|
+
actions = build_entry_actions(runtime)
|
|
105
|
+
phase_machine = build_phase_machine(actions)
|
|
106
|
+
|
|
107
|
+
Phronomy::FSMSession.new(
|
|
108
|
+
id: tool_invocation.id,
|
|
109
|
+
context: tool_invocation,
|
|
110
|
+
entry_point: :idle,
|
|
111
|
+
phase_machine_class: phase_machine,
|
|
112
|
+
entry_actions: {},
|
|
113
|
+
auto_state_set: AUTO_STATE_SET,
|
|
114
|
+
declared_states: DECLARED_STATES,
|
|
115
|
+
wait_state_names: WAIT_STATES,
|
|
116
|
+
external_events: EXTERNAL_EVENTS,
|
|
117
|
+
recursion_limit: 20,
|
|
118
|
+
event_loop: runtime.event_loop,
|
|
119
|
+
resume_event: resume_event,
|
|
120
|
+
resume_phase: resume_phase
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
private_class_method :build_session
|
|
124
|
+
|
|
125
|
+
def self.build_entry_actions(runtime)
|
|
126
|
+
{
|
|
127
|
+
validating: [method(:validating_action)],
|
|
128
|
+
authorizing: [
|
|
129
|
+
method(:authorizing_action).curry.call(runtime)
|
|
130
|
+
],
|
|
131
|
+
awaiting_approval: [
|
|
132
|
+
method(:awaiting_approval_action).curry.call(runtime)
|
|
133
|
+
],
|
|
134
|
+
authorized: [
|
|
135
|
+
method(:authorized_action).curry.call(runtime)
|
|
136
|
+
],
|
|
137
|
+
queued: [method(:queued_action)],
|
|
138
|
+
running: [
|
|
139
|
+
method(:running_action).curry.call(runtime)
|
|
140
|
+
],
|
|
141
|
+
completed: [
|
|
142
|
+
method(:completed_action).curry.call(runtime)
|
|
143
|
+
],
|
|
144
|
+
failed: [
|
|
145
|
+
method(:failed_action).curry.call(runtime)
|
|
146
|
+
],
|
|
147
|
+
rejected: [
|
|
148
|
+
method(:rejected_action).curry.call(runtime)
|
|
149
|
+
],
|
|
150
|
+
cancelled: [
|
|
151
|
+
method(:cancelled_action).curry.call(runtime)
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
private_class_method :build_entry_actions
|
|
156
|
+
|
|
157
|
+
def self.build_phase_machine(entry_actions)
|
|
158
|
+
callbacks = entry_actions
|
|
159
|
+
callback_builder = method(:build_entry_callback)
|
|
160
|
+
|
|
161
|
+
Class.new do
|
|
162
|
+
attr_accessor :context, :current_event
|
|
163
|
+
|
|
164
|
+
state_machine :phase, initial: :idle do
|
|
165
|
+
state :idle
|
|
166
|
+
state :validating
|
|
167
|
+
state :authorizing
|
|
168
|
+
state :awaiting_approval
|
|
169
|
+
state :authorized
|
|
170
|
+
state :queued
|
|
171
|
+
state :running
|
|
172
|
+
state :completed
|
|
173
|
+
state :failed
|
|
174
|
+
state :rejected
|
|
175
|
+
state :cancelled
|
|
176
|
+
|
|
177
|
+
event :state_completed do
|
|
178
|
+
transition idle: :validating
|
|
179
|
+
|
|
180
|
+
transition validating: :failed,
|
|
181
|
+
if: ->(machine) { machine.context&.failed? }
|
|
182
|
+
transition validating: :completed,
|
|
183
|
+
if: ->(machine) {
|
|
184
|
+
machine.context&.validation_completed?
|
|
185
|
+
}
|
|
186
|
+
transition validating: :authorizing,
|
|
187
|
+
if: ->(machine) {
|
|
188
|
+
machine.context&.validation_passed?
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
transition queued: :running
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
event :authorization_completed do
|
|
195
|
+
transition authorizing: :cancelled,
|
|
196
|
+
if: ->(machine) { machine.context&.cancelled? }
|
|
197
|
+
transition authorizing: :failed,
|
|
198
|
+
if: ->(machine) { machine.context&.failed? }
|
|
199
|
+
transition authorizing: :rejected,
|
|
200
|
+
if: ->(machine) { machine.context&.rejected? }
|
|
201
|
+
transition authorizing: :awaiting_approval,
|
|
202
|
+
if: ->(machine) {
|
|
203
|
+
machine.context&.awaiting_approval?
|
|
204
|
+
}
|
|
205
|
+
transition authorizing: :authorized,
|
|
206
|
+
if: ->(machine) { machine.context&.authorized? }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
event :execution_completed do
|
|
210
|
+
transition running: :cancelled,
|
|
211
|
+
if: ->(machine) { machine.context&.cancelled? }
|
|
212
|
+
transition running: :failed,
|
|
213
|
+
if: ->(machine) { machine.context&.failed? }
|
|
214
|
+
transition running: :completed,
|
|
215
|
+
if: ->(machine) {
|
|
216
|
+
machine.context&.execution_completed?
|
|
217
|
+
}
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
event :approve do
|
|
221
|
+
transition awaiting_approval: :authorized
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
event :reject do
|
|
225
|
+
transition awaiting_approval: :rejected
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
event :dispatch do
|
|
229
|
+
transition authorized: :queued
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
event :cancel do
|
|
233
|
+
transition awaiting_approval: :cancelled
|
|
234
|
+
transition authorized: :cancelled
|
|
235
|
+
transition queued: :cancelled
|
|
236
|
+
transition running: :cancelled
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
callbacks.each do |state_name, callables|
|
|
240
|
+
callables.each do |callable|
|
|
241
|
+
after_transition(
|
|
242
|
+
to: state_name,
|
|
243
|
+
do: callback_builder.call(callable, state_name)
|
|
244
|
+
)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
private_class_method :build_phase_machine
|
|
251
|
+
|
|
252
|
+
def self.build_entry_callback(callable, state_name)
|
|
253
|
+
->(machine) {
|
|
254
|
+
result = callable.call(machine.context)
|
|
255
|
+
if result.is_a?(Phronomy::Task)
|
|
256
|
+
raise Phronomy::InvalidAsyncEntryActionError,
|
|
257
|
+
"Tool entry action for #{state_name.inspect} returned Phronomy::Task"
|
|
258
|
+
end
|
|
259
|
+
machine.context = result if result.respond_to?(:set_graph_metadata)
|
|
260
|
+
}
|
|
261
|
+
end
|
|
262
|
+
private_class_method :build_entry_callback
|
|
263
|
+
|
|
264
|
+
def self.validating_action(invocation)
|
|
265
|
+
invocation.validate!
|
|
266
|
+
end
|
|
267
|
+
private_class_method :validating_action
|
|
268
|
+
|
|
269
|
+
def self.authorizing_action(runtime, invocation)
|
|
270
|
+
task = invocation.authorization_task(runtime: runtime)
|
|
271
|
+
observe_task(
|
|
272
|
+
runtime,
|
|
273
|
+
invocation,
|
|
274
|
+
task,
|
|
275
|
+
event_type: :authorization_completed
|
|
276
|
+
)
|
|
277
|
+
invocation
|
|
278
|
+
end
|
|
279
|
+
private_class_method :authorizing_action
|
|
280
|
+
|
|
281
|
+
def self.awaiting_approval_action(runtime, invocation)
|
|
282
|
+
invocation.mark_awaiting_approval!
|
|
283
|
+
notify_parent(runtime, invocation, :tool_approval_required)
|
|
284
|
+
invocation
|
|
285
|
+
end
|
|
286
|
+
private_class_method :awaiting_approval_action
|
|
287
|
+
|
|
288
|
+
def self.authorized_action(runtime, invocation)
|
|
289
|
+
invocation.mark_authorized!
|
|
290
|
+
notify_parent(runtime, invocation, :tool_authorized)
|
|
291
|
+
invocation
|
|
292
|
+
end
|
|
293
|
+
private_class_method :authorized_action
|
|
294
|
+
|
|
295
|
+
def self.queued_action(invocation)
|
|
296
|
+
invocation.mark_queued!
|
|
297
|
+
end
|
|
298
|
+
private_class_method :queued_action
|
|
299
|
+
|
|
300
|
+
def self.running_action(runtime, invocation)
|
|
301
|
+
# execution_task checks dispatchable? which requires :queued status;
|
|
302
|
+
# mark_running! is deferred until after the task is started.
|
|
303
|
+
task = invocation.execution_task(runtime: runtime)
|
|
304
|
+
invocation.mark_running!
|
|
305
|
+
observe_task(
|
|
306
|
+
runtime,
|
|
307
|
+
invocation,
|
|
308
|
+
task,
|
|
309
|
+
event_type: :execution_completed
|
|
310
|
+
)
|
|
311
|
+
invocation
|
|
312
|
+
end
|
|
313
|
+
private_class_method :running_action
|
|
314
|
+
|
|
315
|
+
def self.observe_task(
|
|
316
|
+
runtime,
|
|
317
|
+
invocation,
|
|
318
|
+
task,
|
|
319
|
+
event_type:
|
|
320
|
+
)
|
|
321
|
+
task.on_complete do |outcome, error|
|
|
322
|
+
payload = error || outcome
|
|
323
|
+
accepted = runtime.event_loop.post_to_session(
|
|
324
|
+
Phronomy::Event.new(
|
|
325
|
+
type: event_type,
|
|
326
|
+
target_id: invocation.id,
|
|
327
|
+
payload: payload
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
next if accepted
|
|
331
|
+
|
|
332
|
+
Phronomy.configuration.logger&.warn(
|
|
333
|
+
"[Phronomy] Dropped #{event_type.inspect} for " \
|
|
334
|
+
"ToolInvocation #{invocation.id}"
|
|
335
|
+
)
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
private_class_method :observe_task
|
|
339
|
+
|
|
340
|
+
def self.completed_action(runtime, invocation)
|
|
341
|
+
notify_parent(runtime, invocation, :tool_completed)
|
|
342
|
+
invocation
|
|
343
|
+
end
|
|
344
|
+
private_class_method :completed_action
|
|
345
|
+
|
|
346
|
+
def self.failed_action(runtime, invocation)
|
|
347
|
+
notify_parent(runtime, invocation, :tool_failed)
|
|
348
|
+
invocation
|
|
349
|
+
end
|
|
350
|
+
private_class_method :failed_action
|
|
351
|
+
|
|
352
|
+
def self.rejected_action(runtime, invocation)
|
|
353
|
+
invocation.mark_rejected!
|
|
354
|
+
notify_parent(runtime, invocation, :tool_rejected)
|
|
355
|
+
invocation
|
|
356
|
+
end
|
|
357
|
+
private_class_method :rejected_action
|
|
358
|
+
|
|
359
|
+
def self.cancelled_action(runtime, invocation)
|
|
360
|
+
invocation.mark_cancelled!
|
|
361
|
+
notify_parent(runtime, invocation, :tool_cancelled)
|
|
362
|
+
invocation
|
|
363
|
+
end
|
|
364
|
+
private_class_method :cancelled_action
|
|
365
|
+
|
|
366
|
+
def self.notify_parent(runtime, invocation, event_type)
|
|
367
|
+
runtime.event_loop.post_to_session(
|
|
368
|
+
Phronomy::Event.new(
|
|
369
|
+
type: event_type,
|
|
370
|
+
target_id: invocation.parent_agent_invocation_id,
|
|
371
|
+
payload: {tool_invocation_id: invocation.id}
|
|
372
|
+
)
|
|
373
|
+
)
|
|
374
|
+
end
|
|
375
|
+
private_class_method :notify_parent
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
data/lib/phronomy/agent.rb
CHANGED
|
@@ -2,16 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Agent
|
|
5
|
-
#
|
|
5
|
+
# Immutable event emitted by Agent async APIs.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# :
|
|
13
|
-
#
|
|
14
|
-
# :
|
|
7
|
+
# invoke_async and stream_async share lifecycle and Tool events. Streaming
|
|
8
|
+
# additionally emits :token events.
|
|
9
|
+
#
|
|
10
|
+
# Common event types:
|
|
11
|
+
# :tool_call
|
|
12
|
+
# :tool_result
|
|
13
|
+
# :approval_required
|
|
14
|
+
# :done
|
|
15
|
+
# :error
|
|
16
|
+
# :timeout
|
|
17
|
+
# :cancelled
|
|
18
|
+
#
|
|
19
|
+
# Streaming-only event type:
|
|
20
|
+
# :token
|
|
15
21
|
StreamEvent = Data.define(:type, :payload)
|
|
16
22
|
end
|
|
17
23
|
end
|
|
24
|
+
|
|
25
|
+
require_relative "agent/async_event_api"
|
|
26
|
+
|
|
27
|
+
unless Phronomy::Agent::Base < Phronomy::Agent::AsyncEventApi
|
|
28
|
+
Phronomy::Agent::Base.prepend(Phronomy::Agent::AsyncEventApi)
|
|
29
|
+
end
|
|
@@ -10,6 +10,9 @@ module Phronomy
|
|
|
10
10
|
# config.recursion_limit = 50
|
|
11
11
|
# end
|
|
12
12
|
class Configuration
|
|
13
|
+
STREAM_CALLBACK_ERROR_POLICIES = %i[report fail_task].freeze
|
|
14
|
+
private_constant :STREAM_CALLBACK_ERROR_POLICIES
|
|
15
|
+
|
|
13
16
|
# Default LLM model name (nil delegates to RubyLLM default)
|
|
14
17
|
attr_accessor :default_model
|
|
15
18
|
|
|
@@ -106,11 +109,17 @@ module Phronomy
|
|
|
106
109
|
# @return [Float, nil]
|
|
107
110
|
attr_accessor :blocking_detect_threshold_ms
|
|
108
111
|
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
|
|
112
|
+
# Determines how an unhandled Application exception from a terminal stream
|
|
113
|
+
# callback affects the Task returned by Agent#stream_async or
|
|
114
|
+
# Agent#approve_async.
|
|
115
|
+
#
|
|
116
|
+
# +:report+ logs the callback failure and preserves the Agent result.
|
|
117
|
+
# +:fail_task+ logs the callback failure and fails the current Task with
|
|
118
|
+
# {Phronomy::StreamCallbackError}. Neither policy terminates EventLoop.
|
|
119
|
+
#
|
|
120
|
+
# Default: +:report+.
|
|
121
|
+
# @return [:report, :fail_task]
|
|
122
|
+
attr_reader :stream_callback_error_policy
|
|
114
123
|
|
|
115
124
|
# Number of OS worker threads in the default {BlockingAdapterPool}.
|
|
116
125
|
# All LLM calls, MCP tool calls, and other blocking I/O share this pool.
|
|
@@ -126,6 +135,20 @@ module Phronomy
|
|
|
126
135
|
# @return [Integer]
|
|
127
136
|
attr_accessor :blocking_io_queue_size
|
|
128
137
|
|
|
138
|
+
# Worker count for Tool authorization evaluation. The named pool is owned
|
|
139
|
+
# by Runtime#pool(:authorization) and shares PoolRegistry lifecycle.
|
|
140
|
+
# @return [Integer]
|
|
141
|
+
attr_accessor :authorization_pool_size
|
|
142
|
+
|
|
143
|
+
# Maximum queued Tool authorization evaluations.
|
|
144
|
+
# @return [Integer]
|
|
145
|
+
attr_accessor :authorization_queue_size
|
|
146
|
+
|
|
147
|
+
# Operation-wide deadline for approval_facts, requires_approval callables,
|
|
148
|
+
# and Agent#tool_approval_policy. Timeout fails closed to Human approval.
|
|
149
|
+
# @return [Numeric]
|
|
150
|
+
attr_accessor :authorization_timeout
|
|
151
|
+
|
|
129
152
|
# Scheduler starvation threshold (milliseconds).
|
|
130
153
|
# When a task waits more than this many milliseconds after calling
|
|
131
154
|
# +runtime.yield+ before being resumed, the wait is counted as a starvation
|
|
@@ -162,6 +185,16 @@ module Phronomy
|
|
|
162
185
|
# @return [Boolean]
|
|
163
186
|
attr_accessor :strict_runtime_guards
|
|
164
187
|
|
|
188
|
+
def stream_callback_error_policy=(value)
|
|
189
|
+
unless STREAM_CALLBACK_ERROR_POLICIES.include?(value)
|
|
190
|
+
allowed = STREAM_CALLBACK_ERROR_POLICIES.map(&:inspect).join(", ")
|
|
191
|
+
raise Phronomy::ConfigurationError,
|
|
192
|
+
"stream_callback_error_policy must be one of: #{allowed}"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
@stream_callback_error_policy = value
|
|
196
|
+
end
|
|
197
|
+
|
|
165
198
|
def initialize
|
|
166
199
|
@recursion_limit = 25
|
|
167
200
|
@tracer = Phronomy::Tracing::NullTracer.new
|
|
@@ -173,9 +206,12 @@ module Phronomy
|
|
|
173
206
|
@event_loop_dispatch_threshold_seconds = nil
|
|
174
207
|
@scheduler_debug = false
|
|
175
208
|
@blocking_detect_threshold_ms = nil
|
|
176
|
-
@
|
|
209
|
+
@stream_callback_error_policy = :report
|
|
177
210
|
@blocking_io_pool_size = 10
|
|
178
211
|
@blocking_io_queue_size = 100
|
|
212
|
+
@authorization_pool_size = 4
|
|
213
|
+
@authorization_queue_size = 100
|
|
214
|
+
@authorization_timeout = 5
|
|
179
215
|
@starvation_threshold_ms = 50
|
|
180
216
|
@runtime_backend = :thread
|
|
181
217
|
@strict_runtime_guards = false
|